第4章:数据清洗与预处理——处理缺失值、异常值、噪声数据,数据标准化与归一化
说实话,做另类数据整合分析这么多年,我踩过最大的坑,不是模型选错了,也不是数据源找偏了,而是——数据本身是脏的。
你想想看,我们费了老大劲从各种犄角旮旯里扒拉出另类数据,结果一跑模型,全崩了。为什么?因为缺失值没处理、异常值没识别、噪声数据还在里面搅局。嗯,这就像你辛辛苦苦淘了一堆食材,结果发现有的烂了、有的没洗、有的还掺了沙子——直接下锅,能好吃才怪。
所以今天这一章,咱们就专门聊聊数据清洗与预处理。我个人习惯把这步叫做「数据的体检和整容」,虽然听起来糙了点,但道理就是这个道理。
4.1 缺失值处理:别让「不知道」毁了你的分析
缺失值,说白了就是数据里某些字段是空的。比如你爬了一堆电商评论,结果有的评论没有用户ID,有的没有评分——这就是缺失。
为什么会有缺失?
- 数据采集时漏了(比如爬虫没爬到)
- 用户没填(比如注册时非必填项)
- 数据本身就不存在(比如某些指标对某些样本不适用)
我在项目中遇到过最离谱的一次,是某金融另类数据里,有30%的字段都是空的。当时团队差点想放弃这个数据源。后来我仔细一看,其实很多缺失是有规律的——比如某些小公司的财务数据就是不全,但大公司基本都有。这种情况下,直接删掉小公司样本,反而会引入偏差。
4.1.1 常见处理方法
| 方法 | 适用场景 | 缺点 |
|---|---|---|
| 直接删除 | 缺失比例很小(<5%) | 可能丢失信息 |
| 均值/中位数填充 | 数值型数据,分布较对称 | 会降低方差 |
| 众数填充 | 分类数据 | 可能引入偏差 |
| 前向/后向填充 | 时间序列数据 | 不适合跳跃性数据 |
| 模型预测填充 | 缺失值较多,且特征相关性强 | 计算成本高 |
# Python示例:缺失值处理
import pandas as pd
import numpy as np
# 读取数据
df = pd.read_csv('alternative_data.csv')
# 检查缺失比例
missing_ratio = df.isnull().sum() / len(df)
print(missing_ratio)
# 数值列:用中位数填充
df['price'].fillna(df['price'].median(), inplace=True)
# 分类列:用众数填充
df['category'].fillna(df['category'].mode()[0], inplace=True)
# 时间序列:前向填充
df['sensor_value'].fillna(method='ffill', inplace=True)
4.2 异常值检测:揪出那些「不对劲」的数据点
异常值,就是那些明显偏离正常范围的数据点。比如某只股票平时波动1%,突然有一天波动了50%——这要么是极端事件,要么就是数据出错了。
我曾经处理过一批卫星图像数据,发现某个区域的植被指数突然飙升到正常值的10倍。一开始以为是生态奇迹,后来一查,是传感器坏了。你看,如果不做异常值检测,这种「奇迹」就会把模型带偏。
4.2.1 常用检测方法
- Z-Score法: 假设数据服从正态分布,Z-Score绝对值大于3的视为异常。适合数据量大的场景。
- IQR(四分位距)法: 小于Q1-1.5*IQR或大于Q3+1.5*IQR的视为异常。不依赖正态分布假设,更稳健。
- 孤立森林: 基于随机森林的异常检测算法,适合高维数据。我在做用户行为分析时经常用。
- DBSCAN聚类: 将密度低的点视为异常。适合空间数据。
# Python示例:IQR法检测异常值
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df['value'] < lower_bound) | (df['value'] > upper_bound)]
print(f"检测到 {len(outliers)} 个异常值")
4.3 噪声数据处理:给数据「降噪」
噪声数据,就是那些随机波动、没有实际意义的数据。比如传感器采集的温度数据,每隔几秒就跳一下,但实际温度根本没变——这就是噪声。
噪声和异常值的区别在于:异常值是「大错」,噪声是「小抖」。处理方式也不同。
4.3.1 常用降噪方法
- 移动平均: 用窗口内的均值代替当前值。简单有效,但会丢失细节。
- 中值滤波: 用窗口内的中位数代替当前值。对脉冲噪声效果好。
- 小波变换: 将信号分解到不同频率,去掉高频噪声。适合非平稳信号。
- 卡尔曼滤波: 基于状态空间模型的递归滤波。适合实时数据。
# Python示例:移动平均降噪
df['smoothed'] = df['noisy_value'].rolling(window=5, center=True).mean()
# 中值滤波
from scipy.signal import medfilt
df['filtered'] = medfilt(df['noisy_value'].values, kernel_size=5)
4.4 数据标准化与归一化:让不同量纲的数据「对话」
标准化和归一化,说白了就是把不同尺度的数据拉到同一个量级上。比如有的特征取值范围是0-1,有的是0-10000,如果不处理,模型就会天然偏向数值大的特征。
我记得有一次做多源数据融合,一边是股价(几百块),一边是交易量(几百万股),直接扔进模型,结果模型只学了交易量,股价完全没起作用。后来做了标准化,效果立竿见影。
4.4.1 标准化 vs 归一化
| 方法 | 公式 | 输出范围 | 适用场景 |
|---|---|---|---|
| Z-Score标准化 | (x - μ) / σ | 无固定范围,均值为0,标准差为1 | 数据近似正态分布,或需要保留异常值信息 |
| Min-Max归一化 | (x - min) / (max - min) | [0, 1] | 数据有明确边界,或需要将数据压缩到固定区间 |
| Robust标准化 | (x - median) / IQR | 无固定范围,受异常值影响小 | 数据包含较多异常值 |
# Python示例:标准化与归一化
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
# Z-Score标准化
scaler_std = StandardScaler()
df['std_scaled'] = scaler_std.fit_transform(df[['value']])
# Min-Max归一化
scaler_minmax = MinMaxScaler()
df['minmax_scaled'] = scaler_minmax.fit_transform(df[['value']])
# Robust标准化
scaler_robust = RobustScaler()
df['robust_scaled'] = scaler_robust.fit_transform(df[['value']])
4.5 本章知识体系总览
下面这张图,是我自己总结的数据清洗与预处理的核心流程。你把它打印出来贴在工位上,每次做数据预处理前看一眼,能少走很多弯路。
4.6 本章小结
数据清洗与预处理,说白了就是「磨刀不误砍柴工」。你花80%的时间把数据洗干净,后面20%的时间建模就能顺风顺水。反过来,如果数据没洗干净,后面花再多时间调参也是白搭。
我个人习惯把这四个步骤做成一个自动化流水线:缺失值处理 → 异常值检测 → 噪声降噪 → 标准化/归一化。每次拿到新数据,先跑一遍这个流水线,看看结果再决定下一步。
嗯,最后提醒一句:不要盲目套用方法。每种方法都有它的假设和适用场景,你得先理解你的数据,再选择合适的方法。这才是数据清洗的正确姿势。