3. 数据收集与准备:拒绝样本的数据结构、特征对齐、数据清洗与预处理
好,咱们进入正题。拒绝推断这个事儿,说白了就是跟「被拒绝的客户」打交道。你想想看,银行每天拒绝那么多申请,这些人的数据你总不能扔了吧?扔了,你的模型就永远只看到「好人」,看不到「坏人」长什么样。嗯,这就是拒绝推断要解决的核心问题。
但问题来了——这些被拒绝的客户,数据质量怎么样?我告诉你,非常糟糕。我在项目中遇到过好几次,拿到的拒绝样本数据,简直像被狗啃过一样。所以今天这一章,咱们就专门聊聊怎么收拾这个烂摊子。
3.1 拒绝样本的数据结构长什么样?
先看数据结构。拒绝样本和通过样本,在字段上其实差不多。但有几个关键差异,我列出来给你看:
| 字段类别 | 通过样本 | 拒绝样本 |
|---|---|---|
| 客户基本信息 | 完整(年龄、收入、职业等) | 完整(申请时填的) |
| 征信数据 | 完整 | 完整(银行能查到的) |
| 行为数据 | 有(贷后表现) | 无(根本没放款) |
| 标签(Y值) | 有(是否逾期) | 无(这是核心问题) |
| 拒绝原因 | 无 | 有(策略规则或模型分数) |
看到没?拒绝样本最大的问题就是没有Y值。你不知道这个人如果放了款,到底会不会逾期。这就是为什么我们需要拒绝推断——给它「猜」一个标签出来。
核心要点:拒绝样本的数据结构 = 通过样本的数据结构 - 行为数据 - Y值 + 拒绝原因。
3.2 特征对齐:别让数据「对不上」
特征对齐,说白了就是确保拒绝样本和通过样本的字段是「同一个东西」。我见过最坑的一次——某家银行,通过样本里的「收入」字段是月收入,拒绝样本里的「收入」字段是年收入。你想想看,这要是直接合并建模,模型不崩才怪。
特征对齐要检查三件事:
- 字段名一致吗? 同一个字段,不同系统可能叫法不同。比如「年龄」在A系统叫age,在B系统叫AGE。统一。
- 单位一致吗? 上面说的收入问题。还有金额字段,有的用元,有的用万元。统一。
- 缺失值逻辑一致吗? 通过样本里缺失值可能是「未填写」,拒绝样本里缺失值可能是「系统未采集」。处理方式不同。
我的习惯:在做特征对齐之前,先拉一张字段映射表。把两个数据源的字段名、类型、单位、缺失率全部列出来。一目了然。
3.3 数据清洗:拒绝样本的「脏」超乎你想象
数据清洗,每个做风控的人都懂。但拒绝样本的清洗,有几个坑你得特别注意。
3.3.1 缺失值处理
拒绝样本的缺失率通常比通过样本高。为什么?因为很多拒绝客户是「随便填填」的,信息不完整。我见过一个极端案例——某个拒绝样本里,「工作单位」字段缺失率高达60%。
处理方式:
- 高缺失率字段(>50%): 建议直接删除,或者单独做一个「是否缺失」的哑变量。
- 中缺失率字段(20%-50%): 用中位数/众数填充,或者用模型预测填充。
- 低缺失率字段(<20%): 正常填充即可。
3.3.2 异常值处理
拒绝样本里异常值特别多。你想想看,一个正常申请者,年龄不会填200岁吧?但拒绝样本里,这种离谱数据比比皆是。
我的做法:
- 对连续变量做箱线图,标记出3倍IQR以外的点。
- 对离散变量做频次统计,标记出出现次数极少的类别。
- 然后——不要直接删除。先看看这些异常值是不是有业务含义。比如年龄200岁,可能是系统录入错误,也可能是测试数据。确认后再处理。
我曾经踩过的坑:有一次做拒绝推断,我把所有异常值都删了。结果模型上线后,拒绝率飙升。后来一查,原来那些「异常值」其实是高收入人群的特殊情况。删了它们,模型就学偏了。
3.3.3 重复值处理
拒绝样本里重复申请很常见。同一个客户,被拒了一次,换个手机号再申请一次。这种数据如果不处理,相当于给同一个样本「加权」了。
处理方式:
- 按身份证号去重,保留最近一次申请记录。
- 如果没有身份证号,用姓名+手机号+出生日期组合去重。
3.4 预处理:为建模铺好路
预处理这一步,拒绝样本和通过样本要一起做。不能分开处理,否则特征分布会不一致。
3.4.1 标准化/归一化
如果你的模型对尺度敏感(比如逻辑回归、SVM),那就需要标准化。我习惯用Z-score标准化:
# Python示例
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# 注意:用通过样本拟合scaler,再transform拒绝样本
scaler.fit(approved_data)
rejected_scaled = scaler.transform(rejected_data)
为什么要用通过样本拟合?因为通过样本才是我们「信任」的数据分布。拒绝样本的分布可能有偏差,用它拟合会引入噪声。
3.4.2 分箱处理
对于评分卡模型,分箱是必须的。拒绝样本的分箱要沿用通过样本的切分点。不能重新分箱,否则两个样本的特征分布就不一致了。
# 伪代码示例
# 1. 在通过样本上计算分箱切分点
bins = calculate_bins(approved_data['age'], n_bins=5)
# 2. 用同样的切分点对拒绝样本分箱
rejected_data['age_bin'] = pd.cut(rejected_data['age'], bins=bins)
3.4.3 特征衍生
拒绝样本的特征衍生,和通过样本一样。但要注意——不要衍生出依赖Y值的特征。比如「逾期天数」这种特征,拒绝样本里根本没有。
常见的衍生方式:
- 交叉特征:收入/负债比、年龄*学历等
- 统计特征:近3个月查询次数、近6个月逾期次数等
- 时间特征:申请时间、距离上次申请的天数等
3.5 知识体系总览
说了这么多,我画了一张图帮你理清思路。拒绝样本的数据准备,其实就三步:对齐、清洗、预处理。每一步都有坑,但踩过去就好了。
一句话总结:拒绝样本的数据准备,核心就四个字——保持一致。和通过样本保持一致,别让数据「对不上」。数据对上了,后面的拒绝推断才能站得住脚。