2. 数据清洗与预处理
任务目标
对已获取的股票日线数据执行标准化预处理:统一日期列为datetime类型、去除含缺失值的记录、识别并处理收盘价中的异常离群值,最终输出一份可直接用于后续策略回测的干净数据集。
环境与依赖
需要 pandas 与 numpy。若尚未安装,执行:
pip install pandas numpy
关键完整代码
import pandas as pd
import numpy as np
def load_raw_data():
"""构造含缺失值与异常值的原始股票数据(模拟真实获取结果)"""
return pd.DataFrame({
'date': ['2024-01-03', '2024-01-04', None, '2024-01-08',
'2024-01-09', '2024-01-10', '2024-01-11'],
'open': [10.5, 10.8, 11.0, 10.9, 11.2, 11.5, 11.3],
'close': [10.7, 10.9, np.nan, 11.0, 11.4, 11.6, 11.2],
'volume': [10000, 12000, 11000, 13000, 12500, 14000, 99999999]
})
def preprocess_stock_data(df):
"""清洗:统一日期格式 → 去缺失 → 处理异常值"""
# 1. 统一日期格式:转为 datetime,非法值转为 NaT
df['date'] = pd.to_datetime(df['date'], errors='coerce')
# 2. 去除缺失值(含 date 为 NaT 的行)
df = df.dropna().copy()
# 3. 处理异常值:用 IQR 法识别收盘价离群点,以中位数填充
q1 = df['close'].quantile(0.25)
q3 = df['close'].quantile(0.75)
iqr = q3 - q1
lower = q1 - 1.5 * iqr
upper = q3 + 1.5 * iqr
median_close = df['close'].median()
df['close'] = df['close'].where(
(df['close'] >= lower) & (df['close'] <= upper), median_close
)
# 4. 按日期排序,重置索引
df = df.sort_values('date').reset_index(drop=True)
return df
if __name__ == '__main__':
raw = load_raw_data()
print('原始数据:')
print(raw)
print('\n数据形状:', raw.shape)
cleaned = preprocess_stock_data(raw)
print('\n清洗后数据:')
print(cleaned)
print('\n数据形状:', cleaned.shape)
print('日期类型:', cleaned['date'].dtype)
print('缺失值数量:', cleaned.isna().sum().sum())
运行与验证
直接运行脚本即可看到清洗前后对比。判定通过条件:
- 日期统一:清洗后
date列类型为datetime64[ns],且无NaT; - 缺失值去除:清洗后
isna().sum().sum()输出为0; - 异常值处理:原始数据中
volume列的99999999与close列的缺失值所在行被移除;close列中若存在超出 1.5 倍 IQR 的离群值,会被替换为该列中位数(本例原始close无离群点,因此数值不变,但逻辑已生效); - 行数变化:原始 7 行 → 清洗后 5 行(缺失日期 1 行 + 缺失收盘价 1 行被删除)。
预期输出(由代码逻辑唯一确定)
原始数据:
date open close volume
0 2024-01-03 10.5 10.7 10000
1 2024-01-04 10.8 10.9 12000
2 None 11.0 NaN 11000
3 2024-01-08 10.9 11.0 13000
4 2024-01-09 11.2 11.4 12500
5 2024-01-10 11.5 11.6 14000
6 2024-01-11 11.3 11.2 99999999
数据形状: (7, 4)
清洗后数据:
date open close volume
0 2024-01-03 10.5 10.7 10000
1 2024-01-04 10.8 10.9 12000
2 2024-01-08 10.9 11.0 13000
3 2024-01-09 11.2 11.4 12500
4 2024-01-10 11.5 11.6 14000
数据形状: (5, 4)
日期类型: datetime64[ns]
缺失值数量: 0
常见问题
- 真实数据源日期格式混杂:
pd.to_datetime的errors='coerce'会将无法解析的日期转为NaT,随后被dropna()统一移除,避免格式不一致影响后续时间序列操作。 - 异常值判定口径:本例采用 IQR 法则,适用于无先验阈值场景。若业务上对价格有硬性涨跌幅限制(如 ±10%),可改用自定义上下界替换 IQR 逻辑。
- 填充 vs 删除:缺失值采用直接删除策略,适合缺失比例低的情况;若缺失集中在某只股票的时间段,可考虑改用前向填充或插值,但需在代码中显式替换
dropna()逻辑。