Chapter 07

分组与聚合

groupby SAC 机制深度解析,agg/transform/filter 三大操作,多键分组与自定义聚合

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()  # 变异系数
)

本章小结