3、Pandas金融数据处理基础:Series与DataFrame、时间索引创建、数据读取与写入(CSV/Excel)、数据清洗与缺失值处理

做量化金融,说白了就是跟数据打交道。你策略再牛,模型再花哨,数据一塌糊涂,全白搭。我入行那会儿,第一个项目就是处理一堆从Wind导出的日频数据,格式乱得让人头皮发麻。那时候我就明白了一个道理:数据清洗占掉你80%的时间,剩下20%才是建模。所以这一章,咱们把Pandas这个吃饭的家伙彻底搞透。

3.1 Series与DataFrame:金融数据的左膀右臂

Pandas里就两个核心数据结构:Series和DataFrame。Series就是一列带标签的数据,你可以把它想象成Excel里的一列,但比那玩意儿灵活多了。DataFrame呢,就是多列Series拼起来的表格,金融数据基本都是这格式。

先看Series怎么用。我习惯把Series叫做“带索引的一维数组”。

import pandas as pd
import numpy as np

# 创建一个收盘价序列
close_prices = pd.Series([100.5, 102.3, 101.8, 103.6, 105.2],
                         index=['2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05', '2024-01-08'],
                         name='close')
print(close_prices)

你看,索引和值是一一对应的。你想想看,如果只用Python列表,你得自己维护一个日期列表和一个价格列表,多麻烦。Series把这事儿给你封装好了。

DataFrame就更常用了。我处理股票数据时,通常会把开盘价、收盘价、最高价、最低价、成交量都塞进一个DataFrame里。

# 创建一个简单的OHLCV DataFrame
data = {
    'open': [100.0, 102.0, 101.5, 103.0, 104.8],
    'high': [101.2, 103.5, 102.8, 104.2, 106.0],
    'low':  [99.8, 101.5, 100.9, 102.5, 103.9],
    'close':[100.5, 102.3, 101.8, 103.6, 105.2],
    'volume':[10000, 12000, 11000, 13000, 15000]
}
df = pd.DataFrame(data, index=['2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05', '2024-01-08'])
print(df)
我的小习惯:创建DataFrame时,我总喜欢先把索引设好。很多新手先创建DataFrame,再单独设索引,容易搞乱。一步到位最省心。

3.2 时间索引创建:金融数据的灵魂

金融数据跟时间绑定的。没有时间戳的数据,就像没有坐标的地图,毫无意义。Pandas处理时间序列的能力,说实话,是我见过所有Python库里最强的。

我个人最常用的是pd.to_datetime()pd.date_range()。前者把字符串转成时间对象,后者批量生成时间序列。

# 把字符串索引转成时间索引
df.index = pd.to_datetime(df.index)
print(df.index)
# 输出:DatetimeIndex(['2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05', '2024-01-08'], dtype='datetime64[ns]', freq=None)

# 批量生成交易日序列(跳过周末)
trade_dates = pd.date_range(start='2024-01-02', end='2024-01-31', freq='B')  # 'B'代表工作日
print(trade_dates[:10])

嗯,这里要注意:freq='B'只跳过周六周日,不处理法定节假日。如果你做A股回测,得用pd.offsets.CustomBusinessDay配合节假日日历。我曾经因为这个疏忽,回测结果差了3个点,找bug找了一整天。

避坑指南:我曾经在回测时直接用'B'频率生成日期,结果发现春节、国庆这些长假数据全对不上。后来改用pd.offsets.CustomBusinessDay(calendar=ChinaCalendar)才搞定。A股交易日历一定要自己维护一份。

3.3 数据读取与写入:CSV和Excel

实际工作中,数据来源五花八门。最常见的就是CSV和Excel。Pandas的read_csvread_excel功能强大,但参数多到让人眼花。我挑几个最常用的说说。

先看CSV读取。我处理过的金融数据CSV,十有八九第一列是日期。

# 读取CSV文件,指定日期列为索引
df = pd.read_csv('stock_data.csv', 
                 index_col=0,        # 第一列作为索引
                 parse_dates=True,   # 自动解析日期
                 encoding='utf-8')   # 防止中文乱码
print(df.head())

Excel读取也类似,但要注意sheet名。

# 读取Excel文件
df = pd.read_excel('stock_data.xlsx', 
                   sheet_name='Sheet1', 
                   index_col=0,
                   parse_dates=True)

写入数据时,我习惯把时间索引先转成字符串再存,这样其他软件打开时不会乱。

# 写入CSV
df.to_csv('cleaned_data.csv', encoding='utf-8-sig')  # utf-8-sig防止Excel打开乱码

# 写入Excel
df.to_excel('cleaned_data.xlsx', sheet_name='cleaned')
我的经验:保存CSV时用encoding='utf-8-sig',这样Excel打开中文不会乱码。我刚开始用utf-8,发给同事后他说全是乱码,尴尬得很。

3.4 数据清洗与缺失值处理:脏活累活我来干

真实世界的金融数据,没有一份是干净的。缺失值、异常值、重复值,应有尽有。数据清洗占掉我80%的时间,这话一点不夸张。

先看缺失值。Pandas里用NaN表示缺失。处理方式就三种:删除、填充、插值。

# 检查缺失值
print(df.isnull().sum())  # 每列缺失值数量

# 方法一:删除缺失值
df_drop = df.dropna()  # 删除任何包含NaN的行
df_drop_col = df.dropna(axis=1)  # 删除任何包含NaN的列

# 方法二:填充缺失值
df_fill = df.fillna(method='ffill')  # 用前一个值填充(前向填充)
df_fill_bfill = df.fillna(method='bfill')  # 用后一个值填充(后向填充)

# 方法三:插值(更平滑)
df_interp = df.interpolate(method='linear')  # 线性插值

你想想看,股票停牌时,那几天的数据全是NaN。直接删掉?不行,会破坏时间序列的连续性。我一般用前向填充,因为停牌期间价格不变,用前一个交易日的数据最合理。

但如果是分钟数据,某个时刻缺失了,线性插值可能更合适。这得看具体情况。

避坑指南:我曾经在回测时用了dropna(),结果把停牌日全删了。回测曲线漂亮得很,实盘一跑就崩。后来才发现,停牌日的数据不能删,得用前向填充。记住:金融数据的时间连续性比数据完整性更重要

除了缺失值,重复值也得处理。有时候数据源会重复推送同一时刻的数据。

# 检查重复值
print(df.duplicated().sum())

# 删除重复值(保留第一个)
df_unique = df.drop_duplicates(keep='first')

异常值处理更考验经验。我一般用Z-score或者IQR方法。

# 用Z-score检测异常值(假设数据服从正态分布)
from scipy import stats
z_scores = np.abs(stats.zscore(df['close']))
df_no_outlier = df[(z_scores < 3)]  # 保留Z-score小于3的数据

# 用IQR方法(更稳健)
Q1 = df['close'].quantile(0.25)
Q3 = df['close'].quantile(0.75)
IQR = Q3 - Q1
df_no_outlier_iqr = df[(df['close'] >= Q1 - 1.5*IQR) & (df['close'] <= Q3 + 1.5*IQR)]

我个人更偏爱IQR方法,因为它不受极端值影响。Z-score对极端值太敏感,有时候会把正常波动误判为异常。

3.5 本章知识体系总览

下面这张图,把本章的核心知识点串起来了。你可以把它当作一个速查地图,以后遇到数据处理问题,先看这张图找思路。

Pandas金融数据处理知识体系 Pandas金融数据处理 Series与DataFrame • Series: 一维带标签数组 • DataFrame: 二维表格结构 • 索引与值的一一对应 时间索引创建 • pd.to_datetime() • pd.date_range() • 工作日频率与节假日处理 数据读取与写入 • read_csv / read_excel • to_csv / to_excel • 编码与索引设置 数据清洗与缺失值 • 缺失值: dropna / fillna / interpolate • 重复值: drop_duplicates • 异常值: Z-score / IQR方法 核心原则:数据质量决定模型上限,清洗占工作量的80%

这一章的内容,说白了就是让你学会跟数据“对话”。你掌握了Series和DataFrame,就等于有了工具箱;学会了时间索引,就等于有了指南针;会读写数据,就等于有了原材料;能清洗数据,就等于有了打磨工具。四者缺一不可。

我见过太多人,模型写得花里胡哨,结果数据预处理一塌糊涂。记住:垃圾进,垃圾出。金融时序预测,数据质量永远是第一位的。

本章核心要点:
  • Series是一维带标签数组,DataFrame是二维表格,金融数据首选DataFrame
  • 时间索引用pd.to_datetime()pd.date_range()创建,注意节假日处理
  • CSV读写用read_csv/to_csv,Excel用read_excel/to_excel,编码用utf-8-sig
  • 缺失值处理:金融数据优先用前向填充(ffill),保持时间连续性
  • 异常值检测:IQR方法比Z-score更稳健,适合金融数据

专注资料整理