数据准备:数据清洗、缺失值处理、异常值初步检测与处理方法

数据准备这一步,说实话,往往决定了模型的上限。我见过太多团队花大把时间调参,结果模型效果还是不行——最后一查,数据里一堆脏东西。你想想看,垃圾进,垃圾出,再牛的算法也救不回来。

今天我们就来聊聊数据清洗、缺失值处理和异常值初步检测。这三个环节,算是数据准备的核心骨架。我做异常评分项目时,光数据清洗就来回折腾了好几轮,踩过不少坑,后面我会逐个说。

数据清洗:别让脏数据毁了模型

数据清洗干的事,说白了就是去掉那些明显不合理的记录。我习惯按三步走:

  1. 去重——重复数据会扭曲统计量,比如均值偏向重复多的样本。可以用 df.duplicated() 检查,df.drop_duplicates() 删除。
  2. 格式统一——比如日期字段有的用“2024-01-01”,有的用“01/01/2024”,不统一的话后面没法处理。我一般直接转成时间戳。
  3. 剔除无效值——比如“年龄=-999”、“收入=0但实际不为0”这种明显瞎填的。可以用业务规则过滤。
我以前的经验: 做金融异常交易检测时,发现有些交易金额字段出现“null”字符串而不是真正的 NaN。不处理的话,Python 会把它当成字符串,后续计算全乱套。所以清洗时一定先看字段类型,别想当然。

缺失值处理:选对方法很重要

缺失值处理的方式很多,但选错了反而引入偏差。我个人常用的策略如下:

方法适用场景优点缺点
直接删除缺失比例小(<5%)且随机简单、不影响分布可能丢失有用信息
均值/中位数填充数值型、分布较对称计算快、保持样本量降低方差、可能扭曲分布
众数填充类别型或偏态分布符合多数情况可能引入偏差
插值法时间序列、单调趋势利用前后趋势对突变不敏感
模型预测填充缺失非随机、变量间相关精度较高计算成本大、可能过拟合
注意: 如果你用的是均值填充,建议先检查缺失值的分布。我曾经做过一个信用卡交易量预测,有个字段缺失率达到40%,用均值填充后模型准确率反而下降——因为缺失值本身不是随机的,都是大额交易才缺失。这种情况下,最好用模型预测或者单独标记“缺失”作为新特征。

简单的代码实现:

import pandas as pd
import numpy as np

# 加载数据
df = pd.read_csv('transaction.csv')

# 检查缺失
print(df.isnull().sum())

# 数值列用中位数填充(避免受异常值影响)
for col in ['amount', 'balance']:
    df[col].fillna(df[col].median(), inplace=True)

# 类别列用众数填充
df['channel'].fillna(df['channel'].mode()[0], inplace=True)

# 时间序列用插值
df['daily_amount'].interpolate(method='linear', inplace=True)

异常值初步检测:先捞出来再决定怎么办

异常值检测是异常评分模型的前置步骤——你总得知道哪些点是“可疑”的,才能去训练模型。我常用的方法有三类:

  • 统计方法: Z-score(一般认为Z>3或Z<-3是异常)或IQR(四分位距法,Q1-1.5*IQR ~ Q3+1.5*IQR之外)。
  • 可视化方法: 箱线图、散点图,一眼就能看出离群点。但数据量大时效率低。
  • 业务规则: 比如交易金额超过日均10倍、登录地点异常等。这个往往最有效,我习惯先用业务规则过滤一遍。

这里有个坑:Z-score 对数据分布敏感。如果数据本身偏态很大,直接用 Z-score 会误判很多正常点。我建议先用 Box-Cox 变换把数据拉成正态,再算 Z-score,结果会更靠谱。

看看 IQR 法的代码:

Q1 = df['amount'].quantile(0.25)
Q3 = df['amount'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# 标记异常
df['is_outlier_iqr'] = (df['amount'] < lower_bound) | (df['amount'] > upper_bound)

异常值处理方法:别一棍子打死

检测出异常值,不代表要直接删。我的原则是:先看异常值产生的原因。如果是数据录入错误,删除或修正;如果是真实的小概率事件(比如银行大额转账),则要保留——因为异常评分模型正是要学习这些模式。

常见处理手段:

  • 截尾(截断): 把超过上下界的值直接拉到边界,适合非极端异常的情况。
  • 封顶/封底: 类似截尾,但更柔和,比如用 99% 分位数代替。
  • 替换: 用均值、中位数或预测值替换,适用于确定是脏数据。
  • 保留: 单独加一列标记是否为异常,让模型自己去学关系。

我个人做异常评分模型时,更倾向于保留异常值但加上权重或者标记字段。为什么?因为异常评分本质就是识别“少数派”,如果一开始就把异常点全处理掉,模型反而学不到什么是异常。

避坑指南: 我曾经做过一个网络入侵检测的项目,一开始把明显异常的流量包都删掉了,结果模型在测试集上召回率极低。后来才发现,那些“异常”其实是真实攻击样本,删除等于删了标签。所以异常值处理一定要先确认业务含义,别一刀切。

数据准备流程总览

我把这三步串起来,画了张流程图,方便你对照操作。

数据准备流程(数据清洗 → 缺失值处理 → 异常值检测与处理) 原始数据 数据清洗 去重|格式统一|无效值剔除 缺失值处理 删除|填充(均值/插值)|模型预测 异常值初步检测 Z-score|IQR|箱线图|业务规则 是否异常? 异常值处理 截尾/封顶/替换/保留 保留特征 清洗后的干净数据 说明: 每一阶段都可能需要多次迭代。实际项目中,我往往会在异常值检测后再检查缺失值——因为某些异常值可能是缺失值造成的。 注意箭头方向:先清洗,再补缺,最后检测处理异常值。顺序不能乱。

好了,数据准备这三大块就这些。记住:没有统一的最佳实践,只有最适合你数据的方案。多检查,多试验,别怕花时间——数据准备做扎实了,后面模型训练就顺多了。