数据预处理基础:数据清洗、缺失值处理、异常值检测与修正
各位同学,咱们今天聊点实在的。做景气度领先指标,数据预处理这步要是没做好,后面模型再花哨也是白搭。我见过太多人一上来就搞复杂模型,结果数据里一堆脏东西,最后跑出来的结果根本没法看。说白了,数据预处理就是给指标打地基,地基不稳,楼盖得再高也得塌。
数据清洗:先把脏东西筛出去
数据清洗,听起来简单,做起来全是坑。我个人的习惯是,拿到数据第一件事不是看统计量,而是先扫一眼原始数据长什么样。你想想看,宏观经济数据里经常出现什么?重复值、格式不统一、单位混乱,这些都得先处理掉。
核心原则:清洗不是删数据,而是让数据变得可用。能修的就修,实在修不了的再考虑删除。
我在项目中遇到过最典型的问题:某个月份的工业增加值数据,因为统计口径调整,突然跳了个大台阶。这种数据你要是直接扔进模型,模型会以为经济突然起飞了,其实只是统计方法变了。怎么办?
- 重复值处理:检查是否有完全相同的行记录,通常用
df.duplicated()就能搞定 - 格式统一:日期格式、数值格式、单位,必须统一。比如有的数据用"亿",有的用"万",不统一就是灾难
- 逻辑校验:比如"固定资产投资完成额"不可能为负数,出现负值就是录入错误
# 我常用的数据清洗代码片段
import pandas as pd
import numpy as np
# 加载数据
df = pd.read_csv('macro_data.csv')
# 检查重复值
print(f"重复行数: {df.duplicated().sum()}")
# 统一日期格式
df['date'] = pd.to_datetime(df['date'], format='%Y%m')
# 逻辑校验:剔除明显异常
df = df[df['value'] >= 0] # 剔除负值
缺失值处理:别让空值毁了你的指标
宏观经济数据,缺失值简直是家常便饭。统计局有时候会延迟发布数据,或者干脆某个指标就不公布了。这时候怎么办?
我个人把缺失值处理分成三步走:先看缺失比例,再看缺失模式,最后选处理方法。
| 缺失比例 | 建议处理方式 | 适用场景 |
|---|---|---|
| < 5% | 插值法(线性/样条) | 数据波动平稳,缺失点少 |
| 5% - 20% | 前向填充 + 后向填充 | 时间序列数据,趋势明显 |
| 20% - 40% | 模型预测填充(如ARIMA) | 有较强的周期性或趋势 |
| > 40% | 考虑剔除该变量 | 缺失太多,填充意义不大 |
小技巧:我曾经处理过一组PMI数据,中间有3个月缺失。直接用线性插值?不行,因为PMI有明显的季节性。我用了X-13ARIMA-SEATS做季节调整后再插值,效果好了很多。
嗯,这里要注意:千万别用均值填充时间序列数据。为什么?因为宏观经济数据有趋势和周期,均值填充会破坏数据的动态结构。你想想看,GDP数据用均值填充,那趋势线直接就平了,还怎么做景气度分析?
# 缺失值处理示例
# 前向填充 + 后向填充
df['value'] = df['value'].ffill().bfill()
# 线性插值
df['value'] = df['value'].interpolate(method='linear')
# 季节性插值(如果有周期)
df['value'] = df['value'].interpolate(method='spline', order=3)
异常值检测与修正:揪出那些"离谱"的数据点
异常值检测,说白了就是找出那些"不太对劲"的数据。我刚开始做量化的时候,有一次跑模型,结果R²高得离谱,0.99。我当时还挺高兴,后来一查,原来是某个数据点录入时多了一个零,把整个回归结果带偏了。
从那以后,我养成了一个习惯:每次建模前,必做异常值检测。
常用的检测方法
- 3σ原则:适用于正态分布的数据,超过均值±3倍标准差就算异常
- 箱线图法:用IQR(四分位距)来判定,超过Q1-1.5*IQR或Q3+1.5*IQR就算异常
- 移动平均法:对于时间序列,看当前值是否偏离移动平均线太远
避坑指南:我曾经在分析工业增加值时,用3σ原则检测异常值,结果把2008年金融危机期间的数据全标成了异常。但那些数据恰恰是真实的经济波动,不是录入错误。所以,异常值检测一定要结合业务背景来判断,不能机械地套公式。
修正策略
发现异常值后,怎么处理?我的经验是分情况讨论:
- 录入错误:直接修正,比如多了一个零就删掉
- 真实极端值:保留,但做缩尾处理(Winsorize),比如把极端值拉到99%分位数
- 结构性突变:分段处理,比如2008年金融危机前后的数据分开建模
# 异常值检测与修正
from scipy import stats
# 3σ原则
z_scores = np.abs(stats.zscore(df['value']))
outliers = z_scores > 3
# 缩尾处理(Winsorize)
lower = df['value'].quantile(0.01)
upper = df['value'].quantile(0.99)
df['value_winsorized'] = df['value'].clip(lower, upper)
# 移动平均法检测
df['ma_3'] = df['value'].rolling(window=3).mean()
df['deviation'] = np.abs(df['value'] - df['ma_3']) / df['ma_3']
outliers_ma = df['deviation'] > 0.2 # 偏离超过20%算异常
知识体系总览
下面这张图,是我自己总结的数据预处理流程。每次做新项目,我都会先过一遍这个流程,确保没有遗漏。
最后说一句,数据预处理没有标准答案。同样的数据,不同的人处理方式可能完全不同。关键是要理解你的数据、理解你的业务场景。我做了这么多年,最大的体会就是:预处理花的时间越多,后面建模就越省心。别急着跑模型,先把数据收拾利索了。