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的衔接

完成缺失值分析、异常值检测、分布直方图和相关性热力图后,应形成以下决策清单:

  1. 缺失特征: 确定填充策略或生成缺失标志特征。
  2. 异常值: 截尾或分箱,保留极端风险样本。
  3. 分布偏态: 对数变换或分位数分箱,提升分桶效率。
  4. 高相关特征: 删除冗余特征,保留与目标强相关的特征。

这些预处理结果将直接输入到第5章的特征工程环节,为LightGBM提供干净、信息密集的训练数据。

数据探索与可视化流程(第4章) 原始信贷数据集 缺失值分析 缺失率 & 填充策略 异常值检测 IQR / 截尾 / 分箱 特征分布直方图 偏态 / 多峰 / 对数变换 相关性热力图 共线性 / 冗余特征 EDA 决策清单 特征工程(第5章) 核心原则 缺失不随意删 · 异常不直接弃 · 分布要观察 · 冗余要剔除