4. 数据探索性分析(EDA):描述性统计、缺失值分布、异常值初步识别
在构建贷后预警模型之前,必须对原始数据进行系统性的探索性分析。本章将围绕描述性统计、缺失值分布和异常值初步识别三个核心任务展开,为后续特征工程与模型训练奠定数据基础。
4.1 描述性统计:快速掌握数据全貌
描述性统计帮助我们理解各字段的集中趋势、离散程度与分布形态。以下为贷后预警数据集关键字段的统计摘要:
| 字段名称 | 数据类型 | 非空计数 | 均值 | 标准差 | 最小值 | 25%分位 | 50%分位 | 75%分位 | 最大值 |
|---|---|---|---|---|---|---|---|---|---|
| 贷款金额(元) | float64 | 100,000 | 85,432.10 | 120,345.67 | 1,000 | 20,000 | 50,000 | 100,000 | 5,000,000 |
| 逾期天数 | int64 | 100,000 | 15.23 | 32.45 | 0 | 0 | 3 | 18 | 365 |
| 客户年龄 | int64 | 99,850 | 38.56 | 10.21 | 18 | 30 | 37 | 45 | 85 |
| 近6月查询次数 | int64 | 100,000 | 8.12 | 6.78 | 0 | 3 | 6 | 11 | 50 |
| 收入负债比 | float64 | 97,200 | 0.45 | 0.28 | 0.01 | 0.22 | 0.40 | 0.62 | 2.50 |
关键发现:贷款金额与逾期天数呈现右偏分布(均值远大于中位数),说明存在少量高额贷款和长期逾期样本,需在后续建模中关注长尾效应。
4.2 缺失值分布:识别数据空洞
缺失值会直接影响模型训练质量。我们按字段统计缺失率,并分析缺失模式:
| 字段名称 | 缺失数量 | 缺失率 | 缺失类型 | 初步处理建议 |
|---|---|---|---|---|
| 客户年龄 | 150 | 0.15% | 随机缺失 | 中位数填充 |
| 收入负债比 | 2,800 | 2.80% | 非随机缺失(可能与收入低相关) | 模型预测填充或单独编码 |
| 工作单位类型 | 5,300 | 5.30% | 随机缺失 | 众数填充 + 新增“未知”类别 |
| 担保人收入 | 12,000 | 12.00% | 结构缺失(无担保人时为空) | 填充0并新增“有无担保人”标志 |
| 历史最大逾期金额 | 1,050 | 1.05% | 随机缺失 | 中位数填充 |
处理原则:缺失率低于1%的字段可直接用中位数/众数填充;缺失率在1%-5%之间的字段需分析缺失机制;缺失率超过5%的字段建议构造“是否缺失”标志变量,再结合业务逻辑填充。
4.3 异常值初步识别:箱线图与业务规则
异常值会扭曲模型学习方向,需结合统计方法与业务知识进行初步筛查。以下为关键字段的异常值识别规则:
- 贷款金额:超过均值+3倍标准差(约446,469元)或低于1,000元视为异常。
- 逾期天数:超过365天视为数据录入错误或极端案例,需单独处理。
- 收入负债比:大于1.5视为异常(收入无法覆盖负债),需核实数据源。
- 近6月查询次数:超过30次视为频繁查询,可能为多头借贷信号,但非数据错误,保留为特征。
- 客户年龄:小于18岁或大于80岁视为异常,需剔除或修正。
注意:异常值不等于错误值。部分异常值(如高查询次数、高收入负债比)可能包含重要的风险信号,应在建模前与业务方确认是否保留或单独编码。
4.4 EDA 流程总览
以下 SVG 图展示了贷后预警数据 EDA 的完整流程,从原始数据输入到最终输出清洗后的数据集:
4.5 代码实现:EDA 核心步骤
以下代码展示了使用 Python 进行描述性统计、缺失值分析和异常值识别的标准流程:
import pandas as pd
import numpy as np
# 加载数据
df = pd.read_csv('loan_early_warning.csv')
# 1. 描述性统计
desc_stats = df.describe(percentiles=[0.25, 0.5, 0.75])
print("描述性统计结果:")
print(desc_stats)
# 2. 缺失值分布
missing_counts = df.isnull().sum()
missing_rates = (df.isnull().sum() / len(df)) * 100
missing_df = pd.DataFrame({
'缺失数量': missing_counts,
'缺失率(%)': missing_rates.round(2)
})
missing_df = missing_df[missing_df['缺失数量'] > 0].sort_values('缺失率(%)', ascending=False)
print("\n缺失值分布:")
print(missing_df)
# 3. 异常值初步识别(基于IQR方法)
def detect_outliers_iqr(series):
Q1 = series.quantile(0.25)
Q3 = series.quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = series[(series < lower_bound) | (series > upper_bound)]
return len(outliers), lower_bound, upper_bound
numeric_cols = ['贷款金额', '逾期天数', '客户年龄', '近6月查询次数', '收入负债比']
print("\n异常值初步识别(IQR方法):")
for col in numeric_cols:
count, lb, ub = detect_outliers_iqr(df[col])
print(f"{col}: 异常值数量={count}, 下界={lb:.2f}, 上界={ub:.2f}")
输出解读:描述性统计揭示了数据的分布形态与潜在偏态;缺失值分布表帮助确定填充策略;异常值识别结果需结合业务规则进行二次确认,避免误删有效风险信号。