3. 数据清洗基础:缺失值处理与异常值检测
数据清洗,说白了就是给原始数据「洗澡」。我见过太多人一上来就调模型,结果数据里全是坑,跑出来的结果自己都不敢信。今天咱们聊聊两个最基础也最头疼的问题:缺失值和异常值。
核心观点:数据清洗不是机械操作,而是基于业务理解的技术决策。你处理缺失值和异常值的方式,直接决定了模型的上限。
3.1 缺失值处理策略
信用评分模型里,缺失值太常见了。比如客户的工作年限、收入水平,总有人不愿意填或者填不全。我个人习惯,拿到数据第一件事就是看缺失率分布。
3.1.1 删除策略
什么时候该删?我总结了三类场景:
- 缺失率过高:某个字段缺失超过70%,我一般直接扔掉。你想想看,一个变量70%都是空的,它能提供什么信息?
- 随机缺失且样本充足:如果缺失是随机的,而且样本量够大,删掉几行影响不大。
- 目标变量缺失:这个没得商量,必须删。目标值都没有,你拿什么训练?
注意:千万不要盲目删除!我曾经在一个项目中,删掉了缺失收入的样本,结果发现这些样本恰恰是高逾期风险人群。删完之后模型反而变差了。嗯,这里要注意,删除前一定要分析缺失模式。
3.1.2 填充策略
填充是更常用的方法。我常用的填充方式有这些:
| 填充方法 | 适用场景 | 我的经验 |
|---|---|---|
| 均值/中位数填充 | 连续变量,分布较对称 | 收入字段我常用中位数,避免被极端值带偏 |
| 众数填充 | 分类变量 | 比如学历,填「本科」往往最安全 |
| 前向/后向填充 | 时间序列数据 | 信用卡消费记录里很常见 |
| 固定值填充 | 有业务含义的缺失 | 比如收入缺失填-1,表示「未知」 |
举个例子,假设我们有客户的月收入数据:
# 中位数填充示例
import pandas as pd
import numpy as np
# 模拟数据
data = {'income': [5000, 6000, np.nan, 8000, 5500, np.nan, 7000]}
df = pd.DataFrame(data)
# 计算中位数
median_income = df['income'].median()
print(f'收入中位数: {median_income}')
# 填充
df['income_filled'] = df['income'].fillna(median_income)
print(df)
3.1.3 模型预测填充
这个方法听起来高大上,其实原理很简单:用其他字段预测缺失值。我一般在缺失率10%-30%之间用这个方法。
具体做法:
- 把数据分成两部分:有缺失值的和无缺失值的
- 用无缺失值的数据训练一个模型(比如随机森林)
- 用训练好的模型预测缺失值
小技巧:模型预测填充虽然精度高,但计算量大。我建议只在关键字段上用,比如收入、年龄这种对信用评分影响大的变量。其他字段用简单填充就够了。
3.2 异常值检测与处理
异常值,说白了就是「不合群」的数据。信用评分里,异常值可能是欺诈行为,也可能是录入错误。怎么区分?得靠业务规则和技术方法结合。
3.2.1 IQR方法
IQR(四分位距法)是我最常用的方法,因为它不受极端值影响。原理很简单:
- 计算Q1(25%分位数)和Q3(75%分位数)
- IQR = Q3 - Q1
- 正常范围:[Q1 - 1.5*IQR, Q3 + 1.5*IQR]
- 超出这个范围的就是异常值
# IQR异常值检测
def detect_outliers_iqr(data, column):
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = data[(data[column] < lower_bound) |
(data[column] > upper_bound)]
return outliers
# 示例:检测年龄异常值
outliers_age = detect_outliers_iqr(df, 'age')
print(f'检测到 {len(outliers_age)} 个异常年龄')
避坑指南:我曾经在信贷数据里用IQR检测收入异常值,结果发现很多高收入客户被标记为异常。为什么?因为收入分布是右偏的,高收入人群虽然少,但他们是真实存在的。所以,IQR的1.5倍系数不是铁律,你可以根据业务调整到2倍甚至3倍。
3.2.2 Z-Score方法
Z-Score假设数据服从正态分布。它计算每个值偏离均值多少个标准差:
- Z = (x - μ) / σ
- 通常|Z| > 3 视为异常值
- |Z| > 2 视为可疑值
from scipy import stats
import numpy as np
# Z-Score异常值检测
z_scores = np.abs(stats.zscore(df['income']))
threshold = 3
outliers_z = df[z_scores > threshold]
print(f'Z-Score检测到 {len(outliers_z)} 个异常值')
你想想看,Z-Score和IQR有什么区别?我个人的理解是:
- 数据服从正态分布时,Z-Score更准确
- 数据偏态严重时,IQR更稳健
- 实际项目中,我两个方法都会用,然后取交集
3.2.3 业务规则
这个才是最重要的。技术方法只是辅助,业务规则才是根本。举个例子:
| 字段 | 业务规则 | 说明 |
|---|---|---|
| 年龄 | 18-100岁 | 低于18或高于100,大概率是录入错误 |
| 月收入 | 0-100万 | 超过100万需要核实,可能是企业主 |
| 信用卡额度 | 0-100万 | 超过100万需要特殊审批 |
| 逾期天数 | 0-999 | 超过999天,可能是数据错误 |
重要提醒:业务规则一定要和业务方确认。我曾经遇到过,一个客户的年龄显示150岁,我以为是录入错误准备删掉。结果业务方告诉我,这是系统默认值,表示「未知」。你看,没有业务理解,技术再强也会犯错。
3.3 本章知识体系
为了让你更直观地理解数据清洗的流程,我画了一张图:
这张图展示了数据清洗的核心逻辑:先检查数据质量,然后分两条线处理缺失值和异常值,最后汇聚成干净的数据。我在实际项目中,这个流程至少要走3-5轮才能放心。
最后说一句:数据清洗没有标准答案。同样的数据,不同业务场景处理方式完全不同。我的建议是:多问为什么,多和业务方沟通,多尝试不同的方法。数据清洗花的时间,会在模型效果上十倍百倍地回报你。