第4章:数据清洗实战:缺失值处理、异常值检测、数据类型转换、去重与标准化

数据清洗,说白了就是给数据「洗澡」。

我做了这么多年金融数据,见过太多「脏数据」了。有的字段空着,有的数值离谱到天上,有的明明该是数字却存成了字符串。嗯,今天咱们就把这些坑一个一个填上。

4.1 缺失值处理:别让空值毁了你的模型

金融数据里,缺失值太常见了。比如某只股票某天停牌,交易量就是空的。再比如财报里某个指标没披露,也是空值。

我个人习惯,先搞清楚缺失的原因。是随机缺失?还是系统缺失?这决定了后续的处理策略。

4.1.1 删除法

如果缺失比例很小(比如低于5%),直接删掉那几行就行。我在项目中遇到过,某次处理日频交易数据,缺失率只有0.3%,删掉完全不影响统计结果。

# 删除含有缺失值的行
df_clean = df.dropna()

# 删除缺失比例超过50%的列
df_clean = df.dropna(thresh=len(df) * 0.5, axis=1)

4.1.2 填充法

缺失比例高的时候,删除就不划算了。这时候得填充。

  • 均值/中位数填充:适合数值型字段。比如某只股票的历史收益率缺失,用过去30天的均值填上。
  • 前向填充:时间序列数据常用。比如某天的收盘价缺失,用上一个交易日的价格填。
  • 插值法:更平滑的处理方式。我记得有一次处理债券收益率曲线,用线性插值效果特别好。
# 均值填充
df['column'] = df['column'].fillna(df['column'].mean())

# 前向填充
df['price'] = df['price'].fillna(method='ffill')

# 线性插值
df['yield'] = df['yield'].interpolate(method='linear')
小技巧:金融时间序列数据,我建议优先用前向填充。因为金融数据有很强的连续性,用均值反而会破坏趋势。

4.2 异常值检测:揪出那些「离谱」的数据

异常值,说白了就是数据里的「刺头」。比如某只股票日收益率突然涨了1000%,这明显是数据录入错误。

你想想看,如果带着这些异常值去做回测,结果能信吗?

4.2.1 基于统计的方法

方法 适用场景 阈值
Z-Score 正态分布数据 |Z| > 3
IQR(四分位距) 偏态分布数据 Q1 - 1.5*IQR 或 Q3 + 1.5*IQR
MAD(中位数绝对偏差) 含极端值的数据 MAD > 3
# Z-Score 检测
from scipy import stats
z_scores = stats.zscore(df['return'])
outliers = df[abs(z_scores) > 3]

# IQR 检测
Q1 = df['volume'].quantile(0.25)
Q3 = df['volume'].quantile(0.75)
IQR = Q3 - Q1
outliers = df[(df['volume'] < Q1 - 1.5*IQR) | (df['volume'] > Q3 + 1.5*IQR)]
注意:金融数据往往不是正态分布。比如收益率数据有「肥尾」特征,用Z-Score容易漏掉真正的异常。我建议优先用IQR方法。

4.2.2 基于业务规则的方法

有些异常,统计方法检测不出来,但业务规则一眼就能看出来。

我曾经处理过一份债券数据,某只债券的票面利率竟然填了50%。这明显是录入时多打了个0。用业务规则一过滤,这种错误就无处遁形了。

# 业务规则:股票日收益率不能超过±20%
df_valid = df[(df['return'] >= -0.2) & (df['return'] <= 0.2)]

# 业务规则:交易量不能为负数
df_valid = df[df['volume'] > 0]

4.3 数据类型转换:让数据「对号入座」

数据类型不对,是金融数据清洗里最常见的坑。明明是个日期,存成了字符串;明明是个数字,存成了对象。

为什么会这样? 很多时候是因为数据源来自不同的系统,格式不统一。

4.3.1 日期时间转换

# 字符串转日期
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')

# 时间戳转日期
df['date'] = pd.to_datetime(df['timestamp'], unit='s')

4.3.2 数值类型转换

# 字符串转浮点数
df['price'] = pd.to_numeric(df['price'], errors='coerce')

# 整数类型转换
df['volume'] = df['volume'].astype('int64')
避坑指南:用 errors='coerce' 参数,遇到无法转换的值会自动变成NaN。这样就不会因为一个脏数据导致整个流程崩溃。

4.4 去重:别让重复数据「浑水摸鱼」

重复数据在金融数据里也很常见。比如同一个交易日的同一只股票,被录入了两次。

我记得有一次做因子分析,发现某个因子的IC值特别高,高兴了半天。后来一查,原来是数据重复了,导致相关性被高估。

# 完全去重
df_unique = df.drop_duplicates()

# 按指定列去重
df_unique = df.drop_duplicates(subset=['stock_code', 'date'])

4.5 标准化:让不同量纲的数据「同台竞技」

标准化,说白了就是把不同尺度的数据拉到同一个量级。比如股价是几百块,交易量是几百万股,不标准化的话,模型会天然偏向数值大的特征。

4.5.1 Z-Score 标准化

from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
df_scaled = scaler.fit_transform(df[['price', 'volume', 'return']])

4.5.2 Min-Max 标准化

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler(feature_range=(0, 1))
df_scaled = scaler.fit_transform(df[['price', 'volume', 'return']])
我的建议:如果数据中有异常值,优先用Z-Score标准化。因为Min-Max对异常值太敏感,一个极端值就能把其他数据压缩到很小的区间。

知识体系总览

下面这张图,把本章的核心逻辑串起来了。你可以把它当作数据清洗的「操作手册」。

数据清洗实战知识体系 原始数据 缺失值处理 删除法 填充法 异常值检测 统计方法 业务规则 数据类型转换 日期转换 数值转换 去重 完全去重 指定列去重 标准化 Z-Score Min-Max 清洗后的干净数据 数据清洗是ETL流程中最耗时、最关键的环节 清洗质量直接决定后续分析和建模的效果

数据清洗这件事,说白了就是「慢工出细活」。我见过太多人急着跑模型,结果被脏数据坑得怀疑人生。嗯,把上面这五个步骤走一遍,你的数据基本就「干净」了。

总结一下:缺失值先看比例再决定删还是填;异常值用统计方法加业务规则双重过滤;数据类型转换用pandas的to_*系列函数;去重注意指定关键列;标准化根据数据分布选方法。这五步走完,数据清洗就完成了80%。

公众号:蓝海资料掘金营,微信deep3321