3. 数据清洗基础:缺失值处理与异常值检测

数据清洗,说白了就是给原始数据「洗澡」。我见过太多人一上来就调模型,结果数据里全是坑,跑出来的结果自己都不敢信。今天咱们聊聊两个最基础也最头疼的问题:缺失值和异常值。

核心观点:数据清洗不是机械操作,而是基于业务理解的技术决策。你处理缺失值和异常值的方式,直接决定了模型的上限。

3.1 缺失值处理策略

信用评分模型里,缺失值太常见了。比如客户的工作年限、收入水平,总有人不愿意填或者填不全。我个人习惯,拿到数据第一件事就是看缺失率分布。

3.1.1 删除策略

什么时候该删?我总结了三类场景:

  • 缺失率过高:某个字段缺失超过70%,我一般直接扔掉。你想想看,一个变量70%都是空的,它能提供什么信息?
  • 随机缺失且样本充足:如果缺失是随机的,而且样本量够大,删掉几行影响不大。
  • 目标变量缺失:这个没得商量,必须删。目标值都没有,你拿什么训练?

注意:千万不要盲目删除!我曾经在一个项目中,删掉了缺失收入的样本,结果发现这些样本恰恰是高逾期风险人群。删完之后模型反而变差了。嗯,这里要注意,删除前一定要分析缺失模式。

3.1.2 填充策略

填充是更常用的方法。我常用的填充方式有这些:

填充方法 适用场景 我的经验
均值/中位数填充 连续变量,分布较对称 收入字段我常用中位数,避免被极端值带偏
众数填充 分类变量 比如学历,填「本科」往往最安全
前向/后向填充 时间序列数据 信用卡消费记录里很常见
固定值填充 有业务含义的缺失 比如收入缺失填-1,表示「未知」

举个例子,假设我们有客户的月收入数据:

# 中位数填充示例
import pandas as pd
import numpy as np

# 模拟数据
data = {'income': [5000, 6000, np.nan, 8000, 5500, np.nan, 7000]}
df = pd.DataFrame(data)

# 计算中位数
median_income = df['income'].median()
print(f'收入中位数: {median_income}')

# 填充
df['income_filled'] = df['income'].fillna(median_income)
print(df)

3.1.3 模型预测填充

这个方法听起来高大上,其实原理很简单:用其他字段预测缺失值。我一般在缺失率10%-30%之间用这个方法。

具体做法:

  1. 把数据分成两部分:有缺失值的和无缺失值的
  2. 用无缺失值的数据训练一个模型(比如随机森林)
  3. 用训练好的模型预测缺失值

小技巧:模型预测填充虽然精度高,但计算量大。我建议只在关键字段上用,比如收入、年龄这种对信用评分影响大的变量。其他字段用简单填充就够了。

3.2 异常值检测与处理

异常值,说白了就是「不合群」的数据。信用评分里,异常值可能是欺诈行为,也可能是录入错误。怎么区分?得靠业务规则和技术方法结合。

3.2.1 IQR方法

IQR(四分位距法)是我最常用的方法,因为它不受极端值影响。原理很简单:

  • 计算Q1(25%分位数)和Q3(75%分位数)
  • IQR = Q3 - Q1
  • 正常范围:[Q1 - 1.5*IQR, Q3 + 1.5*IQR]
  • 超出这个范围的就是异常值
# IQR异常值检测
def detect_outliers_iqr(data, column):
    Q1 = data[column].quantile(0.25)
    Q3 = data[column].quantile(0.75)
    IQR = Q3 - Q1
    
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    
    outliers = data[(data[column] < lower_bound) | 
                    (data[column] > upper_bound)]
    return outliers

# 示例:检测年龄异常值
outliers_age = detect_outliers_iqr(df, 'age')
print(f'检测到 {len(outliers_age)} 个异常年龄')

避坑指南:我曾经在信贷数据里用IQR检测收入异常值,结果发现很多高收入客户被标记为异常。为什么?因为收入分布是右偏的,高收入人群虽然少,但他们是真实存在的。所以,IQR的1.5倍系数不是铁律,你可以根据业务调整到2倍甚至3倍。

3.2.2 Z-Score方法

Z-Score假设数据服从正态分布。它计算每个值偏离均值多少个标准差:

  • Z = (x - μ) / σ
  • 通常|Z| > 3 视为异常值
  • |Z| > 2 视为可疑值
from scipy import stats
import numpy as np

# Z-Score异常值检测
z_scores = np.abs(stats.zscore(df['income']))
threshold = 3
outliers_z = df[z_scores > threshold]
print(f'Z-Score检测到 {len(outliers_z)} 个异常值')

你想想看,Z-Score和IQR有什么区别?我个人的理解是:

  • 数据服从正态分布时,Z-Score更准确
  • 数据偏态严重时,IQR更稳健
  • 实际项目中,我两个方法都会用,然后取交集

3.2.3 业务规则

这个才是最重要的。技术方法只是辅助,业务规则才是根本。举个例子:

字段 业务规则 说明
年龄 18-100岁 低于18或高于100,大概率是录入错误
月收入 0-100万 超过100万需要核实,可能是企业主
信用卡额度 0-100万 超过100万需要特殊审批
逾期天数 0-999 超过999天,可能是数据错误

重要提醒:业务规则一定要和业务方确认。我曾经遇到过,一个客户的年龄显示150岁,我以为是录入错误准备删掉。结果业务方告诉我,这是系统默认值,表示「未知」。你看,没有业务理解,技术再强也会犯错。

3.3 本章知识体系

为了让你更直观地理解数据清洗的流程,我画了一张图:

数据清洗核心流程 原始数据 数据质量检查 缺失值处理 • 删除(缺失率>70%) • 填充(均值/中位数/众数) • 模型预测填充 异常值检测 • IQR方法(稳健) • Z-Score方法(正态) • 业务规则(最重要) 清洗后数据

这张图展示了数据清洗的核心逻辑:先检查数据质量,然后分两条线处理缺失值和异常值,最后汇聚成干净的数据。我在实际项目中,这个流程至少要走3-5轮才能放心。

最后说一句:数据清洗没有标准答案。同样的数据,不同业务场景处理方式完全不同。我的建议是:多问为什么,多和业务方沟通,多尝试不同的方法。数据清洗花的时间,会在模型效果上十倍百倍地回报你。

专注资料整理