3. Python金融数据处理基础:Pandas读取股票数据、DataFrame基础操作、处理缺失值
做量化择时,第一步就是跟数据打交道。你想想看,没有数据,再牛的LSTM模型也只能干瞪眼。这一章,我们就来聊聊怎么用Python把股票数据拿下来,然后收拾得干干净净,喂给模型吃。
我个人习惯用Pandas来处理金融时间序列。这玩意儿在量化圈里,基本人手必备。我刚开始做量化那会儿,还傻乎乎地用Excel手动整理数据,后来发现Pandas一行代码能顶我半小时的活。嗯,真香。
3.1 用Pandas读取股票数据
读取数据的方式有很多种。你可以从本地CSV文件读,也可以直接从Tushare、AKShare这些数据接口拉。我这里用AKShare举个例子,因为它免费,而且数据源比较全。
import pandas as pd
import akshare as ak
# 获取上证50指数历史数据
df = ak.stock_zh_index_daily(symbol="sh000016")
print(df.head())
跑完这段代码,你会看到一个DataFrame。里面包含了日期、开盘价、收盘价、最高价、最低价、成交量这些字段。这就是我们做择时最原始的材料。
pd.read_csv('data.csv', parse_dates=['date']) 就能读进来。记得把日期列解析成时间格式,后面做时间序列分析会方便很多。
我在项目中遇到过一个问题:从不同数据源拿到的字段名不一样。有的叫'close',有的叫'收盘价'。我的建议是,一开始就统一字段名,比如都用英文小写。省得后面写代码时来回切换,容易搞混。
3.2 DataFrame基础操作
DataFrame说白了就是一个表格。但它的操作比Excel灵活太多了。我们来看看几个最常用的操作。
3.2.1 查看数据概貌
# 查看前5行
print(df.head())
# 查看数据基本信息
print(df.info())
# 查看统计摘要
print(df.describe())
info() 会告诉你每列的数据类型,有没有缺失值。describe() 则给出均值、标准差、分位数这些统计量。我每次拿到新数据,第一件事就是跑这两个函数。为什么?因为能快速发现异常。比如某只股票的最高价突然变成0,那八成是数据出问题了。
3.2.2 选择与切片
选择数据列,有两种方式:
# 方式一:用列名
close = df['close']
# 方式二:用属性(列名不能有空格或特殊字符)
close = df.close
我个人更推荐方式一。因为当列名里有中文或特殊字符时,方式二会报错。你想想看,万一哪天数据源把'close'改成了'收盘价(元)',用属性方式就抓瞎了。
选择行,可以用 loc 和 iloc:
# 按标签选择(这里日期是索引)
df.loc['2024-01-01':'2024-01-10']
# 按位置选择
df.iloc[0:10] # 前10行
这里有个坑:loc 是闭区间,包含结束点;iloc 是左闭右开,不包含结束点。我曾经因为这个bug,回测时多算了一天数据,导致策略信号全乱了。嗯,细节决定成败。
3.2.3 添加与删除列
做量化择时,我们经常需要计算一些衍生指标,比如收益率、移动平均线。
# 计算日收益率
df['return'] = df['close'].pct_change()
# 计算5日均线
df['ma5'] = df['close'].rolling(window=5).mean()
# 删除不需要的列
df.drop(['volume'], axis=1, inplace=True)
pct_change() 和 rolling() 这两个函数,在量化分析里出场率极高。说白了,一个算变化率,一个算滑动窗口统计量。后面我们做LSTM特征工程时,还会反复用到它们。
3.3 处理缺失值
金融数据里出现缺失值,太常见了。停牌、节假日、数据源抽风,都可能导致某天数据为空。如果不处理,模型训练时会直接报错,或者学到错误规律。
3.3.1 检测缺失值
# 检查每列缺失值数量
print(df.isnull().sum())
# 查看缺失值占比
print(df.isnull().mean() * 100)
我一般先看缺失值占比。如果超过20%,我会怀疑这个数据源是不是有问题。如果只有零星几个,那就直接处理掉。
3.3.2 处理方式
处理缺失值,主要有三种思路:
| 方法 | 代码 | 适用场景 |
|---|---|---|
| 删除 | df.dropna() |
缺失值很少,且不影响时间连续性 |
| 向前填充 | df.fillna(method='ffill') |
停牌数据,用上一个交易日数据填充 |
| 插值 | df.interpolate() |
缺失值较少,且数据趋势明显 |
我曾经处理过一只停牌半年的股票。复牌后第一天,数据直接跳空。如果用向前填充,那半年里的数据全是同一个价格,模型会以为这只股票半年没波动。后来我改用插值法,用复牌前后的价格做线性插值,效果好了很多。
3.3.3 实战中的选择
对于LSTM模型,我个人建议:
- 如果缺失值连续超过5天,直接删除这段数据。因为LSTM依赖时间步之间的连续性,缺失太多会影响模型学习。
- 如果缺失值只有1-2天,用向前填充。因为金融数据有惯性,昨天的价格对今天最有参考价值。
- 永远不要在训练集和测试集之间做填充。也就是说,先切分数据,再分别处理缺失值。否则会造成数据泄露,回测结果虚高。
# 一个完整的处理流程
def clean_data(df):
# 先按日期排序
df.sort_index(inplace=True)
# 删除全部为空的列
df.dropna(axis=1, how='all', inplace=True)
# 对价格列做向前填充
price_cols = ['open', 'high', 'low', 'close']
df[price_cols] = df[price_cols].fillna(method='ffill')
# 如果还有缺失,直接删除
df.dropna(inplace=True)
return df
这个函数是我自己项目里一直在用的。逻辑很简单:先排序,再删全空列,然后填充价格,最后删残余缺失。你拿过去就能用,基本不会出问题。
3.4 本章小结
这一章我们聊了Pandas读取股票数据、DataFrame的基础操作,以及缺失值处理。说白了,就是教你怎么把原始数据变成干净、可用的时间序列。这些操作看起来基础,但却是整个量化择时流程的基石。我见过太多人模型调得飞起,结果数据预处理一塌糊涂,最后回测结果全是假的。
下一章,我们会把这些干净数据做成LSTM能吃的特征。嗯,那才是真正有意思的部分。