3. 数据质量初探:缺失值、异常值、重复值对样本代表性的影响,以及初步清洗方法
在风控建模中,样本的代表性直接决定了模型在真实业务场景中的泛化能力。然而,原始数据几乎总是存在三类常见问题:缺失值、异常值和重复值。这些问题会扭曲数据分布,导致样本偏差,进而使模型产生系统性误判。本章将系统分析这三类问题对样本代表性的影响,并给出可落地的初步清洗方法。
3.1 三类数据质量问题对样本代表性的影响
数据质量问题并非简单的“脏数据”,它们会从不同维度破坏样本的随机性和分布一致性。下表总结了核心影响机制:
| 问题类型 | 对样本代表性的影响 | 典型风控场景 |
|---|---|---|
| 缺失值 | 导致有效样本量减少;若缺失机制非随机(MNAR),会引入系统性偏差,使样本分布偏离总体 | 收入字段缺失(高收入群体更倾向不填),导致模型低估还款能力 |
| 异常值 | 拉偏统计量(均值、方差),破坏变量分箱稳定性;极端值可能掩盖正常样本的分布模式 | 单笔交易金额异常高(如1000万),导致额度模型分箱边界失真 |
| 重复值 | 人为放大某些样本的权重,造成过拟合假象;若重复样本标签不一致,会引入噪声 | 同一用户多次申请被重复记录,导致模型对该用户行为过度学习 |
关键认知: 缺失、异常、重复不是孤立问题。例如,缺失值处理不当可能产生新的异常值;异常值剔除后可能改变缺失模式。清洗策略需要全局视角,而非逐字段独立操作。
3.2 缺失值:机制识别与初步处理
3.2.1 缺失机制分类
处理缺失值前,必须判断其缺失机制。错误的假设会导致严重的样本偏差:
- 完全随机缺失(MCAR): 缺失与任何变量无关。例如,系统随机丢包导致字段丢失。此时删除缺失样本不会引入偏差,但会损失样本量。
- 随机缺失(MAR): 缺失与其他已观测变量有关,但与自身真实值无关。例如,高收入用户更可能填写收入字段,但收入缺失与否与收入本身无关(仅与已观测的职业等级有关)。
- 非随机缺失(MNAR): 缺失与自身真实值有关。例如,负债率过高的用户刻意隐瞒负债数据。这是风控中最危险的情况,直接破坏样本代表性。
3.2.2 初步清洗方法
根据缺失机制和业务含义,选择以下方法:
- 删除法: 仅适用于MCAR且缺失比例<5%的场景。直接删除缺失样本或特征。
- 统计填充: 均值/中位数/众数填充。适用于MAR且缺失比例较低(5%-20%)。注意:填充会压缩方差,需在后续建模中做标记。
- 模型预测填充: 使用其他特征构建回归/分类模型预测缺失值。适用于MAR且特征间相关性较强。
- 缺失指示变量: 新增二值特征标记是否缺失,保留缺失信息。适用于所有机制,尤其适合MNAR场景。
风控实践建议: 对于收入、负债等关键字段,优先使用“缺失指示变量 + 中位数填充”的组合策略。这样既保留了缺失模式信息,又避免了模型因缺失值而无法计算。
3.3 异常值:识别方法与处理策略
3.3.1 异常值识别方法
异常值识别需结合统计方法和业务规则,避免误杀正常样本:
| 方法 | 适用场景 | 阈值设定 |
|---|---|---|
| Z-Score | 近似正态分布 | |Z| > 3 |
| IQR(四分位距) | 偏态分布、含离群点 | Q1 - 1.5*IQR 或 Q3 + 1.5*IQR |
| 业务规则 | 有明确业务约束 | 如年龄 < 18 或 > 100 |
| 孤立森林 | 高维数据、非线性边界 | 异常得分 > 0.6(可调) |
3.3.2 处理策略
- 截尾处理(Winsorization): 将异常值替换为阈值边界值。例如,将超过99%分位数的值替换为99%分位数。保留样本量,减少极端值影响。
- 缺失化处理: 将异常值视为缺失,然后按缺失值策略处理。适用于异常值比例较低且业务上认为异常值不可信的场景。
- 分箱处理: 将连续变量离散化,异常值落入单独箱体。例如,收入字段设置“>100万”为独立箱体。
避坑提示: 不要一次性剔除所有异常值。风控数据中,部分异常值(如极高收入、极低负债)可能代表优质客户群体。建议先做分箱观察,确认异常值群体的坏账率后再决定处理方式。
3.4 重复值:检测与去重策略
3.4.1 重复值类型
- 完全重复: 所有字段完全相同。通常由系统重复采集或数据合并错误导致。
- 部分重复: 关键字段(如身份证号、手机号)相同,但其他字段有差异。常见于用户多次申请、信息更新场景。
3.4.2 去重策略
| 场景 | 策略 | 注意事项 |
|---|---|---|
| 完全重复 | 保留第一条,删除其余 | 确认重复原因,避免误删时间序列数据 |
| 部分重复(标签一致) | 保留最新记录或合并信息 | 按时间戳或业务优先级选择 |
| 部分重复(标签冲突) | 标记为噪声,单独分析 | 标签冲突可能暗示数据源不一致,需溯源 |
实战案例: 某信贷平台发现模型在“手机号”字段上表现异常。经排查,约8%的用户存在重复申请记录,且重复样本的逾期标签不一致(一次逾期、一次正常)。去重后模型AUC提升0.03,说明重复值导致的标签噪声严重干扰了模型学习。
3.5 初步清洗流程与代码示例
以下是一个标准的数据质量初步清洗流程,适用于风控建模前的数据预处理阶段:
import pandas as pd
import numpy as np
def initial_clean(df, id_col='user_id', target_col='label'):
"""
风控数据初步清洗函数
"""
# 1. 重复值处理
print(f"去重前样本量: {len(df)}")
# 基于ID列去重,保留最新记录
df = df.sort_values('timestamp').drop_duplicates(subset=[id_col], keep='last')
print(f"去重后样本量: {len(df)}")
# 2. 缺失值统计
missing_ratio = df.isnull().mean()
print("缺失比例:\n", missing_ratio[missing_ratio > 0])
# 3. 异常值检测(IQR方法)
numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist()
for col in numeric_cols:
if col in [id_col, target_col]:
continue
Q1 = df[col].quantile(0.25)
Q3 = df[col].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outlier_mask = (df[col] < lower_bound) | (df[col] > upper_bound)
if outlier_mask.sum() > 0:
print(f"{col}: 发现 {outlier_mask.sum()} 个异常值 ({outlier_mask.mean():.2%})")
# 截尾处理
df[col] = df[col].clip(lower=lower_bound, upper=upper_bound)
# 4. 缺失值填充(中位数 + 缺失指示变量)
for col in missing_ratio[missing_ratio > 0].index:
df[f'{col}_is_missing'] = df[col].isnull().astype(int)
if df[col].dtype in ['float64', 'int64']:
df[col].fillna(df[col].median(), inplace=True)
else:
df[col].fillna(df[col].mode()[0], inplace=True)
return df
执行建议: 上述代码应在探索性数据分析(EDA)之后、特征工程之前执行。清洗过程中记录每一步的样本量变化和字段处理日志,便于后续回溯和审计。
3.6 清洗后的样本代表性校验
清洗完成后,必须验证样本代表性是否得到改善:
- 分布对比: 使用KS检验或卡方检验,对比清洗前后关键特征与总体(或拒绝样本)的分布差异。
- PSI(群体稳定性指标): 计算清洗前后样本的PSI值,PSI < 0.1表示分布稳定。
- 业务规则校验: 检查清洗后数据是否仍符合业务常识(如年龄、收入范围)。
通过上述校验,可以确认清洗操作是否真正改善了样本代表性,还是引入了新的偏差。如果校验不通过,需要回溯到缺失值、异常值或重复值的处理环节,调整策略后重新清洗。