概述

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: float64

4.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 性能更优且保留原始索引。


相关笔记