最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Pandasgroupby如何用?从入门到实战的分组聚合完全指南
时间:2026-08-06 14:06:50 编辑:袖梨 来源:一聚教程网
df.groupby() 是 Pandas 最强大的功能之一,遵循分 裂-应用-组合(Split-Apply-Combine)范式。

GroupBy 基础
创建 GroupBy 对象
import pandas as pdimport numpy as npdf = pd.DataFrame({ '部门': ['销售', '销售', '技术', '技术', '人事', '人事'], '员工': ['张三', '李四', '王五', '赵六', '钱七', '孙八'], '工资': [8000, 9000, 15000, 12000, 7000, 7500], '奖金': [2000, 2500, 3000, 3500, 1500, 1800], '入职年份': [2019, 2021, 2018, 2020, 2022, 2021]})# 按单列分组grouped = df.groupby('部门')# 返回 DataFrameGroupBy 对象,尚未执行任何计算# 按多列分组multi_grouped = df.groupby(['部门', '入职年份'])# 查看分组print(grouped.groups) # 返回 {组名: 行索引列表}print(grouped.indices) # 同上但返回 numpy 数组print(grouped.ngroups) # 分组数量for name, group in grouped: print(name, group.shape) # 遍历每个组聚合(Aggregation)
1. 基本聚合函数
# 对单个列聚合print(grouped['工资'].sum()) # 每组工资总和print(grouped['工资'].mean()) # 每组工资均值print(grouped['工资'].count()) # 每组非空计数print(grouped['工资'].std()) # 标准差print(grouped['工资'].min()) # 最小值print(grouped['工资'].max()) # 最大值print(grouped['工资'].median()) # 中位数print(grouped['工资'].first()) # 第一个值print(grouped['工资'].last()) # 最后一个值print(grouped['工资'].nunique()) # 唯一值数print(grouped['工资'].var()) # 方差print(grouped['工资'].sem()) # 均值的标准误差print(grouped['工资'].quantile(0.75)) # 0.75 分位数
2.agg()— 多函数聚合
# 方式一: 传入函数列表result = grouped['工资'].agg(['sum', 'mean', 'std', 'count'])print(result)# sum mean std count# 部门# 人事 14500 7250 353.5 2# 技术 27000 13500 2121.3 2# 销售 17000 8500 707.1 2# 方式二: 传入字典 -> 对不同列应用不同聚合result = grouped.agg({ '工资': ['sum', 'mean'], '奖金': ['min', 'max', 'sum']})print(result)# 方式三: 命名聚合(推荐!)result = grouped.agg( 总工资=('工资', 'sum'), 平均工资=('工资', 'mean'), 最高奖金=('奖金', 'max'), 人数=('员工', 'count'))print(result)3. 常用内置字符串聚合
| 函数名 | 说明 | 函数名 | 说明 |
|---|---|---|---|
| 'sum' | 求和 | 'prod' | 乘积 |
| 'mean' | 均值 | 'std' | 标准差 |
| 'median' | 中位数 | 'var' | 方差 |
| 'min' | 最小值 | 'max' | 最大值 |
| 'count' | 非空计数 | 'size' | 包括 NaN 的计数 |
| 'first' | 第一个 | 'last' | 最后一个 |
| 'nunique' | 唯一值数 | 'sem' | 均值标准误 |
| 'quantile' | 分位数 | 'ohlc' | Open-High-Low-Close |
4. 自定义聚合函数
# 自定义函数def range_func(x): return x.max() - x.min()print(grouped['工资'].agg(range_func))print(grouped['工资'].agg(lambda x: x.max() - x.min()))# 多列自定义print(grouped.agg(lambda x: x.max() - x.min()))
Transform — 组内变换
transform 返回与原始 DataFrame 等长的 Series,非常适合做组内标准化。
# 组内减去均值(去中心化)df['工资_组内去中心'] = grouped['工资'].transform(lambda x: x - x.mean())# 组内 Z-Score 标准化df['工资_ZScore'] = grouped['工资'].transform( lambda x: (x - x.mean()) / x.std())# 组内填充均值df['工资_组内补均值'] = grouped['工资'].transform( lambda x: x.fillna(x.mean()))# 常用内置 transformdf['工资_排名'] = grouped['工资'].transform('rank') # 组内排名df['工资_百分比'] = grouped['工资'].transform('pct_change') # 组内变化率df['工资_组均值'] = grouped['工资'].transform('mean') # 组均值广播agg vs transform 对比:
# agg 返回: 每个组一行print(grouped['工资'].agg('mean')) # shape: (3,)# transform 返回: 与原 DataFrame 相同行数print(grouped['工资'].transform('mean')) # shape: (6,)Filter — 组级筛选
# 保留组大小 >2 的组filtered = grouped.filter(lambda g: len(g) > 2)# 保留工资总和 > 20000 的组filtered = grouped.filter(lambda g: g['工资'].sum() > 20000)# 保留组内工资最大值 > 10000 的组filtered = grouped.filter(lambda g: g['工资'].max() > 10000)
Apply — 最灵活的组操作
# apply 可以返回任意形状的 DataFrame/Seriesdef top_n(group, n=1): return group.nlargest(n, '工资')result = grouped.apply(top_n, n=1)print(result)# 返回标量result = grouped.apply(lambda g: g['工资'].max() - g['工资'].min())# apply 可返回多列def summary(g): return pd.Series({ '人数': len(g), '平均工资': g['工资'].mean(), '工资范围': g['工资'].max() - g['工资'].min() })result = grouped.apply(summary)print(result)其他分组方式
1. 按函数分组
# 按索引长度的奇偶分组df.groupby(lambda idx: idx % 2 == 0).sum()# 按工资是否 > 10000 分组df.groupby(lambda idx: df.loc[idx, '工资'] > 10000).sum()
2. 按映射字典分组
mapping = {'张三': '组A', '李四': '组A', '王五': '组B', '赵六': '组B', '钱七': '组C', '孙八': '组C'}df.groupby(mapping).sum() # 注意: 按 index 映射df.groupby(df['员工'].map(mapping)).sum() # 按列值映射3.groupby的as_index参数
# as_index=True (默认): 分组键作为索引result = df.groupby('部门', as_index=True).sum()# as_index=False: 分组键保留为列(类似 SQL group by)result = df.groupby('部门', as_index=False).sum()4. 多个groupby参数
# level: 按 MultiIndex 的层级分组# axis: 按列分组 (axis=1)# sort: 是否对组键排序(默认 True,设为 False 提升性能)# dropna: 是否丢弃 NaN 组键(默认 True)# observed: 分类数据是否只显示出现的组# 性能优化设置df.groupby('部门', sort=False, dropna=True)实用分组技巧
累计操作
df['工资_累计和'] = grouped['工资'].cumsum() # 组内累计和df['工资_累计最大'] = grouped['工资'].cummax() # 组内累计最大值df['工资_累计积'] = grouped['工资'].cumprod() # 组内累计积
组内偏移
df['工资_上一行'] = grouped['工资'].shift(1) # 组内上移df['工资_下一行'] = grouped['工资'].shift(-1) # 组内下移df['工资_变化'] = grouped['工资'].diff() # 组内差分df['工资_变化率'] = grouped['工资'].pct_change() # 组内变化率
组内排名
df['工资排名'] = grouped['工资'].rank(method='dense', ascending=False)# method: 'average', 'min', 'max', 'first', 'dense'
取每组的头/尾
df.groupby('部门').head(2) # 每组前 2 行df.groupby('部门').tail(1) # 每组最后 1 行df.groupby('部门').nth(0) # 每组第 0 个df.groupby('部门').nth([0, -1]) # 每组第 0 和最后一个df.groupby('部门').nlargest(2, '工资') # 每组工资最高的 2 行df.groupby('部门').nsmallest(1, '奖金') # 每组奖金最低的 1 行抽样
df.groupby('部门').sample(n=1, random_state=42) # 每组抽 1 个df.groupby('部门').sample(frac=0.5, random_state=42) # 每组抽 50%链式操作
# Pandas 支持方法链result = (df .groupby('部门') .agg(总工资=('工资', 'sum'), 人数=('员工', 'count')) .query('人数 > 1') .sort_values('总工资', ascending=False) .reset_index())print(result)总结
相关文章
- 如何修改Linux SFTP默认端口 08-06
- ubuntu tigervnc如何限制访问 08-06
- 倒置动漫海滩错觉艺术 08-06
- 韩系甜美氛围感人像 08-06
- 米兰大教堂前的深夜拥抱 08-06
- 蚂蚁庄园小课堂今日最新答案2026年1月13日 08-06