一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Pandasgroupby如何用?从入门到实战的分组聚合完全指南

时间:2026-08-06 14:06:50 编辑:袖梨 来源:一聚教程网

df.groupby() 是 Pandas 最强大的功能之一,遵循分 裂-应用-组合(Split-Apply-Combine)范式。

Pandasgroupby怎么用?从入门到实战的分组聚合完全指南

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)

总结

热门栏目