3. 数据预处理基础:缺失值处理、异常值检测与标准化
在催收触达特征工程中,原始数据通常包含大量不完整、异常或量纲不一致的记录。直接使用这些数据建模会导致模型偏差、收敛缓慢甚至失效。本章系统讲解数据预处理的三大核心环节:缺失值处理、异常值检测与处理、数据标准化与归一化,并结合催收场景给出具体操作指南。
3.1 缺失值处理
催收数据中缺失值常见原因包括:客户未填写、系统采集故障、数据源合并遗漏等。缺失值处理策略分为删除、填充和模型预测三类,需根据缺失率、特征重要性及业务含义选择。
| 策略 | 适用场景 | 催收案例 | 注意事项 |
|---|---|---|---|
| 删除 | 缺失率 > 70% 或缺失完全随机 | 客户“第二联系人电话”缺失率 85% | 删除后样本量仍充足;避免删除关键特征 |
| 统计填充 | 缺失率 < 30%,特征分布稳定 | “近3月逾期天数”用中位数填充 | 均值受异常值影响大,优先用中位数/众数 |
| 模型预测 | 缺失率 10%~50%,特征间关联强 | 用“收入”“职业”预测“可支配收入”缺失值 | 避免信息泄露;需交叉验证评估预测误差 |
3.1.1 删除策略
直接删除缺失记录或特征,适用于缺失率极高或缺失完全随机的场景。在 Python 中可通过 dropna() 实现。
import pandas as pd
# 删除缺失率超过 70% 的特征
df = df.loc[:, df.isnull().mean() < 0.7]
# 删除缺失值超过 3 个的样本
df = df.dropna(thresh=df.shape[1] - 3)
3.1.2 填充策略
根据特征分布选择填充值:连续型特征用中位数或均值,离散型特征用众数或“未知”类别。填充后建议增加“是否缺失”指示列。
# 中位数填充连续特征
df['逾期天数'] = df['逾期天数'].fillna(df['逾期天数'].median())
# 众数填充离散特征
df['职业类型'] = df['职业类型'].fillna(df['职业类型'].mode()[0])
# 增加缺失指示列
df['逾期天数_missing'] = df['逾期天数'].isnull().astype(int)
3.1.3 模型预测填充
利用非缺失特征构建回归或分类模型,预测缺失值。常用算法包括 KNN、随机森林、线性回归。需注意避免目标变量泄露。
from sklearn.ensemble import RandomForestRegressor
# 分离缺失与非缺失样本
train = df[df['可支配收入'].notna()]
test = df[df['可支配收入'].isna()]
# 特征与目标
features = ['年龄', '收入', '职业编码', '历史还款次数']
X_train = train[features]
y_train = train['可支配收入']
X_test = test[features]
# 训练预测
model = RandomForestRegressor(n_estimators=100)
model.fit(X_train, y_train)
df.loc[df['可支配收入'].isna(), '可支配收入'] = model.predict(X_test)
3.2 异常值检测与处理
催收数据中的异常值可能源于录入错误、系统故障或极端客户行为。异常值会拉偏模型参数,需通过统计方法识别并处理。常用方法包括 IQR 和 Z-score。
3.2.1 IQR 方法(四分位距法)
适用于非正态分布数据。定义下界为 Q1 - 1.5×IQR,上界为 Q3 + 1.5×IQR,超出范围视为异常。
Q1 = df['逾期天数'].quantile(0.25)
Q3 = df['逾期天数'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 标记异常
df['逾期天数_异常'] = ((df['逾期天数'] < lower_bound) | (df['逾期天数'] > upper_bound)).astype(int)
3.2.2 Z-score 方法
适用于近似正态分布数据。计算每个样本的 Z 分数,通常 |Z| > 3 视为异常。
from scipy import stats
z_scores = stats.zscore(df['负债率'])
df['负债率_异常'] = (abs(z_scores) > 3).astype(int)
| 方法 | 适用分布 | 阈值 | 催收场景示例 |
|---|---|---|---|
| IQR | 偏态分布、有离群点 | 1.5×IQR | “逾期天数”右偏严重,IQR 更稳健 |
| Z-score | 近似正态分布 | |Z| > 3 | “年龄”近似正态,Z-score 更合适 |
3.2.3 异常值处理策略
- 截尾(Capping): 将异常值替换为边界值(Q3+1.5×IQR 或 Q1-1.5×IQR),保留样本量。
- 删除: 确认是录入错误且样本量充足时直接删除。
- 单独标记: 保留原始值,增加“是否异常”指示列,让模型自行学习。
# 截尾处理
df['逾期天数'] = df['逾期天数'].clip(lower=lower_bound, upper=upper_bound)
# 单独标记(保留原始值)
df['逾期天数_异常标记'] = ((df['逾期天数'] < lower_bound) | (df['逾期天数'] > upper_bound)).astype(int)
3.3 数据标准化与归一化
催收特征量纲差异巨大(如“年龄”0~100 vs “逾期金额”0~100000),直接输入模型会导致梯度下降震荡、距离度量失效。标准化和归一化是消除量纲影响的两种主要方法。
| 方法 | 公式 | 输出范围 | 适用模型 |
|---|---|---|---|
| Z-score 标准化 | z = (x - μ) / σ | 均值 0,标准差 1 | 线性回归、SVM、PCA、神经网络 |
| Min-Max 归一化 | x' = (x - min) / (max - min) | [0, 1] | KNN、神经网络、梯度下降类模型 |
| Robust 缩放 | x' = (x - median) / IQR | 受异常值影响小 | 含异常值的催收数据 |
3.3.1 Z-score 标准化
将数据转换为均值为 0、标准差为 1 的分布,适用于数据近似正态分布的场景。在 sklearn 中通过 StandardScaler 实现。
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df[['逾期天数', '负债率', '收入']] = scaler.fit_transform(df[['逾期天数', '负债率', '收入']])
3.3.2 Min-Max 归一化
将数据线性映射到 [0, 1] 区间,适用于有明确边界且无极端异常值的特征。注意:新数据超出原范围时需重新计算。
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
df[['年龄', '历史还款次数']] = scaler.fit_transform(df[['年龄', '历史还款次数']])
3.3.3 Robust 缩放
使用中位数和 IQR 代替均值和标准差,对异常值不敏感,适合催收数据中常含极端值的场景。
from sklearn.preprocessing import RobustScaler
scaler = RobustScaler()
df[['逾期金额', '催收次数']] = scaler.fit_transform(df[['逾期金额', '催收次数']])
3.4 本章小结
- 缺失值处理: 根据缺失率选择删除、填充或模型预测;填充后建议增加缺失指示列。
- 异常值检测: IQR 适用于偏态分布,Z-score 适用于正态分布;处理时优先截尾或单独标记,避免丢失高风险样本。
- 标准化与归一化: Z-score 标准化、Min-Max 归一化、Robust 缩放各有适用场景;必须在训练集上拟合参数,再转换验证/测试集。
- 催收场景要点: 异常值可能包含风险信号,处理前需结合业务判断;Robust 缩放对含异常值的催收数据更稳健。