第三章 数据探索与采集:打好评分卡的地基
说实话,做评分卡这么多年,我最大的体会就是——数据质量决定了模型的天花板。算法再牛,特征工程再花哨,数据本身是脏的,那一切都是白搭。这一章,咱们就聊聊数据探索和采集那些事儿。
3.1 内部数据与外部数据源
做申请评分卡,数据从哪来?无非两条路:内部和外部。
内部数据,说白了就是你自己系统里攒的那些家底。比如客户填的申请表信息、历史借贷记录、还款行为、甚至客服通话记录。我个人习惯,先把手头内部数据理清楚,再考虑外采。为什么?因为内部数据最可控,成本也最低。
我在项目中遇到过一家消金公司,他们内部数据其实挺全的,但没人系统整理过。客户近半年的还款逾期次数、最近一次申请时间、历史最大负债金额……这些字段散落在三个不同的库里。后来我们花了两周时间做数据血缘梳理,才把这些金子挖出来。
外部数据源,就是第三方提供的补充信息。常见的有:
- 征信数据:央行征信、百行征信、朴道征信等
- 黑名单库:法院失信被执行人、网贷黑名单
- 多头借贷数据:同盾、百融等平台提供的多头查询记录
- 设备指纹:手机IMEI、MAC地址、IP归属地等
- 社交网络数据:通讯录关系、通话频次等(注意合规)
核心原则:外部数据是锦上添花,不是雪中送炭。别指望靠买几个外部变量就能把模型从0.6 AUC拉到0.8。先把内部数据吃透,再考虑外采。
你想想看,外部数据有成本,而且接口调用还有延迟。如果内部数据已经能区分出好坏客户了,何必多花那个钱?
3.2 数据字典与元数据管理
数据字典这东西,听起来很枯燥,但做评分卡的人要是没有它,就像厨师没有菜谱——全凭感觉炒菜。
什么是数据字典?就是一张表,记录了每个字段的名字、类型、含义、取值范围、来源、更新频率等信息。我见过最糟糕的情况:一个字段叫"flag_01",没人知道它代表什么。后来查了三个月前的邮件才知道,原来是"是否通过人脸识别"的标记。
嗯,这里要注意——元数据管理不只是写个文档就完事了。它需要持续维护。我建议用以下格式来管理:
| 字段名 | 数据类型 | 含义 | 取值范围 | 来源 | 更新频率 | 备注 |
|---|---|---|---|---|---|---|
| cust_id | string | 客户唯一标识 | UUID格式 | 核心系统 | 实时 | 主键 |
| apply_date | date | 申请日期 | 2020-01-01至今 | 前端录入 | 实时 | 注意时区问题 |
| loan_amount | float | 申请金额(元) | 1000-500000 | 前端录入 | 实时 | 单位统一为元 |
| overdue_days | int | 历史最大逾期天数 | 0-365 | 贷后系统 | T+1 | -1表示无历史记录 |
我曾经因为一个字段的取值含义没搞清楚,导致模型上线后效果暴跌。那个字段叫"education_level",取值是1、2、3、4。我以为1是小学,2是初中,3是高中,4是大学。结果后来发现——1是博士,2是硕士,3是本科,4是其他。你想想看,这要是搞反了,模型能不出问题吗?
小技巧:每次拿到新数据,先花半天时间做字段级的数据字典梳理。别急着跑模型。磨刀不误砍柴工。
3.3 数据质量初探:缺失值与异常值
数据质量,说白了就是数据干不干净。做评分卡最怕两件事:缺失太多、异常太多。
3.3.1 缺失值处理
缺失值,就是字段里空着的那部分。为什么会缺失?原因很多:
- 客户没填(比如收入信息很多人不愿意填)
- 系统没采集到(比如某个接口挂了)
- 数据本身就不存在(比如未婚人士的配偶信息)
我个人习惯,先看缺失率。如果某个字段缺失率超过70%,我基本就不考虑了。为什么?因为缺失太多,你很难推断出真实分布,强行填充反而会引入噪声。
常见的缺失值处理方法:
- 删除:缺失率极高(>70%)或缺失模式完全随机
- 填充均值/中位数:适用于连续变量,且缺失率较低(<20%)
- 填充众数:适用于分类变量
- 模型预测填充:用其他字段预测缺失值,适合缺失率中等(20%-50%)
- 单独作为一类:把"缺失"本身当作一个取值,比如"收入未知"
避坑指南:我曾经在某个项目里,把所有缺失值都填充了0。结果模型训练出来,发现"收入=0"的客户反而违约率最低。后来一查,原来这些客户是系统没采集到数据,并不是真的收入为0。嗯,这个教训让我记住了——填充之前,先搞清楚缺失的原因。
3.3.2 异常值检测
异常值,就是那些明显不合理的数据。比如年龄填了200岁,月收入填了9999999元,或者申请金额是负数。
检测异常值,我常用的方法:
- 业务规则:年龄0-120岁,收入0-100万(根据产品类型调整)
- 统计方法:3σ原则、箱线图(IQR方法)
- 聚类方法:DBSCAN等密度聚类,把离群点揪出来
你想想看,一个客户填的月收入是100万,但申请金额只有5000块。这合理吗?大概率是填错了。我建议的做法是:先标记异常值,然后根据业务逻辑判断是修正还是删除。
下面是一个简单的异常值检测代码示例:
import pandas as pd
import numpy as np
def detect_outliers_iqr(data, column):
"""
使用IQR方法检测异常值
"""
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
return outliers
# 示例使用
df = pd.read_csv('application_data.csv')
age_outliers = detect_outliers_iqr(df, 'age')
print(f"年龄字段异常值数量:{len(age_outliers)}")
提示:异常值不一定要删除。有时候异常值反而是重要的信号。比如某个人年龄填了18岁但收入填了50万,这可能是欺诈信号。我建议先标记,再分析,最后决定处理方式。
3.4 本章知识体系总览
说了这么多,咱们用一张图来总结一下本章的核心逻辑:
这张图把本章的核心内容串起来了。数据源是输入,数据字典是地图,数据质量是检查站。三者缺一不可。
最后说一句:数据探索这件事,没有捷径。你花的时间越多,后面建模就越顺。我见过太多人急着建模,结果数据问题没发现,模型上线后各种翻车。嗯,别急,慢慢来。
公众号:蓝海资料掘金营,微信deep3321