groupby 内部机制
groupby 操作分为三步:分组(Split)→ 应用(Apply)→ 合并(Combine)。理解这个 SAC 模式有助于正确选择聚合方式和优化性能。
import pandas as pd
import numpy as np
df = pd.DataFrame({
'dept': ['Dev', 'Dev', 'HR', 'HR', 'Dev'],
'level': ['Senior', 'Junior', 'Senior', 'Junior', 'Senior'],
'salary': [80000, 50000, 70000, 45000, 85000],
'age': [32, 24, 35, 26, 30]
})
# 基础 groupby + 聚合
dept_stats = df.groupby('dept')['salary'].agg(['mean', 'min', 'max', 'count'])
print(dept_stats)
# mean min max count
# Dev 71666 50000 85000 3
# HR 57500 45000 70000 2
# 多列、多聚合
result = df.groupby('dept').agg(
avg_salary=('salary', 'mean'), # 命名聚合语法(推荐)
max_salary=('salary', 'max'),
avg_age=('age', 'mean'),
head_count=('salary', 'count')
)
# 多键分组
df.groupby(['dept', 'level'])['salary'].mean()
agg / transform / filter 三大操作
# agg:收缩 → 每组一行结果
df.groupby('dept')['salary'].agg('mean')
# dept
# Dev 71666.67
# HR 57500.00
# Name: salary, dtype: float64 ← 行数变少了
# transform:广播 → 结果与原 DataFrame 行数相同
df['dept_avg'] = df.groupby('dept')['salary'].transform('mean')
# 每行都有对应部门的均值
# Dev 员工的 dept_avg 都是 71666.67
# HR 员工的 dept_avg 都是 57500.00 ← 行数不变!
# filter:筛选满足条件的整个分组
# 只保留平均薪资 > 60000 的部门
df.groupby('dept').filter(lambda g: g['salary'].mean() > 60000)
# 返回 Dev 部门的所有行(Dev 均薪 71666 > 60000)
自定义聚合函数
# 用 lambda 或具名函数
def salary_range(s):
"""薪资范围(最大 - 最小)"""
return s.max() - s.min()
df.groupby('dept')['salary'].agg(salary_range)
# Dev 35000
# HR 25000
# 用 Named Aggregation 同时应用多个自定义函数
result = df.groupby('dept')['salary'].agg(
range_=salary_range,
cv=lambda x: x.std() / x.mean() # 变异系数
)
本章小结
- groupby 三步:Split(分组)→ Apply(计算)→ Combine(合并)
- agg 收缩结果(每组一行),transform 广播结果(行数不变),filter 筛选整组
- Named Aggregation
agg(name=(col, func))是 Pandas 1.1+ 的推荐写法,可读性高