概述
transform()对每个组应用函数,但将结果广播回原始形状,而不是像agg()那样折叠为每行一个结果。常用于「组内减去均值」「组内填充缺失值」「标准化」等场景。
4.1 transform()
签名
GroupBy.transform(func, *args, engine=None, engine_kwargs=None, **kwargs)
| 参数 | 说明 |
|---|---|
func | 函数名("mean")、可调用对象、函数列表或字典 |
*args / **kwargs | 传递给 func 的额外参数 |
engine | "cython" 或 "numba",性能优化用 |
import pandas as pd
import numpy as np
df = pd.DataFrame({
"部门": ["A", "A", "B", "B", "C"],
"薪资": [8000, 9500, 12000, 11000, 9000],
})
# 单列 transform:将聚合结果广播回每一行
df["平均薪资"] = df.groupby("部门")["薪资"].transform("mean")
df["薪资偏差"] = df.groupby("部门")["薪资"].transform(lambda x: x - x.mean())
# transform 多列
df.groupby("部门")[["薪资"]].transform("mean")
# 字典方式:不同列不同函数
df.groupby("部门").transform({"薪资": "mean"})
# 传入自定义函数
df.groupby("部门")["薪资"].transform(lambda x: (x - x.mean()) / x.std())返回结果要求
transform中的函数必须返回与原始组等长的序列(或广播标量),否则会报错。
4.2 与聚合的区别
| 维度 | agg() 聚合 | transform() 变换 |
|---|---|---|
| 结果行数 | 每组 1 行(折叠) | 与原数据行数一致(广播) |
| 典型函数 | sum、mean、count 等返回标量 | cumsum、rank、x - x.mean() 等返回序列 |
| 索引 | 分组键作为新索引 | 保留原索引 |
| 用途 | 汇总统计 | 组内计算、填充、标准化 |
| 单列结果 | 同组只保留 1 个值 | 同组每行都有相同计算结果 |
# 聚合结果
df.groupby("部门")["薪资"].agg("mean")
# 部门
# A 8750.0
# B 11500.0
# C 9000.0
# Name: 薪资, dtype: float64
# 变换结果(每行都有值)
df.groupby("部门")["薪资"].transform("mean")
# 0 8750.0
# 1 8750.0
# 2 11500.0
# 3 11500.0
# 4 9000.0
# Name: 薪资, dtype: float644.3 填充缺失
经典应用:使用每组非缺失值的均值/中位数填充该组缺失值。
df_missing = pd.DataFrame({
"部门": ["A", "A", "A", "B", "B", "B"],
"薪资": [8000, None, 9500, 12000, None, 11000],
})
# 用每组均值填充缺失值
df_missing["薪资"] = df_missing.groupby("部门")["薪资"].transform(lambda x: x.fillna(x.mean()))
# 或更简洁:transform 后赋值
df_missing["薪资"] = df_missing.groupby("部门")["薪资"].transform("mean")
# 用中位数填充
df_missing["薪资"] = df_missing.groupby("部门")["薪资"].transform(lambda x: x.fillna(x.median()))
# 分组前向/后向填充
df_grouped = df_missing.sort_values("部门")
df_grouped["薪资"] = df_grouped.groupby("部门")["薪资"].transform(lambda x: x.fillna(method="ffill"))4.4 标准化
组内标准化(Z-score)与组内归一化的常见写法:
# Z-score 标准化:(x - mean) / std
df["薪资_zscore"] = df.groupby("部门")["薪资"].transform(lambda x: (x - x.mean()) / x.std())
# 最小-最大归一化:(x - min) / (max - min)
df["薪资_minmax"] = df.groupby("部门")["薪资"].transform(
lambda x: (x - x.min()) / (x.max() - x.min())
)
# 除以组内总和(占比)
df["薪资占比"] = df.groupby("部门")["薪资"].transform(lambda x: x / x.sum())
# 多个变换函数同时计算
df.groupby("部门")["薪资"].transform(["mean", "sum"])也可使用
apply()实现 transform 效果,但transform性能更优且保留原始索引。
相关笔记
- 上一节:分组与聚合-3-聚合
- 下一节:分组与聚合-5-过滤