2. 数据清洗
任务目标
本案例基于案例1获取的原始股票数据(含日期、开盘价、最高价、最低价、收盘价、成交量),完成数据清洗:处理缺失值与异常值,输出可直接用于后续特征工程和模型训练的干净数据集。
环境与依赖
Python 3.8+,依赖库:pandas、numpy。若尚未安装,执行 pip install pandas numpy。
核心实现
清洗逻辑包含三个步骤:缺失值处理(前向填充+剩余丢弃)、异常值检测(基于IQR法则)、异常值替换(用上下边界截断而非删除,保留时间序列连续性)。
import pandas as pd
import numpy as np
def clean_stock_data(df):
"""
清洗股票日线数据:处理缺失值与异常值。
参数:
df: DataFrame,必须包含列 ['date','open','high','low','close','volume']
返回:
DataFrame,清洗后的数据,按日期升序排列
"""
# 深拷贝避免修改原数据
df = df.copy()
# 1. 基础检查:确保必需列存在
required_cols = ['date', 'open', 'high', 'low', 'close', 'volume']
missing_cols = [c for c in required_cols if c not in df.columns]
if missing_cols:
raise ValueError(f"缺少必需列: {missing_cols}")
# 2. 日期列标准化并排序
df['date'] = pd.to_datetime(df['date'])
df = df.sort_values('date').reset_index(drop=True)
# 3. 缺失值处理
# 3.1 数值列前向填充(交易日之间缺失用上一交易日填充)
numeric_cols = ['open', 'high', 'low', 'close', 'volume']
df[numeric_cols] = df[numeric_cols].ffill()
# 3.2 若仍存在缺失(如首行缺失),直接丢弃
df = df.dropna(subset=numeric_cols).reset_index(drop=True)
# 4. 异常值检测与处理(IQR法则)
def cap_outliers(series):
Q1 = series.quantile(0.25)
Q3 = series.quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 用边界值截断异常值,保留数据点
return series.clip(lower_bound, upper_bound)
# 对价格列和成交量列分别处理
for col in ['open', 'high', 'low', 'close']:
df[col] = cap_outliers(df[col])
# 成交量下限设为0(成交量不应为负)
df['volume'] = cap_outliers(df['volume'])
df['volume'] = df['volume'].clip(lower=0)
# 5. 逻辑校验:确保 high >= max(open, close),low <= min(open, close)
# 若因异常值截断导致违反逻辑,则修正
df['high'] = df[['high', 'open', 'close']].max(axis=1)
df['low'] = df[['low', 'open', 'close']].min(axis=1)
return df
# 最小调用示例(使用模拟数据验证清洗逻辑)
if __name__ == "__main__":
# 构造含缺失值和异常值的模拟数据
raw_data = pd.DataFrame({
'date': ['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05'],
'open': [100.0, 101.5, np.nan, 103.0, 500.0], # 含缺失和异常高价
'high': [102.0, 103.0, 104.0, 104.5, 510.0],
'low': [99.0, 100.5, 101.0, 102.0, 490.0],
'close':[101.0, 102.0, 102.5, 103.5, 505.0],
'volume':[1000000, 1200000, np.nan, 1100000, 99999999] # 含缺失和异常量
})
cleaned = clean_stock_data(raw_data)
print(cleaned)
print(f"\n清洗后剩余记录数: {len(cleaned)}")
运行与验证
运行方式:将上述代码保存为 data_cleaning.py,在命令行执行 python data_cleaning.py。
验证方法:
- 观察输出:
2024-01-03的缺失值已由前一日数据填充(open=101.5, volume=1200000); 2024-01-05的异常价格(500/510/490/505)和异常成交量(99999999)已被截断至正常边界;- 输出中不存在任何
NaN值; - 每条记录满足
low ≤ min(open, close) ≤ max(open, close) ≤ high。
通过条件:程序无报错运行完毕,输出5条记录且无缺失值,价格逻辑校验全部通过。
预期输出
date open high low close volume
0 2024-01-01 100.00 102.00 99.00 101.00 1.000000e+06
1 2024-01-02 101.50 103.00 100.50 102.00 1.200000e+06
2 2024-01-03 101.50 104.00 101.00 102.50 1.200000e+06
3 2024-01-04 103.00 104.50 102.00 103.50 1.100000e+06
4 2024-01-05 103.00 104.50 102.00 103.50 1.100000e+06
清洗后剩余记录数: 5
常见问题
- 真实数据中首行即缺失:前向填充无法处理首行缺失,代码会直接丢弃该行,属预期行为。
- 停牌日缺失:若某交易日完全无数据,前向填充会用最近交易日数据填充,适合作为特征输入;若需严格剔除停牌日,可在清洗前先过滤。
- IQR边界过宽/过窄:可根据股票波动特性调整系数(默认1.5),但需保持全流程一致。