在日常的数据分析中 。经常需要将数据根据某个(多个)字段划分为不同的群体(group)进行分析 。如电商领域将全国的总销售额根据省份进行划分 。分析各省销售额的变化情况 。社会交友领域将客户根据画像(性别、年龄)进行细分 。研究客户的使用情况和偏好等 。在Pandas中 。上述的数据处理操作主要使用groupby完成 。这样一篇教学文章就介绍一下groupby的基本原理及对应的agg、transform和apply操作 。
为了后续图解的方便 。选用模拟生成的10个样本数据 。代码和数据如下:company=["A","B","C"]data=http://www.shenzhoubaby.com/pd.DataFrame({"company":[company[x] for x in np.random.randint(0,len(company),10)],"salary":np.random.randint(5,50,10),"age":np.random.randint(15,50,10)})
文章插图
groupby的基本原理在Pandas中 。实现分组操作的代码很简单 。仅需一行代码 。在这里 。将上面的数据集遵从company字段进行划分:
In [5]: group = data.groupby("company")将上述代码输入ipython后 。会得到一个DataFrameGroupBy对象
In [6]: groupOut[6]: <pandas.core.groupby.generic.DataFrameGroupBy object at 0x000002B7E2650240>那这个生成的DataFrameGroupBy是啥呢?对data进行了groupby后发生了什么?ipython所返回的结果是其内存地址 。并不利于直观地理解 。为了看看group内部到底是什么 。这里把group转换成list的形式来看一看:
In [8]: list(group)Out[8]:[('A',company salary age 3A2022 6A2333), ('B',company salary age 4B1017 5B2140 8B830), ('C',company salary age 0C4335 1C1725 2C830 7C4919)]转换成列表的形式后 。可以看到 。列表由三个元组组成 。每个元组中 。第一个元素是组别(这里是遵从company进行分组 。所以最后分为了A,B,C) 。第二个元素的是对应组别下的DataFrame 。整个过程可以图解如下:
文章插图
总结来探讨 。groupby的过程就是将原有的DataFrame遵从groupby的字段(这里是company) 。划分为若干个分组DataFrame 。被分为多少个组就有多少个分组DataFrame 。所以说 。在groupby之后的一系列操作(如agg、apply等) 。均是基于分组DataFrame的操作 。理解了这点 。也就基本摸清了Pandas中groupby操作的主要原理 。下面来讲讲groupby之后的经常可以看见操作 。
agg分组聚合聚合操作是groupby后经常可以看见的操作 。会写SQL的朋友对此应该是非常熟悉了 。聚合操作可以用来求和、均值、最大值、最小值等 。下面的表格列出了Pandas中经常可以看见的聚合操作 。
文章插图
针对样例数据集 。如果我想计算不同公司员工的平均年龄和平均薪水 。可以遵从下方的代码进行:In [12]: data.groupby("company").agg('mean')Out[12]:salaryagecompanyA21.50 27.50B13.00 29.00C29.25 27.25如果想对针对不同的列求不同的值 。比如要计算不同公司员工的平均年龄以及薪水的中位数 。可以利用字典指定进行聚合操作:
In [17]: data.groupby('company').agg({'salary':'median','age':'mean'})Out[17]:salaryagecompanyA21.5 27.50B10.0 29.00C30.0 27.25agg聚合过程可以图解如下(第二个例子为例):
文章插图
transform
transform是一种什么数据操作?和agg有什么区别呢?为了更好地理解transform和agg的不同 。下面从实际的应用场景出发进行对比 。
在上面的agg中 。咱们学会了如何求不同公司员工的平均薪水 。如果现在需要在原数据集中新增一列avg_salary 。代表员工所在的公司的平均薪水(相同公司的员工具有一样的平均薪水) 。该怎么实现呢?如果遵从正常的步骤来计算 。需要先求得不同公司的平均薪水 。然后遵从员工和公司的对应关系填充到对应的位置 。不用transform的话 。实现代码如下:
In [21]: avg_salary_dict = data.groupby('company')['salary'].mean().to_dict()In [22]: data['avg_salary'] = data['company'].map(avg_salary_dict)In [23]: dataOut[23]: company salary age avg_salary0C433529.251C172529.252C83029.253A202221.504B101713.005B214013.006A233321.507C491929.258B83013.00如果使用transform的话 。仅需要一行代码:In [24]: data['avg_salary'] = data.groupby('company')['salary'].transform('mean')In [25]: dataOut[25]: company salary age avg_salary0C433529.251C172529.252C83029.253A202221.504B101713.005B214013.006A233321.507C491929.258B83013.00还是以图解的方式来看看进行groupby后transform的实现过程(为了更直观展示 。图中加入了company列 。实际遵从上面的代码只有salary列):
文章插图
图中的大方框是transform和agg所不一样的地方 。对agg而言 。会计算得到A 。B 。C公司对应的均值并直接返回 。但对transform而言 。则会对每一条数据求得相应的结果 。同一组内的样本会有相同的值 。组内求完均值后会遵从原索引的顺序返回结果 。如果有不怎么理解的可以拿这张图和agg那张对比一下 。
apply
apply应该是各位的老朋友了 。它相比agg和transform而言更加灵活 。能够传入任意自定义的函数 。实现复杂的数据操作 。在Pandas数据处理三板斧 。你会几板?中 。介绍了apply的使用 。那在groupby后使用apply和之前所介绍的有什么区别呢?
区别是有的 。但是整个实现原理是基本一致的 。两者的区别在于 。对于groupby后的apply 。以分组后的分组DataFrame作为参数传入指定函数的 。基本操作单位是DataFrame 。而之前介绍的apply的基本操作单位是Series 。还是以一个案例来介绍groupby后的apply用法 。
假设我现在需要获取各个公司年龄最大的员工的数据 。该怎么实现呢?可以用以下代码实现:In [38]: def get_oldest_staff(x):…:df = x.sort_values(by = 'age',ascending=True)…:return df.iloc[-1,:]…:In [39]: oldest_staff = data.groupby('company',as_index=False).apply(get_oldest_staff)In [40]: oldest_staffOut[40]: company salary age 0A23331B21402C4335这样便得到了每个公司年龄最大的员工的数据 。整个流程图解如下:
文章插图
可以看到 。此处的apply和上篇文章中所介绍的作用原理基本一致 。只是传入函数的参数由Series变为了此处的分组DataFrame 。
【Groupby groupby函数是用来干嘛的】最后 。关于apply的使用 。这里有个小建议 。虽然说apply拥有更大的灵活性 。但apply的运行效率会比agg和transform更慢 。所以 。groupby之后能用agg和transform解决的问题还是优先使用这两个方法 。实在解决不了了才考虑使用apply进行操作 。
- 回调函数和钩子函数 钩子函数和回调函数
- IF函数的5个典型用法 ifi函数的使用方法
- strcpy函数的作用是 strcpy函数的作用是什么
- 查找函数vlookup怎么用 查找函数vlookup
- 自己收集的skynet教程 skynet源码浏览系列从main函数开始
- 学习opencv之cvtColor cvtcolor函数的作用
- mid函数的使用方法详细介绍 vb中mid函数的使用方法示例
- datedif是什么函数 datedif函数是什么函数
- count,countif,countifs函数的区别 countif和countifs的区别
- 奇函数性质 奇函数的性质