概述
当内置聚合、
transform、filter无法满足需求时,可以使用apply()对每个组执行任意函数;pipe()则用于管道式地向分组对象传入函数,便于构建可复用流程。
6.1 apply()
签名
GroupBy.apply(func, *args, include_groups=True, **kwargs)
| 参数 | 默认值 | 说明 |
|---|---|---|
func | — | 作用在每个子组上的函数,可返回标量、Series 或 DataFrame |
include_groups | True | pandas 2.2+,是否将分组列包含在传递给函数的子组中 |
*args / **kwargs | — | 传给 func 的额外参数 |
特性
- 灵活性最高:返回标量 → 类似聚合;返回 Series/DataFrame → 自动拼接;
- 结果索引会叠加分组键(受
group_keys参数控制); - ⚠️
apply()内部会对结果做「对齐」处理,性能通常低于agg()/transform()。
示例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"部门": ["A", "A", "B", "B", "C"],
"员工": ["张三", "李四", "王五", "赵六", "孙七"],
"薪资": [8000, 9500, 12000, 11000, 9000],
"年龄": [30, 25, 40, 35, 28],
})
# 返回标量:等效于聚合
df.groupby("部门").apply(lambda x: x["薪资"].sum())
# 返回 Series:每组生成一个序列
df.groupby("部门").apply(lambda x: x["薪资"] - x["薪资"].mean())
# 返回 DataFrame:每组操作后自动拼接
df.groupby("部门").apply(lambda x: x.sort_values("薪资", ascending=False))
# 返回多行 DataFrame:每个组展开
df.groupby("部门").apply(lambda x: pd.DataFrame({
"统计量": ["人数", "平均薪资"],
"数值": [len(x), x["薪资"].mean()],
}))
# 传入额外参数
def 按阈值筛选(g, 阈值):
return g[g["薪资"] >= 阈值]
df.groupby("部门").apply(按阈值筛选, 阈值=9000)group_keys 的作用
# group_keys=True(默认):结果索引带有分组键层级
df.groupby("部门", group_keys=True).apply(lambda x: x.head(1))
# group_keys=False:不添加分组键层级,索引紧凑
df.groupby("部门", group_keys=False).apply(lambda x: x.head(1))6.2 pipe()
pipe()将GroupBy对象本身传给外部函数,用于函数式管道组合操作。
签名
GroupBy.pipe(func, *args, **kwargs)
# 定义一个接收 GroupBy 对象的函数
def 汇总并排序(gb, 列名):
return gb[列名].agg(["sum", "mean"]).sort_values("sum", ascending=False)
df.groupby("部门").pipe(汇总并排序, "薪资")
# 多步管道组合
def 计算均值(gb, 列名):
return gb[列名].mean()
def 转换为百分比(s):
return s / s.sum() * 100
result = (
df.groupby("部门")
.pipe(计算均值, "薪资")
.pipe(转换为百分比)
)
result
pipe()与apply()的区别
apply(func):func的入参是每个子组(DataFrame/Series);pipe(func):func的入参是整个 GroupBy 对象,一般用于串联外部处理函数。
常见管道模式
# 标准写法
df.groupby("部门").pipe(lambda gb: gb["薪资"].agg(["sum", "mean"]))
# 与 lambda 组合
df.groupby("部门").pipe(lambda gb: gb.size().sort_values(ascending=False))
# 自定义函数返回 DataFrame,继续处理
def 添加标记(gb, 标记值="★"):
s = gb["薪资"].sum()
return s.to_frame("总薪资").assign(标记=标记值)
df.groupby("部门").pipe(添加标记, "★")相关笔记
- 上一节:分组与聚合-5-过滤
- 下一节:分组与聚合-7-累积与排名