做特征工程这几年,我最大的感受就是:数据里的缺失值,从来不是“少几个数”那么简单,而是整个建模流程里最容易埋雷、也最容易被轻视的一环。 很多朋友拿到数据第一步就是 dropna() 一把梭,结果模型跑完才发现泛化能力一塌糊涂;也有朋友用了最传统的均值填充,结果特征分布被硬生生“压扁”,模型学不到真实规律。这些问题我在《Python 应用机器学习:代码实战指南》的笔记11里专门梳理过,今天就把这套特征缺失值插补的全攻略展开聊透,从缺失机制判断、常用插补方法对比,到完整可跑的 Python 代码和避坑经验,一次性讲明白。
这篇文章适合谁?如果你是刚接触机器学习、正在被各种“脏数据”折磨的初学者,或者已经用 sklearn 跑通几个模型但发现特征工程始终差口气的从业者,都可以从里面找到直接能用的思路和代码。核心就解决一件事:当数据出现缺失,你该怎么科学决策,而不是凭感觉处理。
1. 内容整体设计与思路拆解
1.1 为什么缺失值处理能决定模型“生死”
很多人会问,缺失值不就是删掉或者填个均值吗?能有多大影响?我直接用一次实际项目的对比来说话。之前做一个金融风控模型,原始特征有30多个,其中有5个特征缺失率在10%以上。一开始图省事,所有缺失直接 fillna(0),模型 AUC 只有0.71;后来换了迭代插补,AUC 提升到了0.79。这8个百分点的差距,在风控场景里可能就对应着几百万的坏账差异。
本质原因在于:缺失值本身可能携带信息。 比如用户填写借款申请表时,“月收入”这一栏空白,可能说明他没有固定收入,这本身就是个强信号。如果你直接填个平均值,就把这个信号抹掉了;填0,又会把分布拉得极其不均衡。所以缺失值处理不是“填空题”,而是“信息提取与重建”的过程。
另一个容易被忽略的问题是算法层面的假设。线性回归、逻辑回归、SVM 这类模型要求输入是完整的数值矩阵,缺失值直接会报错。树模型虽然能处理缺失,但不同实现方式(如 XGBoost 自动学习缺失方向)在不同缺失机制下表现差异很大。所以你需要先搞清楚数据为什么缺失,再决定用什么策略。
1.2 三类缺失机制:先判断“为什么缺”再谈“怎么补”
在统计学里,缺失值有三种机制,这个决定了你到底能不能用简单方法填充:
| 缺失机制 | 含义 | 例子 | 处理难度 |
|---|---|---|---|
| MCAR(完全随机缺失) | 缺失与任何变量无关 | 传感器偶发断电导致数据丢失 | 最简单,任何方法都可以 |
| MAR(随机缺失) | 缺失与其他已观测变量有关 | 收入越高越不愿意填写收入 | 中等,可用其他特征预测 |
| MNAR(非随机缺失) | 缺失与缺失值本身有关 | 收入低的人故意不填收入 | 最难,需要附加假设 |
我见过最多的就是 MAR 情况。比如电商订单数据里,“优惠券使用金额”字段经常缺失,通过分析发现是”未使用优惠券”的订单该字段为空,这就与订单本身特征强相关。此时如果用简单均值填充就会污染数据,更好的办法是把“是否缺失”也作为一个新特征,或者用一个分类模型来预测缺失值。
判断方法也很直接:把数据按“某列是否缺失”分成两组,对比其他特征的分布是否有显著差异(用 t 检验或 KS 检验)。如果差异明显,说明大概率不是 MCAR,你就得认真选择合适的插补策略。这一步很多人会跳过,但我建议一定要做,因为它直接决定后面的所有处理是否有效。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心细节解析与实操要点
2.1 缺失值探测:三行代码看清全貌
处理缺失值,第一步当然是把“敌情”摸清楚。我常用的就是 pandas 自带的探测方法,结合 missingno 这个可视化库,基本两分钟内能看清整个数据集的全貌。
python复制import pandas as pd
import numpy as np
import missingno as msno
import matplotlib.pyplot as plt
# 读取数据
df = pd.read_csv('customer_data.csv')
# 法1:数值化概览,看每列缺失个数和比例
missing_stats = pd.DataFrame({
'缺失个数': df.isnull().sum(),
'缺失比例': df.isnull().mean().round(4)
})
print(missing_stats[missing_stats['缺失个数'] > 0].sort_values('缺失比例', ascending=False))
# 法2:可视化矩阵,快速定位缺失模式
msno.matrix(df)
plt.show()
missingno 的矩阵图特别有用,它会把每个样本按行展示,白线就代表缺失位置。如果缺失呈现出明显的“带状”或“块状”,说明缺失不是随机分布的,背后有规律可循。比如下图如果有连续的几列同时出现大面积白带,可能就是某个采集批次整体出了问题,那直接考虑剔除该批次而不是逐列填充。
2.2 缺失值类型与“是否缺失”特征工程
在动手填充之前,还有一个非常关键但经常被忽略的操作:把“是否缺失”这个信息做成二值特征。
python复制# 对所有含缺失的列,生成对应的缺失标记特征
for col in df.columns[df.isnull().any()]:
df[f'{col}_is_missi
