数据清洗基础:处理缺失值、重复值、异常值,数据一致性检查

数据清洗,说白了就是给数据「洗澡」。

我见过太多人,模型跑不出来,第一反应是调参、换网络结构。其实呢?八成是数据没洗干净。脏数据喂进去,神仙模型也救不了。今天咱们就聊聊数据清洗的四个核心动作:缺失值、重复值、异常值,还有一致性检查。

一、缺失值处理:别让「空」毁了你的模型

缺失值是最常见的问题。数据采集时设备故障、人工录入遗漏、传输丢包……原因太多了。

1.1 先诊断:缺失模式

动手处理前,我习惯先看看缺失值的分布。不是所有缺失都一样。

  • 完全随机缺失(MCAR):缺失和任何变量无关。比如传感器偶尔抽风。这种最简单,直接删掉或填充都行。
  • 随机缺失(MAR):缺失和其他变量有关。比如高收入人群更不愿意填收入。这种需要小心,直接删会引入偏差。
  • 非随机缺失(MNAR):缺失和自身值有关。比如温度太高导致传感器烧坏。这种最难搞,需要领域知识介入。

我的经验:拿到数据先跑个 df.isnull().sum(),再算算缺失比例。超过 50% 的列,我一般直接扔掉——补出来的东西也不靠谱。

1.2 处理策略:删还是补?

删还是补,看场景。

方法 适用场景 代码示例
直接删除 缺失比例小(<5%),且随机 df.dropna()
均值/中位数填充 数值型,分布较集中 df.fillna(df.mean())
众数填充 分类型,比如性别 df.fillna(df.mode()[0])
前向/后向填充 时间序列,趋势明显 df.fillna(method='ffill')
插值法 有序数据,比如时间 df.interpolate()
模型预测 缺失值重要,且特征丰富 用 KNN、回归等模型预测

避坑指南:我曾经在一个客户流失预测项目里,直接用均值填充了收入字段。结果模型训练出来,高收入群体的预测准确率惨不忍睹。后来才发现,收入分布是长尾的,均值根本代表不了。改用中位数填充后,效果好了很多。

二、重复值处理:别让数据「复制粘贴」

重复数据会放大某些样本的权重,让模型产生偏见。

2.1 完全重复 vs 部分重复

  • 完全重复:所有字段都一样。直接删,没商量。
  • 部分重复:关键字段一样,其他字段有差异。比如同一个用户注册了两次,但地址不同。这种需要合并或去重。
# 检查完全重复
df.duplicated().sum()

# 删除完全重复
df.drop_duplicates()

# 按指定列去重
df.drop_duplicates(subset=['user_id'])

注意:部分重复不能简单删除。我建议先按业务主键去重,再对非关键字段做聚合(比如取最新值、取平均值)。

三、异常值处理:揪出「害群之马」

异常值可能是噪声,也可能是真正的「宝藏」。比如信用卡欺诈检测,异常值就是我们要找的目标。

3.1 怎么发现异常值?

我常用的方法有几种:

  • 3σ 原则:数据服从正态分布时,超过均值±3倍标准差的值视为异常。
  • 箱线图(IQR):超出 Q1-1.5*IQR 或 Q3+1.5*IQR 的值。这个更稳健,不受极端值影响。
  • Z-Score:标准化后的分数,|Z|>3 通常视为异常。
  • DBSCAN 聚类:密度聚类,孤立点自动标为异常。
# 箱线图法
Q1 = df['age'].quantile(0.25)
Q3 = df['age'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outliers = df[(df['age'] < lower) | (df['age'] > upper)]

3.2 处理方式

策略 说明 适用场景
删除 直接移除异常样本 异常值极少,且确认是噪声
截尾 将异常值替换为边界值 不想丢失样本,但需限制极端值影响
视为缺失 先标为 NaN,再用缺失值方法处理 异常值可能是录入错误
单独建模 异常值本身有业务意义 欺诈检测、故障诊断

我的习惯:先可视化。画个箱线图或散点图,一眼就能看出异常。别光靠统计量,眼睛有时候比公式管用。

四、数据一致性检查:别让数据「打架」

一致性检查,就是确保数据内部没有矛盾。

4.1 常见问题

  • 格式不一致:日期有 2024-01-01,也有 01/01/2024。统一格式。
  • 单位不一致:有的用米,有的用厘米。全部转成同一单位。
  • 逻辑矛盾:年龄 150 岁,或者「入职日期」早于「出生日期」。这种明显有问题。
  • 编码不一致:性别有「男」「M」「1」,需要统一映射。
# 日期统一
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')

# 逻辑检查
df = df[df['age'] < 120]
df = df[df['hire_date'] >= df['birth_date']]

# 编码映射
gender_map = {'男': 0, 'M': 0, '1': 0, '女': 1, 'F': 1, '2': 1}
df['gender'] = df['gender'].map(gender_map)

我曾经踩过的坑:一个电商项目,订单金额有的含税有的不含税,但字段名一样。我一开始没发现,模型训练出来预测偏差很大。后来逐条核对业务逻辑才找到原因。所以,一致性检查一定要结合业务规则来做。

五、知识体系总览

下面这张图,概括了数据清洗的完整流程。你可以把它当作检查清单。

数据清洗知识体系 原始数据 缺失值处理 重复值处理 异常值处理 一致性检查 删除 填充 插值 模型预测 完全重复 部分重复 3σ原则 箱线图 Z-Score DBSCAN 格式统一 单位统一 逻辑校验 编码映射 干净数据

数据清洗没有银弹。每个数据集都有自己的脾气。我的建议是:先理解业务,再动手清洗。别一上来就咔咔删数据,先问问自己——这个缺失值合理吗?这个异常值是不是新趋势?

嗯,数据清洗就是这样。看似琐碎,但每一步都在为后面的模型打基础。你想想看,地基没打好,房子能稳吗?


专注资料整理