在进行数据分析工作时可能会遇到需要将某些数据拆分分析后再进行聚合分析,在这个过程中用到的思想就是分组与聚合。Pandas 中提供了一些用于分析与聚合的方法,另外还提供了一些其他的分组级运算。
分组与聚合的原理
分组与聚合主要根据一定的拆分标准将原数据拆分成若干个分组,然后对每个分组应用统计运算,并把运算后的结果合并到一起,便于用户对不同分组的数据进行深入解读。在 Pandas 中,分组与聚合的基本过程可以分成三步:
- 拆分:是指在指定轴上进行的,既可以沿着列方向对原数据进行拆分,也可以沿着行方向对原数据进行拆分。
- 应用:对每个分组应用某个函数或方法进行相应的操作,并产生一个标量值。
- 合并:将分组名称与标量合成新的对象。
分组操作
Pandas 的 Series 类或 DataFrame 类中提供了一个用于实现分组操作的方法 groupby(),该方法可以按照不同的拆分标准将数据拆分成若干个分组。
groupby(by = None ,axis = 0 ,level = None ,as_index = True ,sort = True ,groupby_keys = True , sequeeze = NoDefault.no_default ,observed = False ,dropna = True)- by:用于确定分组的拆分标准
- axis:表示沿着行或列拆分
- 0 / index:沿着行拆分
- 1 / columns:沿着列拆分
- as_index:表示是否返回以分组标签作为索引对象输出(默认为 True)
- sort:表示是否对分组标签进行排序(默认为 True)
- 如果调用方是 Series 类的对象,则该方法返回值是 SeriesGroupby 对象
- 如果调用方是 DataFrame 类的对象,则该方法返回值是 DataFrameGroupby 对象
参数 by 决定了按照什么样的标准对数据进行分组,该参数支持多种形式的值,包括列标签、列表、数组、Series 类对象、字典或函数其中列表或数组的长度必须与待拆分行或列方向上的数据个数相等。
df = pd.DataFrame({'key':['K0','K1','K2','K4','K4','K0','K3','K4','K5','K1'],'A':['A0','A1','A2','A3','A4','A5','A6','A7','A8','A9']})
# 结果
# key A
# 0 K0 A0
# 1 K1 A1
# 2 K2 A2
# 3 K4 A3
# 4 K4 A4
# 5 K0 A5
# 6 K3 A6
# 7 K4 A7
# 8 K5 A8
# 9 K1 A9
gb= df.groupby(by='key')
for i in gb:
print(i)
# 结果
# ('K0', key A
# 0 K0 A0
# 5 K0 A5)
# ('K1', key A
# 1 K1 A1
# 9 K1 A9)
# ('K2', key A
# 2 K2 A2)
# ('K3', key A
# 6 K3 A6)
# ('K4', key A
# 3 K4 A3
# 4 K4 A4
# 7 K4 A7)
# ('K5', key A
# 8 K5 A8)
df = pd.DataFrame({'key':['K0','K1','K2','K4','K4','K0','K3','K4','K5','K1'],'A':['A0','A1','A2','A3','A4','A5','A6','A7','A8','A9']})
ser = pd.Series(['K0','K2','K3','K4','K2','K1','K5','K2','K2','K5'])
# 结果
# key A
# 0 K0 A0
# 1 K1 A1
# 2 K2 A2
# 3 K4 A3
# 4 K4 A4
# 5 K0 A5
# 6 K3 A6
# 7 K4 A7
# 8 K5 A8
# 9 K1 A9
# 按照 Series 类对象进行分组
gb = df.groupby(by = ser)
# 查看分组信息
for i in gb:
print(i)
# 结果
# ('K0', key A
# 0 K0 A0)
# ('K1', key A
# 5 K0 A5)
# ('K2', key A
# 1 K1 A1
# 4 K4 A4
# 7 K4 A7
# 8 K5 A8)
# ('K3', key A
# 2 K2 A2)
# ('K4', key A
# 3 K4 A3)
# ('K5', key A
# 6 K3 A6
# 9 K1 A9)
如果 Series 类对象的索引长度小于原对象的行索引长度,此时只会将原对象的部分数据进行分组,而不会将全部数据进行分组(多余的丢掉)
df = pd.DataFrame({'key':['K0','K1','K2','K4','K4','K0','K3','K4','K5','K1'],'A':['A0','A1','A2','A3','A4','A5','A6','A7','A8','A9']})
ser = pd.Series(['K0','K2','K3','K4','K1','K5','K5'])
# 结果
# key A
# 0 K0 A0
# 1 K1 A1
# 2 K2 A2
# 3 K4 A3
# 4 K4 A4
# 5 K0 A5
# 6 K3 A6
# 7 K4 A7
# 8 K5 A8
# 9 K1 A9
# 按照 Series 类对象进行分组
gb = df.groupby(by = ser)
# 查看分组信息
for i in gb:
print(i)
# 结果
# ('K0', key A
# 0 K0 A0)
# ('K1', key A
# 4 K4 A4)
# ('K2', key A
# 1 K1 A1)
# ('K3', key A
# 2 K2 A2)
# ('K4', key A
# 3 K4 A3)
# ('K5', key A
# 5 K0 A5
# 6 K3 A6)
df = pd.DataFrame({'a':[1,5,9,78,15],
'b':[2,9,54,13,24],
'c':[8,7,36,24,15],
'd':[6,8,45,32,31]})
# 创建一个表示分组标准的字典
rule = {'a':'one','b':'two','c':'three','d':'four'}
# 结果
# a b c d
# 0 1 2 8 6
# 1 5 9 7 8
# 2 9 54 36 45
# 3 78 13 24 32
# 4 15 24 15 31
# 字典必须加上,按列方向分
gb = df.groupby(by=rule,axis=1)
# 查看分组信息
for i in gb:
print(i)
# 结果
# ('four', d
# 0 6
# 1 8
# 2 45
# 3 32
# 4 31)
# ('one', a
# 0 1
# 1 5
# 2 9
# 3 78
# 4 15)
# ('three', c
# 0 8
# 1 7
# 2 36
# 3 24
# 4 15)
# ('two', b
# 0 2
# 1 9
# 2 54
# 3 13
# 4 24)
df = pd.DataFrame({'a':[1,5,9,78,15],
'b':[2,9,54,13,24],
'c':[8,7,36,24,15],
'd':[6,8,45,32,31]},index=['Leo','Jack','Alice','Jeo','Helen'])
gb=df.groupby(by = len)
# 结果
# a b c d
# Leo 1 2 8 6
# Jack 5 9 7 8
# Alice 9 54 36 45
# Jeo 78 13 24 32
# Helen 15 24 15 31
# 统计行索引的长度
gb = df.groupby(by=len)
# 查看分组信息
for i in gb:
print(i)
# 结果
# (3, a b c d
# Leo 1 2 8 6
# Jeo 78 13 24 32)
# (4, a b c d
# Jack 5 9 7 8)
# (5, a b c d
# Alice 9 54 36 45
# Helen 15 24 15 31)
查看分组信息
通过访问 GroupBy 对象的 groups 属性查看分组的信息,groups 属性的值是一个字典,字典中每个键值对对应一个分组。
语法:groups(分组名称)
df = pd.DataFrame({'a':[1,5,9,78,15],
'b':[2,9,54,13,24],
'c':[8,7,36,24,15],
'd':[6,8,45,32,31]},index=['Leo','Jack','Alice','Jeo','Helen'])
gb=df.groupby(by = len)
# 结果
# a b c d
# Leo 1 2 8 6
# Jack 5 9 7 8
# Alice 9 54 36 45
# Jeo 78 13 24 32
# Helen 15 24 15 31
# 统计行索引的长度
gb = df.groupby(by=len)
# 查看分组信息
gb.groups
# 结果
# {3: ['Leo', 'Jeo'], 4: ['Jack'], 5: ['Alice', 'Helen']}
# 或
gb.get_group(3)
数据聚合
聚合一般是指对每个分组执行一些操作,比如求平均值、最大值等,并把操作后所得的结果整合到一起,生成一组新数据。Pandas 中可以通过两种方式实现聚合操作,分别是内置的统计方法和 agg() 方法。
通过统计方法聚合数据
通过某些方法可以直接应用到分组,对分组的数据进行聚合操作。需要注意的是,如果分组数据中有缺失值 NaN,那么在进行聚合操作时会自动忽略 NaN 值。
df = pd.DataFrame({'key':[1,5,1,3,15],
'b':[2,9,54,13,24],
'c':[8,7,36,24,15],
'd':[6,8,45,32,31]},index=['Leo','Jack','Alice','Jeo','Helen'])
gb = df.groupby('key')
gb.mean()
# 结果
# b c d
# key
# 1 28.0 22.0 25.5
# 3 13.0 24.0 32.0
# 5 9.0 7.0 8.0
# 15 24.0 15.0 31.0
通过 agg() 方法聚合数据
在 Pandas 中,除了直接使用统计方法聚合数据外,还可以使用 agg() 方法聚合数据,agg() 方法会在 Groupby 对象的指定行或指定列上应用一个或多个函数或方法,通过这些函数或方法对指定行或指定列的数据执行聚合操作,从而产生一个标量值。
agg(func = None ,axis = 0 ,*args ,**kwargs)- func:用于聚合数据的函数或方法,该参数的取值可以是匿名函数、函数名或方法名、包含函数名或方法名的列表或字典。
- axis:表示哈桑农户被应用到行或列,
- 0 / index:表示将函数应用到每一列
- 1 / columns:表示将函数应用到每一行
# 初始化数据
df = pd.DataFrame(np.arange(36).reshape((6,6)))
cols = list('abcdef')
df['key']=pd.Series(list('aaabbb'),name='key')
gb = df.groupby('key')
gb.agg(sum)
# 结果
# 0 1 2 3 4 5
# key
# a 18 21 24 27 30 33
# b 72 75 78 81 84 87
agg([(子列名,函数名)])
# 初始化数据
df = pd.DataFrame(np.arange(36).reshape((6,6)))
cols = list('abcdef')
df['key']=pd.Series(list('aaabbb'),name='key')
gb = df.groupby('key')
# 定义操作函数
def myStd(arr):
return arr.max() - arr.min()
gb.agg([('和',sum),('差',myStd)])
# 结果
# 0 1 2 3 4 5
# 和 差 和 差 和 差 和 差 和 差 和 差
# key
# a 18 12 21 12 24 12 27 12 30 12 33 12
# b 72 12 75 12 78 12 81 12 84 12 87 12
gb.agg([sum,myStd])
# 结果
# 0 1 2 3 4 5
# sum myStd sum myStd sum myStd sum myStd sum myStd sum myStd
# key
# a 18 12 21 12 24 12 27 12 30 12 33 12
# b 72 12 75 12 78 12 81 12 84 12 87 12
# 初始化数据
df = pd.DataFrame(np.arange(36).reshape((6,6)))
cols = list('abcdef')
df['key']=pd.Series(list('aaabbb'),name='key')
gb = df.groupby('key')
gb.agg({0:'sum',1:'mean'})
# 结果
# 0 1
# key
# a 18 7.0
# b 72 25.0
分组级运算
除了前面所介绍的,Pandas 中还支持其他用于分组级别的运算操作,包括数据转换和数据应用。
数据转换
数据转换是 Pandas 中强大的功能之一,它可以对分组执行一些汇总操作,且不改变分组之前的对象形状,使转换后对象的形状与分组前的形状保持一致。
transform(func ,*args ,engine = None ,engine_kwargs = None ,**kwargs)- func:表示每个分组应用的函数,取值可以使匿名函数或函数名
- *args:表示传递给 func 的位置参数
- **kwargs:表示传递给 func 的关键字参数
- 返回值:在其方法转换成功后通常会返回一个与原始对象形状相同的新对象,新对象中的数据是每隔分组应用函数后计算所得的结果。
# 初始化数据
df = pd.DataFrame(np.arange(36).reshape((6,6)))
key = list('aaabab')
# 分组
gb = df.groupby(key)
# 查看分组
dict([x for x in gb])['a']
# 结果
# 0 1 2 3 4 5
# 0 0 1 2 3 4 5
# 1 6 7 8 9 10 11
# 2 12 13 14 15 16 17
# 4 24 25 26 27 28 29
gb.transform('mean')
# 结果
# 0 1 2 3 4 5
# 0 10.5 11.5 12.5 13.5 14.5 15.5
# 1 10.5 11.5 12.5 13.5 14.5 15.5
# 2 10.5 11.5 12.5 13.5 14.5 15.5
# 3 24.0 25.0 26.0 27.0 28.0 29.0
# 4 10.5 11.5 12.5 13.5 14.5 15.5
# 5 24.0 25.0 26.0 27.0 28.0 29.0
原始对象中包含非数值的列:
# 初始化数据
df = pd.DataFrame(np.arange(36).reshape((6,6)))
df['key']=pd.Series(list('ababab'),name='key')
# 分组并运算
gb = df.groupby('key').transform('mean')
# 结果
# 0 1 2 3 4 5
# 0 12.0 13.0 14.0 15.0 16.0 17.0
# 1 18.0 19.0 20.0 21.0 22.0 23.0
# 2 12.0 13.0 14.0 15.0 16.0 17.0
# 3 18.0 19.0 20.0 21.0 22.0 23.0
# 4 12.0 13.0 14.0 15.0 16.0 17.0
# 5 18.0 19.0 20.0 21.0 22.0 23.0
补充
dict([x for x in df 对象]):用于查看分组信息[x for x in df 对象]:将 df 对象中的每个元素转换为列表
# 列表推导式
result = [x for x in gb]
# 等价的 for 循环
result = []
for x in gb:
result.append(x)
- 时间对象
import datetime as dtdt.datetime.strptime('时间' ,'时间占位符(格式)'):将字符串类型的日期转换为指定日期类型和格式的日期dt.datetime 对象.strftime(‘日期格式’):与上述相反dt.timedelta(days, seconds, microseconds, milliseconds, minutes, hours, weeks):日期加减运算(默认值均为 0 )
rename(列名):重命名列的标签索引
数据应用
当一些分组操作不适用于前面的方法,此时 apply() 方法便可以派上用场,此方法的使用是十分灵活的,既可以在前面介绍的标准用例中替代聚合和转换,也可以处理一些其他的操作。
apply(func ,*args ,**kwargs)- func:表示应用于分组的函数,该函数默认会将调用方作为第一个参数进行传递
- *args:表示传递给 func 的位置参数
- **kwargs:表示传递 func 的关键字参数
# 初始化数据
df = pd.DataFrame(np.arange(36).reshape((6,6)),columns=list('abcdef'))
df['key']=['foo','bar','foo','bar','foo','foo']
# 分组并运算
gb = df.groupby('key')
gb.apply(lambda x:x.describe())
# 结果
# a b c d e f
# key
# bar count 2.0 2.0 2.0 2.0 2.0 2.0
# mean 12.0 13.0 14.0 15.0 16.0 17.0
# std 8.0 8.0 8.0 8.0 8.0 8.0
# min 6.0 7.0 8.0 9.0 10.0 11.0
# 25% 9.0 10.0 11.0 12.0 13.0 14.0
# 50% 12.0 13.0 14.0 15.0 16.0 17.0
# 75% 15.0 16.0 17.0 18.0 19.0 20.0
# max 18.0 19.0 20.0 21.0 22.0 23.0
# foo count 4.0 4.0 4.0 4.0 4.0 4.0
# mean 16.0 18.0 18.0 20.0 20.0 22.0
# std 13.0 13.0 13.0 13.0 13.0 13.0
# min 0.0 1.0 2.0 3.0 4.0 5.0
25% 9.0 10.0 11.0 12.0 13.0 14.0
50% 18.0 19.0 20.0 21.0 22.0 23.0
75% 26.0 26.0 28.0 28.0 30.0 30.0
max 30.0 31.0 32.0 33.0 34.0 35.0
![[学习笔记 Day08]数据分析与应用:数据聚合与分组运算-资源刺客](https://images.kodo.cdn.itdka.cn/wp-content/uploads/2025/09/20250923154612655.png)

![[学习笔记 Day01]C++基础:简单的程序设计,始于梦想的开始!-资源刺客](http://images.kodo.cdn.itdka.cn/wp-content/uploads/2025/09/20250922171813209.webp)
![[学习笔记 Day06]Python 数据分析与应用:数据分析库 Pandas 的使用-资源刺客](http://images.kodo.cdn.itdka.cn/wp-content/uploads/2025/09/20250923154612655.png)
![[学习笔记 Day02]Vue基础:前端造梦,继续干!-资源刺客](http://images.kodo.cdn.itdka.cn/wp-content/uploads/2025/09/20250919193418264.jpeg)
![[Windows + Redis]Windows 部署安装 Redis 软件附注册 Windows 服务!-资源刺客](https://images.kodo.cdn.itdka.cn/wp-contents/uploads/2026/05/20260518221407144-科技感ENVI安装教程封面-5.png)

![[学习笔记 Day03]Redis 基础:Redis 设计的优化建议与最佳实践-资源刺客](https://images.kodo.cdn.itdka.cn/wp-contents/uploads/2026/05/20260521215425110-科技感ENVI安装教程封面-8.png)
![[学习笔记 Day02]Redis + OpenResty + Lua 实现多级缓存-资源刺客](https://images.kodo.cdn.itdka.cn/wp-contents/uploads/2026/05/20260521202850344-科技感ENVI安装教程封面-7.png)
![[学习笔记 Day 01] Redis 基础:从入门到缓存、主从、分片集群的深入-资源刺客](https://images.kodo.cdn.itdka.cn/wp-contents/uploads/2026/05/20260520211903179-科技感ENVI安装教程封面-6.png)
![[.NET Core + AOP + Attribute]实现横切面对象方法的额外操作,简化开发,提高效率!-资源刺客](https://images.kodo.cdn.itdka.cn/wp-contents/uploads/2026/05/20260510223906175-科技感ENVI安装教程封面-3.png)

![[自动化 + 手残党专属]宝塔安装AllinSSL证书管理教程-资源刺客](http://images.kodo.cdn.itdka.cn/wp-content/uploads/2025/11/20251112122722716.png)




暂无评论内容