Reference:Pandas之超好用的Groupby用法详解
在日常的数据分析中,经常需要将数据 根据某个(多个)字段划分为不同的群体(group) 进行分析,如电商领域将全国的总销售额根据省份进行划分,分析各省销售额的变化情况,社交领域将用户根据画像(性别、年龄)进行细分,研究用户的使用情况和偏好等。在Pandas中,上述的数据处理操作主要运用groupby
完成,这篇文章就介绍一下groupby
的基本原理及对应的agg
、transform
和apply
操作。
为了后续图解的方便,采用模拟生成的10个样本数据,代码和数据如下:
company=["A","B","C"]
data=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)
}
)
1 Groupbyの基本原理
在pandas中,实现分组操作的代码很简单,仅需一行代码,在这里,将上面的数据集按照company
字段进行划分:
group=data.groupby('company')
将上述代码输入ipython后,会得到一个DataFrameGroupBy对象
group
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x04AF5CD0>
那这个生成的DataFrameGroupBy是啥呢?对data进行了groupby
后发生了什么?ipython所返回的结果是其内存地址,并不利于直观地理解,为了看看group
内部究竟是什么,这里把group
转换成list
的形式来看一看:
list(group)
[('A', company salary age
1 A 31 30),
('B', company salary age
3 B 17 47
4 B 41 27
6 B 7 18
7 B 30 38
9 B 25 21),
('C', company salary age
0 C 34 35
2 C 7 33
5 C 49 27
8 C 38 48)]
转换成列表的形式后,可以看到,列表由三个元组组成,每个元组中,第一个元素是组别(这里是按照company
进行分组,所以最后分为了A,B,C),第二个元素的是对应组别下的DataFrame,整个过程可以图解如下:
总结来说,groupby
的过程就是将原有的DataFrame按照groupby
的字段(这里是company
),划分为若干个分组DataFrame,被分为多少个组就有多少个分组DataFrame。
所以说,在groupby
之后的一系列操作(如agg
、apply
等),均是基于子DataFrame的操作。理解了这点,也就基本摸清了Pandas中groupby
操作的主要原理。下面来讲讲groupby
之后的常见操作。
2 groupby+agg 聚合
聚合操作是groupby
后非常常见的操作,会写SQL的朋友对此应该是非常熟悉了。聚合操作可以用来求和、均值、最大值、最小值等,下面的表格列出了Pandas中常见的聚合操作。
针对样例数据集,如果我想求不同公司员工的平均年龄和平均薪水,可以按照下方的代码进行:
data.groupby('company').agg('mean')
# data.groupby('company').mean()
如果想对针对不同的列求不同的值,比如要计算不同公司员工的平均年龄以及薪水的中位数,可以利用字典进行聚合操作的指定:
data.groupby('company').agg({'salary':'median','age':'mean'})
3 groupby+transform
为了更好地理解transform
和agg
的不同,下面从实际的应用场景出发进行对比。
在上面的agg
中,我们学会了如何求不同公司员工的平均薪水,如果现在需要在原数据集中新增一列avg_salary
,代表员工所在的公司的平均薪水(相同公司的员工具有一样的平均薪水),该怎么实现呢?
如果按照正常的步骤来计算,需要先求得不同公司的平均薪水,然后按照员工和公司的对应关系填充到对应的位置,不用transform
的话,实现代码如下:
dic=data.groupby('company').agg('mean')['salary'].to_dict()
data['avg_salary']=data['company'].map(dic)
如果使用transform
的话,仅需要一行代码:
data['avg_salary_1']=data.groupby('company')['salary'].transform('mean')
图中的大方框是transform
和agg
所不一样的地方
- 对
agg
而言,会计算得到A,B,C公司对应的均值并直接返回 - 对
transform
而言,则会对每一条数据求得相应的结果,同一组内的样本会有相同的值,组内求完均值后会按照原索引的顺序返回结果,如果有不理解的可以拿这张图和agg
那张对比一下。
4 groupby+apply
apply
应该是大家的老朋友了,它相比agg
和transform
而言更加灵活,能够传入任意自定义的函数,实现复杂的数据操作。
【Python笔记】Pandas数据处理:map、apply、applymap介绍了apply
的使用,那在groupby
后使用apply
和之前所介绍的有什么区别呢?
区别是有的,但是整个实现原理是基本一致的。
两者的区别在于:
- 对于
groupby
后的apply
,以分组后的子DataFrame作为参数传入指定函数的,基本操作单位是DataFrame - 之前介绍的
apply
的基本操作单位是Series
假设我现在需要获取各个公司年龄最大的员工的数据,可以用以下代码实现:
def get_oldest_staff(group):
return group.sort_values(by='age',ascending=True)[-1:]
data.groupby('company',as_index=False).apply(get_oldest_staff)
可以看到,此处的apply
和上篇文章中所介绍的作用原理基本一致,只是传入函数的参数由Series变为了此处的分组DataFrame。
最后,关于apply的使用,这里有个小建议:
- 虽然说
apply
拥有更大的灵活性,但apply
的运行效率会比agg
和transform
更慢。所以,groupby
之后能用agg
和transform
解决的问题还是优先使用这两个方法,实在解决不了了才考虑使用apply进行操作