
本文介绍使用 pandas 的 `groupby().transform()` 结合 `mode()` 高效填充缺失值的方法,无需手动构建映射表,代码简洁、可读性强且性能优异。
在数据预处理中,按某一列(如 col_B)分组后,用该组内另一列(如 col_A)的众数(most frequent value) 填充其缺失值,是一种常见且语义合理的插补策略。相比全局均值或中位数填充,它能更好保留局部分布特征。
Pandas 提供了优雅的一行式解决方案:利用 groupby().transform() 将聚合结果广播回原始索引长度,并配合自定义函数处理每组内的缺失值。核心思路是:对每组 col_A 数据调用 .mode() 获取众数(注意处理空组),再用 .fillna() 完成替换。
以下是完整实现示例:
import pandas as pd
import numpy as np
# 构造示例数据
df = pd.DataFrame({
'col_A': [8, 7, 20, np.nan, 8, 9, 37, np.nan, np.nan],
'col_B': [31, 30, 83, 5, 31, 34, 158, 5, 30]
})
# 定义安全的众数填充函数(兼容空组与多众数情况)
def impute_mode(series):
modes = series.mode()
if len(modes) > 0:
return series.fillna(modes.iloc[0]) # 取第一个众数(.mode() 返回 Series)
else:
return series.fillna(series.dropna().iloc[0] if len(series.dropna()) > 0 else np.nan)
# 应用分组填充:按 col_B 分组,对 col_A 执行众数填充
df['col_A_filled'] = df.groupby('col_B')['col_A'].transform(impute_mode)
print(df)✅ 输出说明:当 col_B == 5 时,若该组 col_A 中 1 出现最频繁,则所有 col_B == 5 且 col_A 为 NaN 的行将被填为 1;同理适用于其他 col_B 组别。
注意事项:
- .mode() 返回 Series,即使只有一个众数也需用 .iloc[0] 提取标量值;
- 若某组内 col_A 全为 NaN,.mode() 返回空 Series,此时函数回退至 np.nan,避免报错;
- transform 保证输出长度与原 DataFrame 一致,天然支持赋值到新列;
- 不推荐使用 apply(lambda x: x.fillna(x.mode().iloc[0])) —— apply 在 transform 上下文中行为不一致,易出错。
该方法完全避免了手动构建索引列表、双重循环和中间 DataFrame,既符合 Pandas 的向量化哲学,又具备良好的可维护性与扩展性(如后续改为中位数或条件众数也只需修改函数内部逻辑)。










