3、坏样本数据清洗:缺失值处理、异常值检测与处理、重复数据去重、数据标准化
坏样本归因的准确性高度依赖数据质量。原始风控样本通常包含缺失值、异常值、重复记录和量纲不一致等问题,直接建模会导致归因偏差。本章系统讲解坏样本清洗的四大核心环节,确保进入归因模型的数据干净、可靠。
3.1 缺失值处理
缺失值在风控样本中普遍存在,原因包括用户未填写、系统采集失败、数据同步延迟等。处理策略需结合缺失率、业务含义和变量重要性。
| 缺失率范围 | 推荐策略 | 适用场景 |
|---|---|---|
| < 5% | 直接删除(行删除)或均值/中位数填充 | 随机缺失,样本量充足 |
| 5% – 20% | 统计填充(均值/中位数/众数)或模型预测填充 | 变量分布稳定,缺失机制可忽略 |
| 20% – 50% | 增加“缺失指示变量” + 中位数填充 | 缺失可能包含信息(如用户刻意隐瞒) |
| > 50% | 删除该变量,或转为二值特征(是否缺失) | 变量信息量极低,易引入噪声 |
风控经验:对于“收入”、“负债”等强预测力变量,即使缺失率较高(30%-40%),也建议保留并增加缺失标志位,因为“用户未填写收入”本身可能是一个坏样本信号。
3.2 异常值检测与处理
异常值会严重扭曲归因分析中的统计量和模型权重。风控场景中,异常值可能来自数据录入错误、设备异常或极端的欺诈行为。常用检测方法包括:
- Z-Score 方法:假设数据服从正态分布,|Z| > 3 视为异常。适用于对称分布变量。
- IQR 四分位距法:超出 Q1 - 1.5×IQR 或 Q3 + 1.5×IQR 为异常。对偏态分布更稳健。
- 孤立森林(Isolation Forest):基于树模型的无监督检测,适合高维数据。
- 业务规则阈值:如“年龄 > 120 岁”、“单日申请次数 > 50”直接判定异常。
处理策略对比:
- 截尾(Winsorize):将异常值替换为边界值(如 99% 分位数),保留样本量。
- 删除:仅当异常值明确为录入错误且比例 < 1% 时使用。
- 单独分组:将异常值作为独立类别编码,用于归因分析中的“极端行为”识别。
3.3 重复数据去重
重复记录会导致坏样本被重复计数,高估特定风险特征的权重。风控数据中的重复来源包括:
- 完全重复:所有字段完全相同,通常由系统重试或数据合并错误导致。
- 关键字段重复:身份证号、手机号、设备ID相同,但其他字段略有差异(如申请时间不同)。
- 近似重复:文本字段(如地址、公司名)存在拼写差异但指向同一实体。
| 重复类型 | 去重策略 | 保留规则 |
|---|---|---|
| 完全重复 | 直接删除重复行 | 保留任意一条 |
| 关键字段重复 | 按用户ID分组,保留最新一条 | 保留申请时间最近或信息最完整的记录 |
| 近似重复 | 使用编辑距离或Jaccard相似度聚类 | 保留置信度最高的记录,其余标记为可疑 |
注意:在坏样本归因中,不要盲目删除所有重复记录。如果同一用户短期内多次申请且均为坏样本,这本身可能是“多头借贷”或“欺诈试探”的重要特征,应保留并生成“重复申请次数”衍生变量。
3.4 数据标准化
坏样本归因通常涉及多个量纲不同的变量(如年龄、收入、逾期天数)。标准化可消除量纲影响,使模型系数和归因贡献度可比。常用方法:
- Z-Score 标准化:\( x' = \frac{x - \mu}{\sigma} \),适用于数据近似正态分布。
- Min-Max 归一化:\( x' = \frac{x - \min}{\max - \min} \),将数据映射到 [0,1] 区间,对异常值敏感。
- Robust 标准化:\( x' = \frac{x - \text{median}}{\text{IQR}} \),使用中位数和四分位距,对异常值鲁棒。
风控场景推荐:
- 对于收入、负债等长尾分布特征,优先使用 Robust 标准化,避免极端值主导归因结果。
- 对于年龄、授信额度等分布较均匀的特征,使用 Z-Score 标准化 即可。
- Min-Max 归一化适合后续需要固定区间输入的模型(如神经网络),但需先处理异常值。
3.5 清洗顺序与代码示例
建议按照以下流水线顺序执行数据清洗,避免后续步骤受前序未处理问题影响:
- 去重 → 2. 异常值检测与处理 → 3. 缺失值处理 → 4. 数据标准化
import pandas as pd
import numpy as np
from scipy import stats
# 加载坏样本数据
df = pd.read_csv('bad_samples.csv')
# 1. 去重:基于用户ID保留最新记录
df = df.sort_values('apply_time').drop_duplicates(subset='user_id', keep='last')
# 2. 异常值处理:IQR 截尾
Q1 = df['income'].quantile(0.25)
Q3 = df['income'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
df['income'] = df['income'].clip(lower=lower_bound, upper=upper_bound)
# 3. 缺失值处理:中位数填充 + 缺失标志
df['income_missing'] = df['income'].isna().astype(int)
df['income'] = df['income'].fillna(df['income'].median())
# 4. 标准化:Robust 标准化
median_income = df['income'].median()
iqr_income = df['income'].quantile(0.75) - df['income'].quantile(0.25)
df['income_scaled'] = (df['income'] - median_income) / iqr_income
关键提醒:清洗参数(如中位数、IQR、截尾边界)必须基于训练集计算,并应用到验证集和测试集,避免数据泄露。在坏样本归因项目中,建议将清洗逻辑封装为函数,确保线上线下一致性。