2、数据获取与初步探索
这一章咱们来动手拿数据。我用的是 Kaggle 上的 Give Me Some Credit 数据集,英文全称是 "Give Me Some Credit, We Can Pay You Back?"。说白了,它就是信用评分领域的经典入门数据,包含 15 万条贷款申请人记录,目标变量是 SeriousDlqin2yrs——表示借款人在 2 年内是否发生过严重逾期。
2.1 下载与加载
去 Kaggle 官网搜 "Give Me Some Credit",下载 cs-training.csv(训练集)和 cs-test.csv(测试集)。我个人习惯把数据放在项目目录的 data/ 文件夹下,这样后面路径管理方便。
import pandas as pd
train = pd.read_csv('data/cs-training.csv')
test = pd.read_csv('data/cs-test.csv')
print('训练集形状:', train.shape)
print('测试集形状:', test.shape)
跑出来训练集 (150000, 12),测试集 (101503, 12)。嗯,12 列包括:Unnamed: 0(索引,可删)、目标变量 SeriousDlqin2yrs,以及 10 个特征。
train.drop('Unnamed: 0', axis=1, inplace=True),否则后面计算特征相关性时会多出一个无意义列。
2.2 数据类型与基本统计
先看列的类型分布:
train.info()
我截一部分输出给大家看:
| 列名 | Non-Null Count | Dtype |
|---|---|---|
| SeriousDlqin2yrs | 150000 | int64 |
| RevolvingUtilizationOfUnsecuredLines | 150000 | float64 |
| age | 150000 | int64 |
| NumberOfTime30-59DaysPastDueNotWorse | 150000 | int64 |
| DebtRatio | 150000 | float64 |
| MonthlyIncome | 120269 | float64 |
| NumberOfOpenCreditLinesAndLoans | 150000 | int64 |
| NumberOfTimes90DaysLate | 150000 | int64 |
| NumberRealEstateLoansOrLines | 150000 | int64 |
| NumberOfTime60-89DaysPastDueNotWorse | 150000 | int64 |
| NumberOfDependents | 146409 | float64 |
注意到 MonthlyIncome 有 27731 个缺失,NumberOfDependents 有 3591 个缺失。为什么会出现缺失?我在项目里遇到最多的情况是申请人没填收入,或者系统录入时漏掉了。缺失值处理咱们后面专门讲,这里先留个印象。
2.3 初步分布探索
看一看目标变量 SeriousDlqin2yrs 的分布:
train['SeriousDlqin2yrs'].value_counts(normalize=True)
输出大概是:
- 0(未逾期):93.3%
- 1(严重逾期):6.7%
典型的类别不平衡问题。我记得上次做风控模型时,正样本只有 5%,如果直接训练,模型会倾向于全部预测为 0,准确率高但毫无意义。所以后面特征工程和采样都要格外小心。
再看几个连续特征的统计:
train.describe()
挑几个关键列说说:
| 特征 | 均值 | 标准差 | 最小值 | 25% | 50% | 75% | 最大值 |
|---|---|---|---|---|---|---|---|
| RevolvingUtilizationOfUnsecuredLines | 6.05 | 249.76 | 0.0 | 0.03 | 0.15 | 0.56 | 50708.0 |
| age | 52.30 | 14.77 | 0 | 41 | 52 | 63 | 109 |
| DebtRatio | 353.00 | 2037.00 | 0.0 | 0.18 | 0.37 | 0.87 | 329664.0 |
| MonthlyIncome | 6470.00 | 14782.00 | 0.0 | 3400 | 5400 | 8200 | 3008750.0 |
| NumberOfOpenCreditLinesAndLoans | 8.45 | 5.15 | 0 | 5 | 8 | 11 | 58 |
RevolvingUtilizationOfUnsecuredLines 最大值 50708.0,明显是异常值——按理说这个比率应该在 0~1 之间。为什么会这样?可能是数据录入错误,或者用户信息被篡改。这就是我们做特征工程要处理的脏数据。还有 age 最小值为 0,明显不合理,应该是缺失值被错误填充了。
train.hist(bins=50, figsize=(12,10)),你会看到几个长尾特别重的变量。
2.4 探索流程总览
我把整个初步探索的流程画成一张图,方便大家对照着走:
这张图就是我做初步探索的标准工作流。先读数据,接着看基本统计,然后重点关注缺失值和异常值。图里我把「缺失值分析」作为核心分叉点——往左做分布统计,往右做异常值检测,最后汇总。当然,实际项目里你可能会来回迭代,但整体逻辑不变。
2.5 两个容易踩的坑
第一次,直接用
train.describe() 里的均值填充缺失值,结果模型在收入特征上完全失效——因为收入分布是右偏的,均值被高收入样本拉高,导致低收入群体被错误归为“正常”。第二次,没注意到
NumberOfTime30-59DaysPastDueNotWorse 这个特征的名字,以为是“30-59天逾期次数”,实际它的含义是“从未超过60天的好记录”,理解反了,导致特征工程方向全错。记住:看列名时一定要读 Kaggle 的变量说明,别凭感觉。
好了,到此我们完成了数据获取和初步探索。掌握了数据的基本样貌——类型、缺失、分布、异常点——下一步才能有针对性地做特征清洗和构造。我个人喜欢把这一步叫做“对数据做一次完整的体检”,体检报告越详细,后面的手术就越精准。