4. 数据探索与可视化:缺失值分析、异常值检测、特征分布直方图、相关性热力图
在构建拒贷原因解释模型之前,必须对原始信贷数据进行全面的探索性数据分析(EDA)。本章聚焦于四个核心诊断环节:缺失值分析、异常值检测、特征分布可视化以及相关性热力图。这些步骤直接决定后续特征工程的方向和LightGBM模型的稳定性。
4.1 缺失值分析
信贷数据中缺失值普遍存在,原因包括客户未填写、系统采集故障或字段非必填。缺失值处理不当会导致模型偏差或信息丢失。
4.1.1 缺失值概览
首先统计每个特征的缺失数量与缺失率,识别高缺失特征。
import pandas as pd
import numpy as np
# 假设 df 为原始信贷数据集
def missing_analysis(df):
missing_count = df.isnull().sum()
missing_rate = (missing_count / len(df)) * 100
missing_df = pd.DataFrame({
'缺失数量': missing_count,
'缺失率(%)': missing_rate.round(2)
})
missing_df = missing_df[missing_df['缺失数量'] > 0].sort_values('缺失率(%)', ascending=False)
return missing_df
missing_result = missing_analysis(df)
print(missing_result.head(10))
4.1.2 缺失值处理策略
| 缺失率范围 | 推荐策略 | 适用场景 |
|---|---|---|
| < 5% | 均值/中位数填充(连续特征)或众数填充(类别特征) | 缺失随机,不影响分布 |
| 5% – 30% | 模型预测填充(如KNN、LightGBM回归)或单独作为“缺失”类别 | 缺失可能与违约风险相关 |
| > 30% | 删除特征或构造“是否缺失”二值特征后保留 | 缺失过多,原始信息不可靠 |
信贷实战提示: 在拒贷场景中,“收入”字段缺失往往比“收入=0”更具信息量。建议对缺失率在5%-30%的特征额外生成一个“缺失标志”二值特征,让LightGBM自动学习缺失模式与违约的关系。
4.2 异常值检测
异常值会拉偏特征分布,影响LightGBM的分裂点选择和模型鲁棒性。信贷数据中常见的异常包括:收入极端高/低、负债率超过100%、年龄为0或大于100等。
4.2.1 基于IQR的检测方法
def detect_outliers_iqr(df, column):
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
return outliers, lower_bound, upper_bound
# 示例:检测“年收入”字段异常值
outliers_income, low, high = detect_outliers_iqr(df, 'annual_income')
print(f"年收入异常值数量: {len(outliers_income)},正常范围: [{low:.2f}, {high:.2f}]")
4.2.2 异常值处理原则
- 业务规则优先: 年龄 < 18 或 > 80 直接标记为异常;负债率 > 1.5 视为极端风险。
- 截尾处理(Winsorization): 将超出1.5倍IQR的值替换为边界值,保留样本量。
- 分箱处理: 对连续特征做分箱(如收入分10档),降低极端值影响。
- 单独建模: 异常值样本量较大时,可单独训练一个异常检测模型(如孤立森林)辅助判断。
注意: 不要直接删除异常值!在信贷场景中,极端值往往对应高风险客户,删除会导致模型低估违约概率。建议先截尾,再观察模型效果。
4.3 特征分布直方图
直方图直观展示每个特征的数值分布形态,帮助识别偏态、多峰、离散程度等关键信息。LightGBM对特征尺度不敏感,但分布形态会影响分箱效率和特征重要性解释。
4.3.1 连续特征分布可视化
import matplotlib.pyplot as plt
import seaborn as sns
# 设置绘图风格
sns.set_style("whitegrid")
# 绘制年收入分布直方图
plt.figure(figsize=(10, 5))
sns.histplot(df['annual_income'], bins=50, kde=True, color='#3498db')
plt.title('年收入分布直方图', fontsize=14)
plt.xlabel('年收入 (元)')
plt.ylabel('频数')
plt.tight_layout()
plt.show()
4.3.2 分布形态与处理建议
| 分布形态 | 示例特征 | 对LightGBM的影响 | 建议操作 |
|---|---|---|---|
| 右偏(长尾) | 收入、贷款金额 | 分裂点集中在低频高值区,易过拟合 | 对数变换或分箱 |
| 左偏 | 信用评分(高分集中) | 模型对低分区分辨力不足 | 平方变换或分位数分箱 |
| 双峰/多峰 | 年龄(年轻+中年两个群体) | 模型需多次分裂才能区分 | 保留原始分布,增加交互特征 |
| 均匀分布 | 随机生成的ID类特征 | 无信息量,浪费分裂 | 删除或降维 |
关键洞察: 对于严重右偏的特征(如收入),LightGBM的直方图算法会自动将长尾部分合并到最后一个桶中,导致信息损失。建议在训练前对这类特征做对数变换(log1p),使分布更接近正态,提升模型对高收入群体的区分能力。
4.4 相关性热力图
相关性热力图用于检测特征之间的线性相关程度,帮助识别多重共线性和冗余特征。虽然LightGBM对特征共线性不敏感,但高相关特征会稀释特征重要性,并增加模型解释的复杂度。
4.4.1 计算与可视化
# 计算皮尔逊相关系数矩阵
corr_matrix = df.corr(method='pearson')
# 绘制热力图
plt.figure(figsize=(14, 12))
sns.heatmap(corr_matrix,
annot=True,
fmt=".2f",
cmap='RdBu_r',
center=0,
square=True,
linewidths=0.5,
cbar_kws={"shrink": 0.8})
plt.title('特征相关性热力图', fontsize=16)
plt.tight_layout()
plt.show()
4.4.2 相关性解读与处理
- |r| > 0.8: 强相关。建议保留与目标变量(是否违约)相关性更高的特征,删除另一个。
- 0.5 < |r| < 0.8: 中等相关。可保留,但需关注特征重要性排序是否被稀释。
- |r| < 0.3: 弱相关或无关。保留所有特征,LightGBM会自动忽略无用特征。
- 与目标变量相关性极低(|r| < 0.05): 考虑删除,减少噪声。
实战技巧: 在拒贷原因解释中,我们更关注特征与“违约”标签的相关性,而非特征之间的相关性。建议在热力图旁额外输出一列“与目标变量的相关系数”,快速定位强预测特征。
4.5 本章小结:EDA与LightGBM的衔接
完成缺失值分析、异常值检测、分布直方图和相关性热力图后,应形成以下决策清单:
- 缺失特征: 确定填充策略或生成缺失标志特征。
- 异常值: 截尾或分箱,保留极端风险样本。
- 分布偏态: 对数变换或分位数分箱,提升分桶效率。
- 高相关特征: 删除冗余特征,保留与目标强相关的特征。
这些预处理结果将直接输入到第5章的特征工程环节,为LightGBM提供干净、信息密集的训练数据。