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。里面包含了日期、开盘价、收盘价、最高价、最低价、成交量这些字段。这就是我们做择时最原始的材料。

小提示: 如果你本地有CSV文件,用 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'改成了'收盘价(元)',用属性方式就抓瞎了。

选择行,可以用 lociloc

# 按标签选择(这里日期是索引)
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() 缺失值较少,且数据趋势明显
注意: 千万不要用均值填充金融时间序列!我见过有人把缺失的收盘价用过去30天均值填上,结果模型学到的全是假规律。金融数据有很强的时序依赖性,均值填充会破坏这种依赖关系。

我曾经处理过一只停牌半年的股票。复牌后第一天,数据直接跳空。如果用向前填充,那半年里的数据全是同一个价格,模型会以为这只股票半年没波动。后来我改用插值法,用复牌前后的价格做线性插值,效果好了很多。

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能吃的特征。嗯,那才是真正有意思的部分。


专注资料整理