3. 数据探索与预处理:加载信贷数据集、缺失值处理、异常值检测与处理、数据标准化

数据预处理是风控模型开发中最耗时但最关键的一环。原始信贷数据通常包含大量缺失值、异常分布和量纲差异,直接建模会导致模型偏差或过拟合。本章将系统讲解从数据加载到标准化处理的完整流程,确保后续可解释评分接口的输入数据质量可靠。

3.1 加载信贷数据集

我们使用公开的信贷违约数据集(如 Home Credit Default Risk 或 LendingClub 子集),包含申请人的基本信息、信用历史、负债情况等字段。加载时需注意数据类型推断和内存优化。

关键字段说明: 数据集包含数值型(收入、年龄、贷款金额)、类别型(职业、教育水平、居住状态)和时间型(申请日期、最近逾期日期)三类特征。
import pandas as pd
import numpy as np

# 加载信贷数据集
df = pd.read_csv('credit_data.csv', parse_dates=['application_date'])

# 查看数据概览
print(f"数据集形状: {df.shape}")
print(f"特征列: {list(df.columns)}")
print(f"目标变量分布:\n{df['is_default'].value_counts(normalize=True)}")
字段名 类型 含义 缺失率
AMT_INCOME float 申请人年收入 2.3%
DAYS_BIRTH int 年龄(负值天数) 0.0%
EXT_SOURCE_1 float 外部信用评分1 35.7%
OCCUPATION_TYPE object 职业类型 12.1%
is_default int 是否违约(目标变量) 0.0%

3.2 缺失值处理

信贷数据中缺失值普遍存在,处理策略需结合业务含义和缺失率。高缺失率特征(>50%)通常直接删除;中等缺失率特征(10%-50%)根据业务逻辑填充;低缺失率特征(<10%)可用统计量填充。

业务经验: 外部信用评分(EXT_SOURCE)缺失往往意味着申请人信用记录不足,这类缺失本身具有信息量,建议用 -999 或中位数填充并保留缺失指示变量。
# 计算缺失率
missing_rate = df.isnull().mean().sort_values(ascending=False)

# 策略1:删除高缺失率特征(>50%)
high_missing_cols = missing_rate[missing_rate > 0.5].index
df.drop(columns=high_missing_cols, inplace=True)

# 策略2:数值型特征用中位数填充
num_cols = df.select_dtypes(include=['float64', 'int64']).columns
for col in num_cols:
    if df[col].isnull().sum() > 0:
        df[col].fillna(df[col].median(), inplace=True)

# 策略3:类别型特征用众数填充,并添加缺失指示列
cat_cols = df.select_dtypes(include=['object']).columns
for col in cat_cols:
    if df[col].isnull().sum() > 0:
        df[f'{col}_missing'] = df[col].isnull().astype(int)
        df[col].fillna(df[col].mode()[0], inplace=True)
缺失值处理决策流程 计算各特征缺失率 缺失率 > 50% 10% ≤ 缺失率 ≤ 50% 缺失率 < 10% 直接删除该特征 (信息损失最小) 业务逻辑填充 + 缺失指示变量 中位数/众数填充 (简单高效) 完整无缺失数据集

3.3 异常值检测与处理

信贷数据中的异常值可能来自数据录入错误、欺诈行为或极端信用状况。常用检测方法包括 IQR 规则、Z-Score 和业务规则过滤。

注意: 风控场景中,部分“异常值”实际上是高风险客户的真实特征(如极高负债率),直接删除可能导致模型对极端风险样本的识别能力下降。建议先标记再评估。
def detect_outliers_iqr(df, column, multiplier=1.5):
    """使用IQR规则检测异常值"""
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - multiplier * IQR
    upper_bound = Q3 + multiplier * 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, 'AMT_INCOME')
print(f"收入异常值数量: {len(outliers_income)} ({len(outliers_income)/len(df)*100:.2f}%)")

# 处理策略:对极端值进行截断(Winsorize)
df['AMT_INCOME'] = df['AMT_INCOME'].clip(lower=low, upper=high)
检测方法 适用场景 阈值 处理方式
IQR 规则 偏态分布特征(收入、负债) 1.5×IQR / 3×IQR 截断或删除
Z-Score 近似正态分布特征(年龄、信用分) |Z| > 3 删除或替换
业务规则 有明确业务约束的特征 年龄 < 18 或 > 100 直接修正或删除
孤立森林 高维特征异常检测 异常分数 > 0.7 标记后人工审核

3.4 数据标准化

风控模型(尤其是逻辑回归、SVM、KNN)对特征量纲敏感。标准化可消除量纲影响,使不同特征具有可比性,同时加速模型收敛。常用方法包括 Z-Score 标准化和 Min-Max 归一化。

选择建议: 对于存在异常值的特征,优先使用 RobustScaler(基于中位数和 IQR);对于稀疏特征,使用 MaxAbsScaler;对于深度学习模型,推荐 BatchNormalization。
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler

# 选择数值型特征进行标准化
num_features = ['AMT_INCOME', 'DAYS_BIRTH', 'EXT_SOURCE_1', 'CNT_FAM_MEMBERS']

# 方法1:Z-Score 标准化(均值为0,标准差为1)
scaler_standard = StandardScaler()
df[num_features] = scaler_standard.fit_transform(df[num_features])

# 方法2:Min-Max 归一化(缩放到[0,1]区间)
scaler_minmax = MinMaxScaler()
df[num_features] = scaler_minmax.fit_transform(df[num_features])

# 方法3:RobustScaler(对异常值鲁棒)
scaler_robust = RobustScaler()
df[num_features] = scaler_robust.fit_transform(df[num_features])
三种标准化方法对比 原始特征数据 StandardScaler (x - μ) / σ MinMaxScaler (x - min) / (max - min) RobustScaler (x - median) / IQR 均值为0,标准差为1 缩放到[0, 1]区间 对异常值鲁棒

3.5 预处理流程总结

完整的数据预处理流程应形成可复用的 Pipeline,确保训练集和测试集使用相同的变换参数。以下是推荐的执行顺序:

  1. 数据加载与类型转换 — 解析日期、类别编码、内存优化
  2. 缺失值处理 — 删除高缺失率特征,填充中位数/众数,添加缺失指示变量
  3. 异常值检测与处理 — IQR/Z-Score 检测,截断或标记
  4. 特征编码 — 类别特征 One-Hot 或 Label Encoding
  5. 数据标准化 — 根据特征分布选择 StandardScaler 或 RobustScaler
  6. 训练/测试集划分 — 保持时间顺序或随机分层抽样
工程实践: 使用 sklearn 的 Pipeline 和 ColumnTransformer 将上述步骤封装为可持久化的预处理对象,便于在评分接口中复用相同的变换逻辑,避免训练-服务不一致问题。
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder

# 定义数值型特征处理管道
num_pipeline = Pipeline([
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

# 定义类别型特征处理管道
cat_pipeline = Pipeline([
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('onehot', OneHotEncoder(handle_unknown='ignore'))
])

# 组合预处理
preprocessor = ColumnTransformer([
    ('num', num_pipeline, num_features),
    ('cat', cat_pipeline, cat_features)
])

# 拟合并转换
X_processed = preprocessor.fit_transform(X_train)