缺失值处理需先判断类型与业务含义,避免盲目填充;数值列优先中位数或分组填充,类别列查分布再处理;时间列慎填;删除需限定subset和how;imputer须fit_transform与transform分离;缺失本身可作特征;关键在业务确认。

用 pandas.DataFrame.fillna() 填什么,得先看缺失值类型和业务逻辑
直接 fillna(0) 或 fillna("未知") 是最常见也最容易翻车的操作。缺失不等于零,也不等于空字符串——比如销售额为 NaN,填 0 会把“未上报”扭曲成“卖了零元”,后续算平均值、做分组统计全偏了。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 先用
df.isna().sum()看哪些列缺失多,再用df[col].dtype判断是数值型还是类别型 - 数值列优先考虑中位数(抗异常值)或按分组填充,比如
df.groupby("region")["sales"].transform("median") - 类别列别硬填
"other",先查下df[col].value_counts(dropna=False),确认NaN是否真代表“未填写”,还是系统漏传 - 时间列慎用
fillna(pd.Timestamp("1970-01-01")),下游可能报OutOfBoundsDatetime
dropna() 不是删得越狠越好,得盯住 how 和 subset
默认 df.dropna() 会删掉任何含 NaN 的整行,但实际中往往只要求关键字段不为空。比如用户表里 email 和 phone 允许缺一个,但 user_id 必须有;盲目删行可能干掉 30% 有效数据。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 明确主键/必要字段,用
subset=["user_id", "reg_date"]限定检查范围 - 别依赖默认
how="any",该用how="all"时就用——比如某行所有特征列全是NaN才值得删 - 删之前先
df.dropna(..., inplace=False)并保存原shape,对比删了多少,心里有数 - 注意
axis=1删列的风险:一列缺失率 95%,删它可能让模型少一个强特征
用 sklearn.impute 做训练/预测一致性时,fit_transform() 和 transform() 必须分开
在建模流程里,用 SimpleImputer 对训练集 fit_transform() 后,测试集必须只调 transform()。如果对测试集也 fit_transform(),相当于用了两套统计量(比如训练集均值 vs 测试集均值),模型上线后结果就不可复现。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 永远把 imputer 当作模型一部分,和
StandardScaler一样存起来:joblib.dump(imputer, "imputer.pkl") - 别在
pd.DataFrame上直接用SimpleImputer,它默认输出numpy.ndarray;加keep_empty_features=True和手动重建列名更稳 - 类别型列别乱用
strategy="most_frequent"——如果某类在训练集里没出现过,测试集突然来了个新类,most_frequent就没意义了 - 时间序列场景下,
IterativeImputer容易过拟合,小数据集上不如前向填充method="ffill"
缺失值标记本身可能是信号,别急着抹掉 NaN
有些字段的缺失不是脏数据,而是业务事实。比如信贷审批中 credit_score 缺失,可能代表用户拒绝授权查询;电商订单中 coupon_code 缺失,大概率是自然流量而非投放渠道。把这些当成普通缺失去填,反而丢掉了关键区分维度。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 新增二值列显式记录缺失状态,比如
df["credit_score_is_missing"] = df["credit_score"].isna() - 把
NaN当作一个合法类别编码进模型(尤其树模型),比填均值更合理 - 警惕
df.replace({np.nan: "MISSING"})这种操作——字符串"MISSING"和真正缺失语义不同,且后续isna()就失效了 - 数据库导出时若用
NULL表示缺失,Python 读进来是NaN,但 Excel 导入可能变成空字符串,得统一用pd.read_csv(..., keep_default_na=False, na_values=["", "NULL", "N/A"])
缺失值处理最麻烦的从来不是代码怎么写,而是得反复跑回业务方确认:“这个空,到底是没填,还是不能填,还是不该填”。代码能填的只是 NaN,填不了上下文。










