第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 无固定范围,受异常值影响小 数据包含较多异常值
我的经验: 如果数据里有明显的异常值,别用Min-Max归一化,因为它会把异常值压缩到0或1附近,导致正常数据挤在一起。这时候用Robust标准化更靠谱。
# 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 本章知识体系总览

下面这张图,是我自己总结的数据清洗与预处理的核心流程。你把它打印出来贴在工位上,每次做数据预处理前看一眼,能少走很多弯路。

数据清洗与预处理核心流程 原始数据 步骤1:缺失值处理 步骤2:异常值检测与处理 步骤3:噪声数据处理 步骤4:标准化 / 归一化 删除 / 填充 / 插补 Z-Score / IQR / 孤立森林 移动平均 / 小波变换 / 滤波 Z-Score / Min-Max / Robust

4.6 本章小结

数据清洗与预处理,说白了就是「磨刀不误砍柴工」。你花80%的时间把数据洗干净,后面20%的时间建模就能顺风顺水。反过来,如果数据没洗干净,后面花再多时间调参也是白搭。

我个人习惯把这四个步骤做成一个自动化流水线:缺失值处理 → 异常值检测 → 噪声降噪 → 标准化/归一化。每次拿到新数据,先跑一遍这个流水线,看看结果再决定下一步。

嗯,最后提醒一句:不要盲目套用方法。每种方法都有它的假设和适用场景,你得先理解你的数据,再选择合适的方法。这才是数据清洗的正确姿势。


专注资料整理