3. 数据收集与准备:拒绝样本的数据结构、特征对齐、数据清洗与预处理

好,咱们进入正题。拒绝推断这个事儿,说白了就是跟「被拒绝的客户」打交道。你想想看,银行每天拒绝那么多申请,这些人的数据你总不能扔了吧?扔了,你的模型就永远只看到「好人」,看不到「坏人」长什么样。嗯,这就是拒绝推断要解决的核心问题。

但问题来了——这些被拒绝的客户,数据质量怎么样?我告诉你,非常糟糕。我在项目中遇到过好几次,拿到的拒绝样本数据,简直像被狗啃过一样。所以今天这一章,咱们就专门聊聊怎么收拾这个烂摊子。

3.1 拒绝样本的数据结构长什么样?

先看数据结构。拒绝样本和通过样本,在字段上其实差不多。但有几个关键差异,我列出来给你看:

字段类别 通过样本 拒绝样本
客户基本信息 完整(年龄、收入、职业等) 完整(申请时填的)
征信数据 完整 完整(银行能查到的)
行为数据 有(贷后表现) (根本没放款)
标签(Y值) 有(是否逾期) (这是核心问题)
拒绝原因 有(策略规则或模型分数)

看到没?拒绝样本最大的问题就是没有Y值。你不知道这个人如果放了款,到底会不会逾期。这就是为什么我们需要拒绝推断——给它「猜」一个标签出来。

核心要点:拒绝样本的数据结构 = 通过样本的数据结构 - 行为数据 - Y值 + 拒绝原因。

3.2 特征对齐:别让数据「对不上」

特征对齐,说白了就是确保拒绝样本和通过样本的字段是「同一个东西」。我见过最坑的一次——某家银行,通过样本里的「收入」字段是月收入,拒绝样本里的「收入」字段是年收入。你想想看,这要是直接合并建模,模型不崩才怪。

特征对齐要检查三件事:

  1. 字段名一致吗? 同一个字段,不同系统可能叫法不同。比如「年龄」在A系统叫age,在B系统叫AGE。统一。
  2. 单位一致吗? 上面说的收入问题。还有金额字段,有的用元,有的用万元。统一。
  3. 缺失值逻辑一致吗? 通过样本里缺失值可能是「未填写」,拒绝样本里缺失值可能是「系统未采集」。处理方式不同。

我的习惯:在做特征对齐之前,先拉一张字段映射表。把两个数据源的字段名、类型、单位、缺失率全部列出来。一目了然。

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 知识体系总览

说了这么多,我画了一张图帮你理清思路。拒绝样本的数据准备,其实就三步:对齐、清洗、预处理。每一步都有坑,但踩过去就好了。

拒绝样本数据准备流程 1. 数据结构分析 2. 特征对齐 3. 数据清洗 字段完整性检查 Y值缺失确认 拒绝原因字段提取 字段名统一 单位/量纲统一 缺失值逻辑对齐 缺失值处理 异常值检测与处理 重复值去重 4. 预处理(与通过样本一起) 标准化/归一化 分箱处理 特征衍生

一句话总结:拒绝样本的数据准备,核心就四个字——保持一致。和通过样本保持一致,别让数据「对不上」。数据对上了,后面的拒绝推断才能站得住脚。

专注资料整理