4、数据清洗实战:缺失值处理、异常值检测、重复数据处理、数据标准化
数据清洗,说白了就是给数据「洗澡」。
我做了这么多年风控,见过太多「脏数据」了。有的缺失值高达80%,有的异常值能把模型带偏十万八千里。你想想看,如果连数据都是脏的,模型再牛也白搭。
这一章,咱们就把数据清洗的四个核心环节掰开揉碎讲清楚。
4.1 缺失值处理
缺失值在风控数据里太常见了。用户填表时漏填、系统采集时丢包、数据迁移时出错……原因五花八门。
我个人习惯,拿到数据第一件事就是看缺失率。
import pandas as pd
import numpy as np
# 加载数据
df = pd.read_csv('credit_data.csv')
# 查看缺失率
missing_rate = df.isnull().sum() / len(df)
print(missing_rate.sort_values(ascending=False))
处理缺失值,我一般分三种情况:
- 缺失率 < 5%:直接删除。样本量大,影响不大。
- 缺失率 5% - 30%:填充。常用均值、中位数、众数,或者用模型预测。
- 缺失率 > 30%:建议删除该特征。别心疼,留着反而添乱。
重点提醒:填充缺失值时,千万别用全局均值。我在项目中遇到过,用全局均值填充后,模型效果反而变差了。后来改成按分组填充——比如按用户等级、按年龄段——效果立马提升。
# 按分组填充缺失值
df['income'] = df.groupby('age_group')['income'].transform(
lambda x: x.fillna(x.median())
)
小技巧:对于风控场景,缺失本身可能也是一种信号。比如「工作单位」字段缺失,可能意味着用户无业或自由职业,风险偏高。这时候可以保留缺失标记,作为一个新特征。
4.2 异常值检测
异常值,就是那些「离谱」的数据点。比如年龄填了200岁,月收入填了1个亿。
为什么会这样?可能是录入错误,也可能是恶意欺诈。在风控里,后者尤其值得关注。
我常用的检测方法有三种:
- 3σ原则:适用于正态分布的数据。超出均值±3倍标准差的值,视为异常。
- 箱线图法:适用于偏态分布。超出上下四分位数1.5倍IQR的值,视为异常。
- 孤立森林:适用于高维数据。通过随机切割来识别异常点。
# 箱线图法检测异常值
Q1 = df['loan_amount'].quantile(0.25)
Q3 = df['loan_amount'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df['loan_amount'] < lower_bound) |
(df['loan_amount'] > upper_bound)]
print(f"检测到 {len(outliers)} 个异常值")
注意:异常值不一定要删除。我曾经处理过一个欺诈检测项目,发现异常值里藏着大量欺诈样本。直接删除的话,模型就学不到欺诈模式了。建议先分析异常值的分布,再决定处理方式。
处理异常值,我一般这么做:
- 明显录入错误 → 直接删除或修正
- 合理但极端 → 截尾处理(用上下限替换)
- 疑似欺诈 → 单独标记,作为新特征
4.3 重复数据处理
重复数据,说白了就是同一条记录出现了多次。
原因很多:用户重复提交、系统重复采集、数据合并时没去重……
嗯,这里要注意:重复数据会放大某些样本的权重,导致模型偏向这些样本。在风控里,这可能会让模型高估或低估某些用户的风险。
# 检测重复数据
duplicates = df.duplicated(subset=['user_id', 'apply_date'], keep='first')
print(f"重复记录数: {duplicates.sum()}")
# 删除重复数据
df_clean = df.drop_duplicates(subset=['user_id', 'apply_date'], keep='first')
避坑指南:我曾经在处理一个信贷数据集时,发现重复率高达15%。一开始以为是数据质量问题,后来一查才发现,是同一个用户在不同时间申请了多次贷款。这种情况下,不能简单去重,而是要保留最近一次申请记录,或者聚合处理。
重复数据的处理原则:
- 完全重复 → 保留一条即可
- 部分重复 → 根据业务逻辑判断保留哪条
- 时间序列重复 → 保留最新记录
4.4 数据标准化
数据标准化,就是把不同量纲的数据统一到同一个尺度上。
你想想看,年龄是0-100,收入是0-100万,这两个特征直接放到模型里,收入会主导模型训练,年龄几乎没发挥作用。这显然不合理。
常用的标准化方法:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score标准化 | (x - μ) / σ | 数据近似正态分布 |
| Min-Max归一化 | (x - min) / (max - min) | 数据有明确边界 |
| Robust标准化 | (x - median) / IQR | 数据有异常值 |
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
# Z-score标准化
scaler = StandardScaler()
df['income_z'] = scaler.fit_transform(df[['income']])
# Min-Max归一化
scaler = MinMaxScaler()
df['income_mm'] = scaler.fit_transform(df[['income']])
# Robust标准化(抗异常值)
scaler = RobustScaler()
df['income_rb'] = scaler.fit_transform(df[['income']])
个人经验:在风控场景里,我比较推荐Robust标准化。因为风控数据经常有异常值,Min-Max会被异常值拉偏,Z-score也会受影响。Robust用中位数和IQR,抗干扰能力强很多。
标准化后的数据,均值为0,方差为1(Z-score),或者范围在0-1之间(Min-Max)。这样不同特征之间才能公平地参与模型训练。
好了,数据清洗的四个核心环节就讲完了。记住一句话:数据清洗不是一次性的工作,而是贯穿整个建模过程的。每次拿到新数据,都要重新审视一遍。
下一章,咱们聊聊特征工程——怎么从原始数据里挖出更有价值的特征。