3. 数据清洗基础:缺失值处理、重复样本去重与异常值检测

数据清洗是样本构建中最耗时但回报最高的环节。原始数据中普遍存在缺失、重复和异常三类问题,若不处理,模型会学到错误的统计规律,导致泛化能力下降。本章聚焦三类问题的检测方法与处理策略,并给出工业级实践建议。

核心原则:清洗策略应基于数据量、缺失机制、业务含义和模型鲁棒性综合选择,不存在“万能方法”。

3.1 缺失值处理

缺失值指数据集中某个或某些特征的值未被记录。根据缺失机制可分为三类:

缺失机制 定义 典型场景 处理倾向
完全随机缺失 (MCAR) 缺失与任何变量无关 传感器偶发故障、录入遗漏 可直接删除或简单填充
随机缺失 (MAR) 缺失与其他观测变量相关 高收入人群更倾向不填收入 需借助其他特征建模填充
非随机缺失 (MNAR) 缺失与缺失值本身相关 病情严重者拒绝提供健康评分 需引入外部信息或业务假设

3.1.1 删除法

  • 行删除(Listwise Deletion):直接丢弃包含缺失值的样本。适用于缺失率低(<5%)且MCAR场景。
  • 列删除:当某特征缺失率超过阈值(如80%)时,直接删除该特征。
  • 成对删除(Pairwise Deletion):在计算相关性或协方差时,仅使用两个变量均非缺失的样本。
注意:行删除会减少样本量,当缺失率较高时可能导致模型训练不足或引入选择偏差。

3.1.2 填充法

方法 适用数据类型 优点 缺点
均值/中位数填充 数值型 简单快速 降低方差,扭曲分布
众数填充 分类型 保持类别分布 可能引入偏差
前向/后向填充 时间序列 保留时序依赖 不适用于非时序数据
KNN填充 数值型/分类型 利用样本相似性 计算量大,对K值敏感
回归/模型填充 数值型 精度高,利用特征关系 可能过拟合,需谨慎验证

3.1.3 插值法

插值法利用已知数据点之间的数学关系估算缺失值,特别适用于连续型时间序列数据。

  • 线性插值:假设缺失点前后数据呈线性变化,适用于趋势平稳的数据。
  • 多项式插值:使用低阶多项式拟合局部数据,适用于非线性但平滑的序列。
  • 样条插值:分段低阶多项式拟合,兼顾平滑性与局部适应性。
  • 时间加权插值:根据时间间隔权重估算,适用于不等间隔采样。
# 线性插值示例(Python伪代码)
import pandas as pd
import numpy as np

# 创建含缺失值的时间序列
ts = pd.Series([1, np.nan, np.nan, 4, 5, np.nan, 7])

# 线性插值
ts_interpolated = ts.interpolate(method='linear')
print(ts_interpolated)
# 输出: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
实践建议:对于时间序列数据,优先尝试线性插值或前向填充;对于表格数据,KNN填充和模型填充通常效果更好,但需注意计算成本。
缺失值处理策略决策流程 检测缺失值 缺失率 < 5%? 是 行删除 / 简单填充 否 时序数据? 是 插值法 / 前向填充 否 KNN / 模型填充 验证填充效果,评估分布变化

3.2 重复样本去重

重复样本指数据集中存在两条或多条完全一致或高度相似的记录。重复样本会导致模型对某些模式过度学习,降低泛化能力,并造成训练-验证集泄露。

3.2.1 重复类型

类型 定义 检测方法
完全重复 所有特征值完全相同 直接使用 df.duplicated()
部分重复 关键字段相同,非关键字段有差异 基于主键或业务规则去重
近似重复 特征值高度相似但非完全一致 使用相似度度量(如编辑距离、Jaccard系数)

3.2.2 去重策略

  • 精确去重:保留第一条或最后一条重复记录,适用于完全重复场景。
  • 基于主键去重:根据业务主键(如用户ID、订单号)保留唯一记录。
  • 相似度去重:对文本或图像数据,设定相似度阈值,合并或删除近似重复。
  • 聚合去重:对重复样本的数值特征取均值,分类型特征取众数,生成聚合样本。
# 精确去重示例
import pandas as pd

df = pd.DataFrame({
    'user_id': [1, 1, 2, 3, 3],
    'age': [25, 25, 30, 35, 35],
    'city': ['北京', '北京', '上海', '广州', '广州']
})

# 检测重复
print(df.duplicated())
# 输出: [False, True, False, False, True]

# 去重(保留第一条)
df_unique = df.drop_duplicates()
print(df_unique)
# 输出: 保留 user_id=1,2,3 各一条
注意:在时间序列或日志数据中,看似重复的记录可能包含时间戳差异,需谨慎判断是否真正重复。

3.3 异常值检测基础

异常值(Outlier)是指显著偏离数据集中其他观测值的样本点。异常值可能由测量误差、数据录入错误或真实稀有事件引起,需结合业务判断处理方式。

3.3.1 常用检测方法

方法 原理 适用场景 参数
Z-Score 基于均值和标准差,假设数据正态分布 单变量、近似正态分布 阈值通常取 2~4
IQR(四分位距) 基于四分位数,不依赖分布假设 单变量、偏态分布 下界 Q1-1.5IQR,上界 Q3+1.5IQR
孤立森林 基于随机森林,异常点更容易被孤立 高维数据、非正态分布 树数量、采样比例
DBSCAN 基于密度聚类,低密度点视为异常 空间聚类、形状复杂数据 邻域半径、最小样本数
LOF(局部异常因子) 比较局部密度,密度远低于邻居则为异常 局部密度变化大的数据 近邻数 K

3.3.2 异常值处理策略

  • 删除:确认异常由错误引起且数量较少时直接删除。
  • 截尾(Winsorization):将异常值替换为阈值(如1%和99%分位数)。
  • 视为缺失:将异常值标记为缺失,再使用缺失值处理方法填充。
  • 保留并标记:在特征中增加“是否为异常”的指示变量,保留原始信息。
  • 分箱处理:将连续值离散化,异常值归入首尾箱。
# IQR方法检测异常值示例
import numpy as np

data = np.array([10, 12, 13, 15, 16, 18, 20, 22, 25, 100])

Q1 = np.percentile(data, 25)
Q3 = np.percentile(data, 75)
IQR = Q3 - Q1

lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

outliers = data[(data < lower_bound) | (data > upper_bound)]
print("异常值:", outliers)
# 输出: [100]
实践建议:异常值检测应结合业务知识。例如,在金融风控中,异常交易可能是欺诈信号,应保留并重点分析;在传感器数据中,异常值通常是噪声,应删除或修正。
异常值检测方法对比 Z-Score 基于均值与标准差 适用:单变量正态分布 阈值:|Z| > 2~4 局限:对偏态分布敏感 IQR 四分位距法 基于分位数,鲁棒性强 适用:偏态分布、非正态 阈值:Q1-1.5IQR / Q3+1.5IQR 局限:对尾部不敏感 孤立森林 随机森林变体 适用:高维、非正态数据 参数:树数量、采样比例 局限:对局部异常不敏感 DBSCAN 密度聚类 低密度点视为异常 适用:空间聚类、形状复杂 参数:邻域半径、最小样本数 局限:密度变化大时效果差 LOF 局部异常因子 比较局部密度 适用:局部密度变化大的数据 参数:近邻数 K 局限:计算复杂度高 业务规则 + 领域知识 结合业务逻辑判断 适用:有明确业务约束 例如:年龄 > 120 视为异常 局限:依赖专家经验

3.4 数据清洗流程总结

在实际项目中,数据清洗通常按以下步骤执行:

  1. 数据概览:统计缺失率、重复率、基本分布特征。
  2. 缺失值处理:根据缺失机制和数据类型选择删除、填充或插值。
  3. 重复样本去重:先精确去重,再根据业务规则处理近似重复。
  4. 异常值检测与处理:结合统计方法和业务知识,选择删除、截尾或标记。
  5. 清洗后验证:对比清洗前后数据分布、样本量、特征相关性,确保清洗未引入新问题。
关键提醒:数据清洗不是一次性工作。在模型迭代过程中,应持续监控数据质量,并根据模型反馈调整清洗策略。记录每次清洗的规则和参数,确保可复现、可审计。