4. 数据预处理基础:缺失值处理、异常值检测、重复数据处理、数据类型统一

在贷后预警坏样本分析中,原始数据通常来自多个业务系统(催收系统、账务系统、客户信息系统),存在大量脏数据。数据预处理是建模前的关键步骤,直接影响后续特征工程和模型效果。本章聚焦四个核心预处理任务:缺失值处理、异常值检测、重复数据处理和数据类型统一。

业务提示:贷后数据中,逾期天数、催收次数、客户收入等字段常出现缺失或异常。预处理阶段需结合业务逻辑判断,避免机械删除或填充导致信息失真。

4.1 缺失值处理

缺失值在贷后数据中普遍存在,原因包括:客户未提供信息、系统采集失败、数据迁移丢失等。处理策略分为删除、填充和模型预测三类。

策略 适用场景 贷后案例 注意事项
删除缺失行 缺失比例 < 5%,且随机缺失 客户手机号缺失(极少) 删除后样本量仍满足建模需求
删除缺失列 缺失比例 > 70%,或业务意义弱 客户配偶信息缺失率 85% 需确认该字段非关键特征
均值/中位数填充 数值型,分布对称或偏态 客户收入(中位数填充) 偏态分布用中位数更稳健
众数填充 分类型,缺失比例低 客户职业类型 避免引入新类别
前向/后向填充 时间序列数据 逾期天数按时间顺序填充 需确保数据有序
模型预测填充 缺失字段与其他字段相关性高 用收入、年龄预测信用评分缺失值 计算成本高,避免过拟合
# Python 示例:缺失值处理
import pandas as pd
import numpy as np

# 加载贷后数据
df = pd.read_csv('post_loan_data.csv')

# 查看缺失率
missing_rate = df.isnull().mean().sort_values(ascending=False)
print("缺失率前5字段:\n", missing_rate.head())

# 删除缺失率 > 70% 的列
df.drop(columns=['spouse_info'], inplace=True)

# 中位数填充收入字段
df['income'].fillna(df['income'].median(), inplace=True)

# 众数填充职业字段
df['occupation'].fillna(df['occupation'].mode()[0], inplace=True)

# 前向填充逾期天数(按客户ID分组后按时间排序)
df.sort_values(['customer_id', 'date'], inplace=True)
df['overdue_days'].fillna(method='ffill', inplace=True)
缺失值处理决策流程 原始数据缺失检测 缺失率 > 70%? 删除该列 数值型? (是/否) 中位数/均值填充 众数填充

4.2 异常值检测

贷后数据中的异常值可能源于:录入错误、极端逾期行为、系统故障等。异常值会扭曲统计分布和模型参数,需结合业务规则和统计方法识别。

常用检测方法

  • 业务规则法:逾期天数 > 365、年龄 > 120 或 < 18、催收次数为负数等,直接判定为异常。
  • Z-Score 法:适用于近似正态分布,|Z| > 3 视为异常。
  • IQR 四分位距法:适用于偏态分布,超出 Q1-1.5×IQR 或 Q3+1.5×IQR 为异常。
  • 孤立森林:适用于高维数据,无分布假设。
# Python 示例:异常值检测
from scipy import stats
import numpy as np

# Z-Score 法检测逾期天数异常
z_scores = np.abs(stats.zscore(df['overdue_days']))
outliers_z = df[z_scores > 3]
print(f"Z-Score 检测异常数:{len(outliers_z)}")

# IQR 法检测收入异常
Q1 = df['income'].quantile(0.25)
Q3 = df['income'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers_iqr = df[(df['income'] < lower_bound) | (df['income'] > upper_bound)]
print(f"IQR 检测异常数:{len(outliers_iqr)}")

# 异常值处理:对极端值进行截尾(Winsorize)
df['income_winsorized'] = df['income'].clip(lower=lower_bound, upper=upper_bound)
风险提示:贷后场景中,部分“异常值”可能是真实的高风险样本(如逾期 300 天以上)。建议先标记异常,由业务专家复核,而非直接删除或替换。

4.3 重复数据处理

重复数据通常由数据合并、系统重复录入或接口重试导致。贷后分析中,重复记录会高估样本量、扭曲统计指标。

重复类型 定义 贷后示例 处理方式
完全重复 所有字段完全相同 催收记录重复导入 保留第一条,删除其余
部分重复 关键字段相同,其他字段有差异 同一客户多条逾期记录,日期不同 按业务规则合并或保留最新
近似重复 字段相似但不完全相同 客户姓名、身份证号略有差异 使用模糊匹配(如 Levenshtein 距离)
# Python 示例:重复数据处理
# 完全重复删除
df.drop_duplicates(inplace=True)

# 按客户ID和日期去重,保留最新记录
df.sort_values('date', ascending=False, inplace=True)
df.drop_duplicates(subset=['customer_id', 'loan_id'], keep='first', inplace=True)

# 近似重复检测(使用 recordlinkage 库)
import recordlinkage
indexer = recordlinkage.Index()
indexer.block('customer_id')  # 按客户ID分块
pairs = indexer.index(df)

compare = recordlinkage.Compare()
compare.string('name', 'name', method='jarowinkler', label='name_score')
compare.exact('id_card', 'id_card', label='id_card_match')
features = compare.compute(pairs, df)

# 筛选高相似度记录
potential_duplicates = features[features['name_score'] > 0.9]

4.4 数据类型统一

数据类型不一致会导致计算错误、内存浪费和模型报错。贷后数据常见问题包括:数值型存为字符串、日期格式不统一、分类变量编码混乱。

统一步骤

  1. 数值型字段:将字符串转为 float/int,处理千分位逗号、货币符号。
  2. 日期字段:统一为 datetime 类型,指定格式(如 %Y-%m-%d)。
  3. 分类字段:转为 category 类型,降低内存占用。
  4. 布尔字段:统一为 bool 类型,处理“是/否”、“1/0”等映射。
# Python 示例:数据类型统一
# 查看当前数据类型
print(df.dtypes)

# 数值型转换:去除逗号和货币符号
df['loan_amount'] = df['loan_amount'].str.replace(',', '').str.replace('¥', '').astype(float)

# 日期统一
df['overdue_date'] = pd.to_datetime(df['overdue_date'], format='%Y-%m-%d', errors='coerce')

# 分类变量优化
df['customer_grade'] = df['customer_grade'].astype('category')

# 布尔映射
df['is_default'] = df['is_default'].map({'是': True, '否': False, '1': True, '0': False}).astype(bool)

# 验证转换结果
print("转换后数据类型:\n", df.dtypes)
print("内存占用减少:", df.memory_usage(deep=True).sum() / 1024**2, "MB")
数据预处理四步流程 缺失值处理 删除/填充/预测 异常值检测 Z-Score/IQR/规则 重复数据处理 完全/部分/近似 数据类型统一 数值/日期/分类 预处理顺序建议 ① 数据类型统一 → ② 缺失值处理 → ③ 异常值检测 → ④ 重复数据处理 先统一类型可避免后续步骤因格式错误导致误判
最佳实践:贷后预警项目中,建议将预处理逻辑封装为可复用的 Pipeline,每次新数据接入时自动执行。同时保留原始数据副本,便于回溯和审计。