数据清洗基础:缺失值处理策略(删除、填充)
信用评分建模的第一步往往是对原始数据的「大扫除」。我做了这么多年特征工程,发现缺失值是最常见也最容易被低估的问题。不少新手一上来就用均值填充所有空值,结果模型反而变差了——嗯,这里有大坑。
这一节,我带你从头捋一遍缺失值的处理逻辑。不只是讲「怎么删怎么填」,更重要的是什么时候删、什么时候填,以及填什么值。你想想看,一个客户的收入字段如果缺失了,是直接删掉这条记录,还是用平均值顶上去?每种选择的后果可能完全不同。
1. 先诊断:缺失值从哪里来?
拿到数据后,我习惯先用 df.isnull().sum() 看一眼每个特征的缺失数量。然后问自己三个问题:
- 缺失是随机的吗?——例如问卷填写时漏选,这类缺失往往可以填充。
- 缺失有内在含义吗?——比如「年收入」为空,可能客户是学生或无业,本身就不该填。
- 缺失比例多大?——超过 50% 的特征,我通常直接砍掉,除非它有极强的业务意义。
💡 我的经验:信用评分数据里,缺失往往不是随机的。比如「贷款逾期天数」为空,通常意味着「从未逾期」,这是个信息。直接填充 0 反而比删掉更合理。
2. 删除策略:简单但别乱用
删除是最直接的方法,但代价也最大。我按两种场景来聊。
2.1 删除缺失行(listwise deletion)
适用场景:样本量大(几十万条以上),缺失行占比很小(< 5%),且缺失完全随机。直接用 df.dropna() 搞定。
# 删除所有含缺失值的行
df_clean = df.dropna()
print(f"删除前:{df.shape[0]} 行,删除后:{df_clean.shape[0]} 行")
⚠️ 我曾经踩过的坑:某次信用评分项目,客户数据有 8 万条,缺失行只占 3%,我直接 drop 了。结果模型上线后,对某类客群预测偏差极大。后来发现这些缺失行恰好集中在「自由职业者」群体,删除导致样本分布偏移。所以——删除前一定要确认缺失是否随机。
2.2 删除缺失列(列删除)
当一个特征的缺失率超过 60%,我个人建议直接丢掉。保留它会引入大量噪声,填充也很难挽救。比如「配偶收入」在单身客户中全部缺失,那就干脆删掉整列。
# 删除缺失比例超过 60% 的特征
threshold = 0.6
df_clean = df.loc[:, df.isnull().mean() < threshold]
3. 填充策略:根据场景选方法
绝大多数时候我们不舍得删数据,那就得「填」。我整理了一张策略对比表,帮你快速决策。
| 填充方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 均值 / 中位数填充 | 数值型特征,数据分布对称(均值)或偏态(中位数) | 简单快速,保持总体均值不变 | 会降低方差,扭曲分布形状 |
| 众数填充 | 分类特征(如职业、学历) | 保持类别分布 | 可能过度强调某个常见类别 |
| 前向/后向填充 | 时间序列数据(如月度消费记录) | 利用时间连续性,自然合理 | 首尾缺失无法填充 |
| 插值法 | 有序数值特征(年龄、评分) | 平滑过渡,不引入离群值 | 计算量大,假设线性关系时可能不准 |
| 模型预测填充 | 缺失值可能与其他特征高度相关时 | 利用已知信息推测,精度较高 | 容易过拟合,增加复杂度 |
我个人的经验法则是:先看缺失比例和数据类型。比例低(< 5%)就用中位数(数值型)或众数(分类型);比例中等(5%~30%)可以试试分组填充或插值;比例高、又有明显规律,才考虑模型预测。
3.1 实战代码:几种常用填充
import pandas as pd
import numpy as np
# 中位数填充(数值型)
df['age'] = df['age'].fillna(df['age'].median())
# 众数填充(分类型)
df['education'] = df['education'].fillna(df['education'].mode()[0])
# 按分组填充(比如按性别填充收入中位数)
df['income'] = df.groupby('gender')['income'].transform(
lambda x: x.fillna(x.median())
)
# 前向填充(时间序列)
df['monthly_spend'] = df['monthly_spend'].fillna(method='ffill')
🔍 注意:填充完后一定要对比一下填充前后的分布。用
df.describe() 或画个直方图,看看均值、标准差变化是否合理。如果方差大幅缩小,说明填充过度集中了。4. 决策流程图:我平时用这个逻辑来选策略
下面这张图总结了我处理缺失值的核心思路。你可以把它贴在工位上,每次遇到缺失值都走一遍流程。
5. 避坑指南——我多年血泪总结
- 别批量统一填充同一个值。比如所有数值列都填 0,会破坏特征间相关性。我曾经看到有人把「年龄」缺失填 0,结果模型认为这群人年龄极小,信贷策略全错了——0 是非法年龄啊!
- 对缺失本身做标记。我习惯在填充的同时增加一个二值特征
age_missing_flag(1 表示原来缺失,0 表示有值)。这样模型可以学到「缺失」本身是否包含信息。 - 模型预测填充别跟目标变量泄露。如果你用目标变量(如「是否违约」)去预测缺失值,那就相当于提前告诉了模型答案。我早期犯过这个错,导致线上 AUC 跌了 0.05。
- 交叉验证过程中不能全局填充。要在每一折的训练集上计算填充统计量(如中位数),再应用到验证集。否则会造成数据泄露。
💡 一个小技巧:对于信用评分数据,很多缺失其实是「0」的替代(比如「逾期次数」为空表示从未逾期)。我会先跟业务方确认,再决定是填 0 还是填一个特殊值(如 -1),然后单独标记。
6. 小结一下
缺失值处理没有银弹。核心就两句话:能保留信息就别删,能利用规律就别乱填。我建议你在项目中每次都走一遍上面那幅决策图,再结合业务理解调整。对了,处理完之后一定要做个简单的分布对比——把填充前后的特征分布画在一起,一眼就能看出问题。