3、数据清洗实战:处理缺失值、异常值,统一不同数据源的字段格式
数据清洗这事儿,说白了就是「把脏数据洗干净」。我在量化圈混了这些年,见过太多人拿着漂亮策略,结果死在数据质量上。你想想看,主力资金流向分析要是用了脏数据,那跟闭着眼睛开车有啥区别?
这一章,咱们就动手干点实在的。我会带着你处理三种最常见的坑:缺失值、异常值,还有不同数据源打架的字段格式问题。
3.1 缺失值处理:别让「空」坑了你
主力资金数据里,缺失值太常见了。比如某天交易所没更新,或者数据源抽风。我遇到过最离谱的一次,某只股票连续三天资金流向全是 NaN,后来发现是接口字段名改了。
处理缺失值,我个人习惯分三步走:
- 先诊断:看看缺失比例高不高
- 再决策:是删、是补、还是留着
- 后执行:选个靠谱的方法填上
来看代码,这是我最常用的诊断方式:
import pandas as pd
import numpy as np
# 假设 df 是主力资金数据
# 看看每列缺失了多少
missing_ratio = df.isnull().mean().sort_values(ascending=False)
print(missing_ratio)
# 如果某列缺失超过 30%,我一般直接扔掉
# 低于 5% 的,用前向填充就够用了
if missing_ratio.max() > 0.3:
df.dropna(thresh=len(df)*0.7, axis=1, inplace=True)
print("缺失太多的列已删除")
核心原则:主力资金数据是时序数据,千万别用均值填充。用前向填充(ffill)或者插值法更靠谱。
嗯,这里要注意:如果连续缺失超过 3 天,我建议直接标记为「无效交易日」,别硬补。为什么?因为主力资金流向是连续行为,硬补出来的数据会扭曲真实信号。
3.2 异常值处理:揪出那些「离谱」的数字
异常值这东西,说白了就是数据里的「刺头」。比如某只股票一天主力净流入 100 亿,而它总市值才 50 亿——这明显是数据录入错误。
我在项目中遇到过最经典的案例:某数据源把「万元」和「元」搞混了,导致资金流向数据大了 10000 倍。当时我的策略回测收益高得吓人,差点就实盘了。还好留了个心眼,做了异常值检测。
常用的检测方法就两种:
| 方法 | 适用场景 | 我的经验 |
|---|---|---|
| 3σ 原则 | 数据近似正态分布 | 主力资金净流入通常偏态,慎用 |
| IQR 四分位法 | 任何分布都行 | 我 90% 的情况都用这个 |
实战代码长这样:
def detect_outliers_iqr(series):
Q1 = series.quantile(0.25)
Q3 = series.quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
return (series < lower) | (series > upper)
# 标记异常值
df['is_outlier'] = detect_outliers_iqr(df['net_inflow'])
# 处理方式:我一般用中位数替换,而不是删除
# 因为删除会破坏时间序列的连续性
df.loc[df['is_outlier'], 'net_inflow'] = df['net_inflow'].median()
避坑指南:我曾经直接把异常值删掉,结果导致时间序列出现断层,后续的择时信号全部错位。记住:对于时序数据,替换比删除安全。
3.3 统一字段格式:让不同数据源「说同一种语言」
做量化的人都知道,不同数据源简直是「方言大会」。同花顺叫「主力净流入」,东方财富叫「大单净额」,万得叫「机构资金流向」。字段名不一样就算了,连单位都可能不同。
我整理了一套标准化的映射规则,你直接拿去用:
# 字段映射表
field_mapping = {
'主力净流入': 'net_inflow',
'大单净额': 'net_inflow',
'机构资金流向': 'net_inflow',
'主力净占比': 'net_inflow_ratio',
'大单净占比': 'net_inflow_ratio',
'超大单净流入': 'super_large_inflow',
'小单净流入': 'small_inflow'
}
# 统一重命名
df.rename(columns=field_mapping, inplace=True)
# 统一单位:全部转为「万元」
# 有的数据源是元,有的是万元,还有的是亿元
def unify_unit(value, source):
if source == 'yuan':
return value / 10000
elif source == 'yi':
return value * 10000
else:
return value # 已经是万元
小技巧:我习惯在数据入库时就做字段映射,建一个统一的 schema。这样后面所有策略代码都不用改,换数据源就像换插头一样简单。
3.4 实战流程:一条龙搞定
说了这么多,咱们串起来走一遍。这是我每天跑数据的标准流程:
这个流程我跑了两年多,基本没出过岔子。你刚开始可能觉得麻烦,但养成习惯后,数据质量会给你省下大把时间。
3.5 完整代码示例
最后,给你一个可以直接跑的完整函数。我把它封装成了工具,每次换数据源直接调用:
def clean_fund_flow_data(df, source='tonghuashun'):
"""
主力资金数据清洗一站式函数
"""
# 1. 字段映射
df.rename(columns=field_mapping, inplace=True)
# 2. 缺失值处理
for col in ['net_inflow', 'net_inflow_ratio']:
if col in df.columns:
df[col] = df[col].ffill(limit=3)
# 3. 异常值处理
for col in ['net_inflow', 'super_large_inflow']:
if col in df.columns:
outliers = detect_outliers_iqr(df[col])
df.loc[outliers, col] = df[col].median()
# 4. 单位统一
if source == 'dongfang':
df['net_inflow'] = df['net_inflow'] / 10000
return df
# 一行搞定
clean_df = clean_fund_flow_data(raw_df, source='tonghuashun')
记住:数据清洗不是一次性工作。每次换数据源、每次策略回测前,都要重新跑一遍。我见过太多人偷懒,结果被脏数据坑得血本无归。
好了,这一章的内容就到这儿。数据清洗看着琐碎,但它是整个量化体系的基石。你把这套流程跑熟了,后面的择时分析才能站得稳。