数据准备:数据清洗、缺失值处理、异常值初步检测与处理方法
数据准备这一步,说实话,往往决定了模型的上限。我见过太多团队花大把时间调参,结果模型效果还是不行——最后一查,数据里一堆脏东西。你想想看,垃圾进,垃圾出,再牛的算法也救不回来。
今天我们就来聊聊数据清洗、缺失值处理和异常值初步检测。这三个环节,算是数据准备的核心骨架。我做异常评分项目时,光数据清洗就来回折腾了好几轮,踩过不少坑,后面我会逐个说。
数据清洗:别让脏数据毁了模型
数据清洗干的事,说白了就是去掉那些明显不合理的记录。我习惯按三步走:
- 去重——重复数据会扭曲统计量,比如均值偏向重复多的样本。可以用
df.duplicated()检查,df.drop_duplicates()删除。 - 格式统一——比如日期字段有的用“2024-01-01”,有的用“01/01/2024”,不统一的话后面没法处理。我一般直接转成时间戳。
- 剔除无效值——比如“年龄=-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% 分位数代替。
- 替换: 用均值、中位数或预测值替换,适用于确定是脏数据。
- 保留: 单独加一列标记是否为异常,让模型自己去学关系。
我个人做异常评分模型时,更倾向于保留异常值但加上权重或者标记字段。为什么?因为异常评分本质就是识别“少数派”,如果一开始就把异常点全处理掉,模型反而学不到什么是异常。
避坑指南: 我曾经做过一个网络入侵检测的项目,一开始把明显异常的流量包都删掉了,结果模型在测试集上召回率极低。后来才发现,那些“异常”其实是真实攻击样本,删除等于删了标签。所以异常值处理一定要先确认业务含义,别一刀切。
数据准备流程总览
我把这三步串起来,画了张流程图,方便你对照操作。
好了,数据准备这三大块就这些。记住:没有统一的最佳实践,只有最适合你数据的方案。多检查,多试验,别怕花时间——数据准备做扎实了,后面模型训练就顺多了。