fillna(0)会静默覆盖nan、none、pd.nat等所有缺失类型,易掩盖数据质量问题;ffill/bfill仅复制最近有效值而非插值;混合填充须分步处理,不可链式调用。

fillna(0) 会静默覆盖所有缺失类型,包括 NaN、None、pd.NaT
很多同学以为 fillna(0) 只填 NaN,其实它对 None、pd.NaT(时间缺失)、甚至某些空字符串(如果列是 object 类型且含空字符串)也会一并替换——但不会报错,容易掩盖数据质量问题。
实操建议:
- 先用
df.isna().sum()看清每列缺失值真实构成,尤其注意时间列是否混有pd.NaT - 若只想填数值型缺失,优先限定列:
df[["col_a", "col_b"]] = df[["col_a", "col_b"]].fillna(0) - 对时间列慎用
fillna(0),0 会被转成1970-01-01,改用fillna(pd.Timestamp("2000-01-01"))更安全
ffill 和 bfill 不是“插值”,它们只复制最近的有效值
ffill(forward fill)和 bfill(backward fill)本质是“搬运”而非计算,不依赖数值规律。比如温度序列中连续三天缺失,ffill 会把前一个有效值重复三次,而不是线性推算。
常见错误现象:
- 在带趋势的数据上滥用
ffill,导致平直假象(如股价突降后连续多日被“冻结”在降前值) - 对分类列(如
"status")误用ffill,把 “pending” 填满后续本该是 “done” 的行 - 未设
limit参数,跨过长空白段传播错误值(例如用户行为日志中断一周,ffill把中断前的操作“延续”到下周)
推荐写法:df["value"].ffill(limit=3) 控制最多向前补 3 行,留出明显断点供人工核查
混合填充策略必须分步做,不能链式调用 fillna 后再 ffill
df.fillna(0).ffill() 这种写法看似简洁,实际会先强行把所有缺失变 0,再用 0 去 forward fill——结果是整段都变成 0,完全失去原始分布特征。
正确顺序取决于业务逻辑:
- 想优先保留局部趋势?先
ffill/bfill,再对仍空的头尾用fillna(0)或fillna(df["x"].mean()) - 想确保数值列不为 null,但分类列保持原缺失?分开处理:
df.select_dtypes(include="number").fillna(0)+df.select_dtypes(include="object").ffill() - 时间序列中需按组填充?必须用
groupby+apply:df.groupby("user_id")["score"].ffill()
fillna 传字典时键名必须严格匹配列名,且不支持正则或通配
写 df.fillna({"col_*": 0}) 是无效的,Pandas 不解析通配符。键必须是真实存在的列名字符串,且大小写、空格、下划线全要对得上。
容易踩的坑:
- 列名含空格(如
"sales amount"),字典里写成"sales_amount"→ 完全不生效 - 从 Excel 读入后列名末尾带不可见空格,用
df.columns.tolist()一眼看不出来,得用repr(df.columns)检查 - 想批量匹配数值列但又怕漏,不如用
df.fillna(df.select_dtypes(include="number").mean().to_dict())
性能提示:对超大表(千万行+),避免用 fillna 传函数(如 lambda x: x.mean()),会触发逐列 apply,比传标量慢一个数量级










