当前位置: 代码网 > it编程>前端脚本>Python > Pandas groupby怎么用?从入门到实战的分组聚合完全指南

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

2026年08月05日 Python 我要评论
df.groupby() 是 pandas 最强大的功能之一,遵循分 裂-应用-组合(split-apply-combine)范式。groupby 基础创建 groupby 对象import pand

df.groupby() 是 pandas 最强大的功能之一,遵循分 裂-应用-组合(split-apply-combine)范式。

groupby 基础

创建 groupby 对象

import pandas as pd
import numpy as np

df = 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())
)

# 常用内置 transform
df['工资_排名'] = 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/series
def 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)

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com