pandas中的groupby
函数是先将df按照某个字段进行拆分,将相同属性分为一组;然后对拆分后的各组执行相应的转换操作;最后输出汇总转换后的各组结果
DataFrame.groupby(by=None,
axis=0,
level=None,
as_index=True,
sort=True,
group_keys=True,
squeeze=NoDefault.no_default,
observed=False,
dropna=True)
by
:用于确定 groupby 的组。 如果 by 是一个函数,它会在对象索引的每个值上调用。 如果传递了 dict 或 Series,则 Series
或 dict
VALUES
将用于确定组(Series 的值首先对齐;参见 .align()
方法)。 如果传递了长度等于所选轴的列表或 ndarray
,则按原样使用这些值来确定组。 一个标签或标签列表可以通过 self 中的列传递给 group。 请注意,元组被解释为(单个)键。axis
:沿行 (0) 或列 (1) 拆分。level
:如果轴是MultiIndex(层次化),则按一个或多个特定级别进行分组。as_index
:对于聚合输出,返回具有组标签作为索引的对象。仅与DataFrame输入相关。as index=False
是有效的sql风格的分组输出。sort
:对组键进行排序
。 关闭此功能可获得更好的性能。 请注意,这不会影响每组内的观察顺序。 Groupby 保留每个组内的行顺序。group_keys
:当调用apply
时,将组键添加到index
以识别片段。squeeze
:如果可能,降低返回类型的维数,否则返回一致的类型。observed
:这仅适用于任何 groupers 是分类的。 如果为真:仅显示分类分组的观察值
。 如果为 False:显示分类分组的所有值
。dropna
:如果为 True
,并且组键包含 NA
值,则 NA 值连同行/列将被删除。 如果为 False
,NA
值也将被视为组中的键。def group_by():
data = {'name': ['apolo', 'apolo', 'apolo', 'adm', 'adm', 'adm', 'bolon', 'bolon', 'bolon',
'ali', 'ali', 'ali', 'cathy', 'cathy', 'cathy', 'jack', 'jack', 'jack'],
'subjects': ['math', 'english', 'chinese', 'math', 'english', 'chinese', 'math', 'english', 'chinese',
'math', 'english', 'chinese', 'math', 'english', 'chinese', 'math', 'english', 'chinese'],
'grades' : [89, 78, 84, 89, 83, 85, 77, 88, 79, 89, 86, 83, 95, 90, 94, 78, 70, 80]
}
df = pd.DataFrame(data)
print(df)
输出 :df记录了六名学生在math,English和Chinese上的考试成绩
name subjects grades
0 apolo math 89
1 apolo english 78
2 apolo chinese 84
3 adm math 89
4 adm english 83
5 adm chinese 85
6 bolon math 77
7 bolon english 88
8 bolon chinese 79
9 ali math 89
10 ali english 86
11 ali chinese 83
12 cathy math 95
13 cathy english 90
14 cathy chinese 94
15 jack math 78
16 jack english 70
17 jack chinese 80
进行分组
print(df.groupby('name'))
这里返回的结果是一个DataFrameGroupBy
对象,后续的操作都是对这个DataFrameGroupBy
对象进行
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x000002DB778B6320>
在得到DataFrameGroupBy对象后,我们就可以根据需要进行相应的转换操作。
可以在DataFrameGroupBy对象后直接加
mean()
,sum()
,min()
,max()
之类的聚合函数进行相应的操作
如求平均值
df.groupby('name').mean()
输出:可以看到输出了每个学生的平均成绩,结果是一个DataFrame
,列索引是grades
,行索引是学生的名字,name
是行索引的名字。
grades
name
adm 85.666667
ali 86.000000
apolo 83.666667
bolon 81.333333
cathy 93.000000
jack 76.000000
大家可以再自己试一试sum()
, min()
, max()
方法
agg()
的功能更加强大,除了可以向agg()函数中传入聚合函数外,也常用列表、字典等形式作为参数。
传入聚合函数
:求平均值
df.groupby('name').agg('mean')
输出:和直接加聚合函数
的结果是一样的,但要注意的是,传入的是字符串,并不是真正的聚合函数。
grades
name
adm 85.666667
ali 86.000000
apolo 83.666667
bolon 81.333333
cathy 93.000000
jack 76.000000
传入列表
:求每个学生的平均成绩和最低成绩
df.groupby('name').agg(['mean', 'min'])
输出:结果是一个多索引的DataFrame
,df.groupby('name').agg(['mean', 'min'])['grade']
则是一个普通的DataFrame
grades
mean min
name
adm 85.666667 83
ali 86.000000 83
apolo 83.666667 78
bolon 81.333333 77
cathy 93.000000 90
jack 76.000000 70
apply()可以传入自定义的面向分组的函数。
求每个学生的数学平均成绩与英语平均成绩之差:
# 自定义函数 lambda表达式
df.groupby('name').apply(lambda x:x[x['subjects'] == 'math']['grades'].mean() - x[x['subjects'] == 'english']['grades'].mean())
输出:输出结果是一个Series
name
adm 6.0
ali 3.0
apolo 11.0
bolon -11.0
cathy 5.0
jack 8.0
dtype: float64
transform
调用函数在每个分组上产生一个与原df相同索引
的DataFrame,整体返回与原来对象拥有相同索引且已填充了转换后的值的DataFrame,相当于就是给原来的Dataframe添加了一列
。
transform
与agg
和apply
的区别相当于SQL中窗口函数
和分组聚合
的区别:transform
并不对数据进行聚合输出,而只是对每一行记录提供了相应聚合结果;而后两者则是聚合后的分组输出。
df.groupby('name').transform('mean')
输出:结果是每个学生的平均成绩
grades
0 83.666667
1 83.666667
2 83.666667
3 85.666667
4 85.666667
5 85.666667
6 81.333333
7 81.333333
8 81.333333
9 86.000000
10 86.000000
11 86.000000
12 93.000000
13 93.000000
14 93.000000
15 76.000000
16 76.000000
17 76.000000
主要参考了文章:https://blog.csdn.net/TSzero/article/details/115430661