groupby函数是用来干嘛的 Groupby详解

在日常的数据分析中 。经常需要将数据根据某个(多个)字段划分为不同的群体(group)进行分析 。如电商领域将全国的总销售额根据省份进行划分 。分析各省销售额的变化情况 。社会交友领域将客户根据画像(性别、年龄)进行细分 。研究客户的使用情况和偏好等 。在Pandas中 。上述的数据处理操作主要使用groupby完成 。这样一篇教学文章就介绍一下groupby的基本原理及对应的agg、transform和apply操作 。
为了后续图解的方便 。选用模拟生成的10个样本数据 。代码和数据如下:【groupby函数是用来干嘛的 Groupby详解】company=["A","B","C"]data=https://www.wangchuang8.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函数是用来干嘛的 Groupby详解

文章插图
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函数是用来干嘛的 Groupby详解

文章插图
总结来探讨 。groupby的过程就是将原有的DataFrame遵从groupby的字段(这里是company) 。划分为若干个分组DataFrame 。被分为多少个组就有多少个分组DataFrame 。所以说 。在groupby之后的一系列操作(如agg、apply等) 。均是基于分组DataFrame的操作 。理解了这点 。也就基本摸清了Pandas中groupby操作的主要原理 。下面来讲讲groupby之后的经常可以看见操作 。
agg分组聚合聚合操作是groupby后经常可以看见的操作 。会写SQL的朋友对此应该是非常熟悉了 。聚合操作可以用来求和、均值、最大值、最小值等 。下面的表格列出了Pandas中经常可以看见的聚合操作 。
groupby函数是用来干嘛的 Groupby详解

文章插图
针对样例数据集 。如果我想计算不同公司员工的平均年龄和平均薪水 。可以遵从下方的代码进行: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聚合过程可以图解如下(第二个例子为例):
groupby函数是用来干嘛的 Groupby详解

文章插图
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列):

推荐阅读