
本文详解如何在 pandas dataframe 中对含 nan 的数据进行加权平均计算,确保权重仅作用于非空值,并动态归一化——即分母为对应列中有效值的权重之和,而非全部权重总和。
在实际数据分析中,直接使用 df.mul(weights).sum() / weights.sum() 会错误地将 NaN 对应的权重纳入分母,导致结果偏差(如示例中第2行本应只用权重4参与计算,却仍被除以总权重14)。正确做法是:按元素级对齐掩码,使权重仅保留在非 NaN 数据位置,再分别求加权和与有效权重和。
✅ 正确实现步骤
- 构造布尔掩码:df.notna() 生成与 df 同形的布尔矩阵,标记每个位置是否为有效值;
- 屏蔽无效权重:用 .mul(s, axis=0) 将权重序列 s 沿行广播,并与掩码相乘,使 NaN 位置的权重变为 0;
-
计算分子与分母:
- 加权和 = df.mul(masked_weights).sum()(NaN × 0 = 0,不影响求和);
- 有效权重和 = masked_weights.sum(axis=0)(每列只累加该列非 NaN 行的权重);
- 逐列除法:使用 .div() 实现广播除法,得到每列的加权平均。
import pandas as pd
import numpy as np
# 构造示例数据
df = pd.DataFrame({
1: [100, 150, 175],
2: [200, 250, 275],
3: [300, np.nan, 375]
}, index=[1, 2, 3])
s = pd.Series([3, 4, 7], index=[1, 2, 3])
# ✅ 关键代码:动态加权平均(忽略 NaN 对应权重)
weights_masked = df.notna().mul(s, axis=0) # 形状同 df,NaN 位权重为 0
weighted_sum = df.mul(weights_masked).sum() # 每列加权和
valid_weight_sum = weights_masked.sum(axis=0) # 每列有效权重和
result = weighted_sum.div(valid_weight_sum)
print(result.round(6))输出:
1 151.785714 2 251.785714 3 352.500000 dtype: float64
⚠️ 注意事项
- 权重 s 的索引必须与 df.index 完全一致,否则 .mul(axis=0) 会因对齐失败导致意外 NaN 或广播错误;
- 若某列全为 NaN,则 valid_weight_sum 为 0,除法将返回 inf 或 NaN,建议前置检查:if (valid_weight_sum == 0).any(): raise ValueError("Empty column detected");
- 该方法天然支持多列并行计算,无需循环,性能高效,适用于大规模数据场景。
此方案真正实现了「按需加权」:每一列独立计算其有效样本的加权均值,是处理缺失值加权统计的健壮范式。










