数据预处理基础:数据清洗、缺失值处理、异常值检测与处理、数据标准化与归一化
各位同学,咱们今天聊点实在的。
做量化因子挖掘,很多人一上来就搞复杂模型,结果数据本身一塌糊涂。我见过太多这样的案例了——模型跑得飞起,回测曲线漂亮得像假的一样,结果一上实盘就崩。为什么?数据预处理没做好。
说白了,数据预处理就是给因子挖掘打地基。地基不稳,楼盖得再高也是危房。今天我就把这块掰开了揉碎了讲清楚。
数据清洗:先把脏东西筛掉
数据清洗,听着简单,做起来全是坑。
我刚开始做量化那会儿,从某数据商拿了一批日线数据,直接丢进模型训练。结果发现某些股票的收盘价居然比开盘价还高出一大截,但当天明明是跌停的。后来一查,是数据商把复权因子搞错了。
所以数据清洗第一步,就是做合理性检查。具体来说,我一般会检查这几项:
- 字段类型检查:日期是不是日期格式?价格是不是数值?别让字符串混进来
- 范围检查:市盈率不可能为负(除非亏损),换手率不可能超过100%,这些常识性边界要卡死
- 逻辑一致性检查:开盘价、最高价、最低价、收盘价之间的逻辑关系——最高价必须大于等于最低价,收盘价必须在区间内
- 重复值检查:同一只股票同一天出现两条记录,这明显有问题
核心原则:宁可丢掉10%的可疑数据,也不要让1%的错误数据污染模型。
缺失值处理:别让空值毁了你的因子
金融数据里缺失值太常见了。停牌、新股上市、数据源断供,各种原因都会导致空值。
处理缺失值,我一般按这个优先级来:
- 直接删除:如果某只股票缺失率超过70%,我建议直接删掉这只股票。留着也是噪音。
- 填充处理:对于少量缺失,可以用均值、中位数、前向填充等方法。
- 模型预测:对于关键因子,可以用其他特征做回归预测来填补。
这里有个坑,我曾经踩过——用未来数据填充缺失值。比如用当天的收盘价去填充开盘价的缺失,这在回测里就是典型的未来函数。切记,填充只能用历史信息。
我的习惯:对于时间序列数据,前向填充(ffill)往往比均值填充更合理。因为金融数据有很强的时序相关性,昨天的值比平均值更接近真实值。
# 一个简单的缺失值处理示例
import pandas as pd
import numpy as np
# 前向填充
df['factor'] = df['factor'].ffill()
# 如果开头还有缺失,用后向填充
df['factor'] = df['factor'].bfill()
# 实在不行,用中位数兜底
df['factor'] = df['factor'].fillna(df['factor'].median())
异常值检测与处理:把野马驯服
异常值,说白了就是那些离谱的数据点。比如某只股票一天的涨幅达到500%,这明显是数据错误或者极端事件。
我常用的检测方法有三种:
| 方法 | 适用场景 | 优缺点 |
|---|---|---|
| 3σ原则 | 数据近似正态分布 | 简单粗暴,但对偏态分布效果差 |
| IQR四分位距法 | 大多数情况 | 稳健,不受极端值影响 |
| MAD绝对中位差法 | 存在大量异常值 | 最稳健,但计算稍复杂 |
我个人最常用的是IQR法。为什么?因为它对极端值不敏感,你想想看,如果数据本身就有很多异常,用3σ法反而会被这些异常值带偏。
注意:处理异常值不是简单地删掉。对于金融数据,某些异常值可能蕴含着重要的市场信息(比如股灾期间的极端波动)。我建议先标记,再根据业务逻辑决定是删除、截尾还是单独处理。
# IQR法检测异常值
def detect_outliers_iqr(data, factor='factor'):
Q1 = data[factor].quantile(0.25)
Q3 = data[factor].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return (data[factor] < lower_bound) | (data[factor] > upper_bound)
数据标准化与归一化:让因子站在同一起跑线
这一步很多人容易忽略。你想想看,市盈率可能是几十到几百,换手率是百分之零点几到几十,量价相关性是-1到1。这些因子量纲完全不同,直接丢进模型,量级大的因子会主导训练过程。
标准化和归一化,就是解决这个问题的。
- 归一化(Min-Max Scaling):把数据压缩到[0,1]区间。适合数据分布比较均匀的情况。
- 标准化(Z-score):把数据变成均值为0、标准差为1的分布。适合数据存在异常值的情况。
- 稳健标准化(Robust Scaling):用中位数和四分位距,对异常值不敏感。我比较推荐这个。
我的建议:在因子挖掘中,优先用稳健标准化。因为金融数据里异常值太多了,Min-Max会被极端值压扁,Z-score也会受影响。稳健标准化用中位数和IQR,稳得很。
from sklearn.preprocessing import RobustScaler
# 稳健标准化
scaler = RobustScaler()
df['factor_scaled'] = scaler.fit_transform(df[['factor']])
知识体系总览
下面这张图,是我梳理的数据预处理核心流程。你可以把它当成一个检查清单,每次做因子挖掘前过一遍。
嗯,流程就是这样。每一步都有它的道理,少一步都不行。
避坑指南:我曾经在做一个多因子模型时,偷懒跳过了异常值检测,结果模型在回测里表现极好,但实盘时连续亏损。后来发现,回测期间有一个极端行情被模型当成了常态,而实盘里根本没有这种极端情况。从那以后,我再也不敢跳过任何一步预处理了。
好了,数据预处理这块就讲到这里。记住一句话:好的因子挖掘,七分在预处理,三分在建模。把基础打牢,后面的路就好走了。