4. Pandas基础:Series与DataFrame、数据读取与写入、数据清洗与预处理、时间序列处理

做量化选股,说白了就是跟数据打交道。你想想看,每天几千只股票,每只股票又有开盘价、收盘价、成交量、换手率……这些数据怎么存?怎么算?怎么清洗?

Pandas就是干这个的。它是Python生态里最核心的数据分析库,没有之一。我个人习惯把Pandas比作「Excel的超级增强版」——但比Excel快得多,也灵活得多。

核心知识点速览:

  • Series:一维数据,类似带标签的列表
  • DataFrame:二维表格,Pandas的核心数据结构
  • 数据读取与写入:CSV、Excel、数据库
  • 数据清洗:缺失值、重复值、异常值处理
  • 时间序列:日期索引、重采样、滚动计算
Pandas 量化数据处理核心 数据结构 Series(一维) DataFrame(二维) 数据读写 read_csv / to_csv read_excel / to_excel 数据清洗 缺失值处理 重复值 / 异常值 时间序列 重采样 / 滚动计算

4.1 Series与DataFrame:Pandas的两大基石

先说说Series。它就是一列数据,每个值都有一个索引。我刚开始学的时候觉得这玩意儿不就是个列表吗?后来才发现,索引才是Series的灵魂。

import pandas as pd

# 创建一个Series
s = pd.Series([100, 102, 101, 105], index=['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04'])
print(s)

输出结果长这样:

2024-01-01    100
2024-01-02    102
2024-01-03    101
2024-01-04    105
dtype: int64

DataFrame就好理解了——多个Series拼在一起,形成一个二维表格。每一列是一个Series,每一行是一条记录。

# 创建一个DataFrame
df = pd.DataFrame({
    '股票代码': ['000001', '000002', '000003'],
    '收盘价': [10.5, 22.3, 15.8],
    '成交量': [1000000, 500000, 800000]
})
print(df)

我的小习惯:创建DataFrame时,我一般用字典传列名和数据,这样可读性最好。如果数据量特别大(比如上百万行),我会用numpy数组来构造,速度能快不少。

4.2 数据读取与写入:跟文件打交道

做量化选股,数据来源五花八门。最常见的就是CSV文件,比如从Tushare、Wind导出的日线数据。

# 读取CSV文件
df = pd.read_csv('stock_data.csv', encoding='utf-8')

# 写入CSV文件
df.to_csv('cleaned_data.csv', index=False, encoding='utf-8-sig')

这里有个坑,我踩过好几次。CSV文件的编码问题——Windows下导出的CSV经常是gbk编码,而Python默认是utf-8。读不出来的时候,试试 encoding='gbk' 或者 encoding='gb2312'

Excel文件也常见,尤其是从券商系统导出的数据:

# 读取Excel文件
df = pd.read_excel('trade_data.xlsx', sheet_name='日线数据')

# 写入Excel文件
df.to_excel('output.xlsx', sheet_name='清洗结果', index=False)

注意:读取Excel需要安装openpyxl或xlrd库。我建议用openpyxl,它对.xlsx格式支持更好。曾经有一次我用了老版本的xlrd,结果死活读不出新格式的Excel文件,折腾了半天才发现是版本问题。

4.3 数据清洗与预处理:脏数据是量化的大敌

真实世界的数据,没有一个是干净的。我见过太多人拿着脏数据跑回测,结果策略收益曲线漂亮得不行,实盘一跑就崩——因为数据里全是未来函数和填充值。

先看缺失值处理:

# 检查缺失值
print(df.isnull().sum())

# 删除缺失值
df_clean = df.dropna()

# 填充缺失值
df_filled = df.fillna(method='ffill')  # 用前一个值填充
df_filled = df.fillna(df.mean())       # 用均值填充

重复值也得处理。有些数据源会重复推送同一时刻的数据:

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

# 删除重复行
df_unique = df.drop_duplicates()

异常值检测,我一般用3σ原则或者IQR方法:

# 用IQR方法检测异常值
Q1 = df['收盘价'].quantile(0.25)
Q3 = df['收盘价'].quantile(0.75)
IQR = Q3 - Q1

# 筛选正常范围的数据
df_normal = df[(df['收盘价'] >= Q1 - 1.5 * IQR) & (df['收盘价'] <= Q3 + 1.5 * IQR)]

避坑指南:我曾经处理过一份港股数据,某只股票因为停牌,连续30天收盘价都是0。如果用均值填充,整个策略就废了。正确的做法是:先判断停牌原因,再决定是删除还是用复牌后的第一个价格填充。

4.4 时间序列处理:量化选股的核心

股票数据天然就是时间序列。Pandas对时间序列的支持,是我最喜欢它的地方。

先把日期列转成时间索引:

# 将日期列转为datetime类型
df['日期'] = pd.to_datetime(df['日期'])

# 设置为索引
df.set_index('日期', inplace=True)

重采样——把日线数据转成周线或月线,做策略回测时经常用到:

# 日线转周线,取每周最后一个交易日的收盘价
weekly_data = df['收盘价'].resample('W').last()

# 日线转月线,取每月平均收盘价
monthly_data = df['收盘价'].resample('M').mean()

滚动计算——计算移动平均线、布林带这些技术指标:

# 计算5日移动平均线
df['MA5'] = df['收盘价'].rolling(window=5).mean()

# 计算20日标准差(布林带用)
df['STD20'] = df['收盘价'].rolling(window=20).std()

我的经验:做滚动计算时,注意窗口大小。5日均线用5个交易日,但遇到节假日停牌,实际可能是7个自然日。如果你需要精确的交易日窗口,可以用 min_periods 参数来控制最少有效数据量。

时间偏移也很实用,比如计算前一天的收益率:

# 计算日收益率
df['收益率'] = df['收盘价'].pct_change()

# 计算前一天的收盘价
df['前日收盘'] = df['收盘价'].shift(1)

嗯,到这里Pandas的基础内容就差不多了。这些操作在量化选股中几乎每天都会用到——从数据读取到清洗,再到时间序列处理,每一步都马虎不得。

记住一句话:数据质量决定了策略的上限。Pandas就是你手里最趁手的工具,用好了它,后面的策略实现才能事半功倍。


公众号:蓝海资料掘金营,微信deep3321