4. Pandas数据分析基础:Series与DataFrame、数据索引与选择、数据合并与连接、分组与聚合操作、时间序列数据处理

做量化交易,说白了就是跟数据打交道。K线数据、订单簿、财务指标……这些数据进了Python,最终都会变成Pandas里的两种核心结构——Series和DataFrame。我刚开始接触量化时,觉得Pandas就是个加强版的Excel,后来踩了不少坑才明白,它的设计哲学远比Excel要深。

4.1 Series与DataFrame:从一维到二维

Series就是一列带标签的数据。你可以把它想象成一个字典,但比字典更灵活。我习惯把Series叫做“带索引的数组”。

import pandas as pd
import numpy as np

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

DataFrame则是多列Series拼起来的表格。每一列可以有不同的数据类型——价格是浮点数,成交量是整数,股票代码是字符串。这在量化场景里太常见了。

# 创建一个DataFrame
data = {
    'open': [100, 102, 101, 105],
    'high': [103, 104, 103, 108],
    'low': [99, 101, 100, 104],
    'close': [102, 101, 105, 107],
    'volume': [10000, 12000, 11000, 15000]
}
df = pd.DataFrame(data, index=['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04'])
print(df)
我的习惯:创建DataFrame时,我总会显式指定index为时间戳。这样后续做时间序列分析会省很多事。曾经有个项目,同事没设索引,结果做滚动计算时各种报错,排查了半天。

4.2 数据索引与选择:别再用Excel的思维了

刚转Python时,我总想着用行号去取数据。但在Pandas里,标签索引才是王道。

Pandas提供了两种索引方式:

  • loc:基于标签名选择
  • iloc:基于整数位置选择
# loc:按标签取
print(df.loc['2024-01-02'])  # 取某一行
print(df.loc['2024-01-02', 'close'])  # 取某个单元格

# iloc:按位置取
print(df.iloc[1])  # 第二行
print(df.iloc[1, 3])  # 第二行第四列

# 布尔索引:筛选出收盘价大于102的行
print(df[df['close'] > 102])
我曾经踩过的坑:用链式赋值(比如df[df['close'] > 102]['volume'] = 0)去修改数据。这会产生一个副本,原数据根本没变。后来我改用df.loc[df['close'] > 102, 'volume'] = 0,才彻底解决。

4.3 数据合并与连接:把碎片拼起来

量化交易中,数据很少是完整的。你可能需要把不同时间段的K线拼起来,或者把股票数据和财务数据合并。Pandas提供了三种主要方式:

方法 用途 类比SQL
concat 简单拼接,按行或按列 UNION ALL
merge 类似数据库的JOIN JOIN
join 基于索引的合并 索引JOIN
# concat:把两个时间段的数据拼起来
df1 = pd.DataFrame({'close': [100, 102]}, index=['2024-01-01', '2024-01-02'])
df2 = pd.DataFrame({'close': [103, 105]}, index=['2024-01-03', '2024-01-04'])
combined = pd.concat([df1, df2])

# merge:类似SQL的inner join
fundamentals = pd.DataFrame({
    'stock': ['AAPL', 'GOOGL'],
    'pe_ratio': [25, 30]
})
prices = pd.DataFrame({
    'stock': ['AAPL', 'GOOGL', 'MSFT'],
    'close': [150, 140, 300]
})
merged = pd.merge(prices, fundamentals, on='stock', how='left')
我的经验:做merge时,我习惯先检查两边的key是否有重复值。曾经有一次merge后数据量翻了好几倍,就是因为key有重复,产生了笛卡尔积。用df.duplicated()检查一下,能省很多事。

4.4 分组与聚合操作:从细节看全局

量化分析中,我们经常需要按股票、按行业、按月份做统计。groupby就是干这个的。

# 按股票代码分组,计算每只股票的平均收盘价
df_grouped = df.groupby('stock')['close'].mean()

# 多级分组:按行业和年份
df_multi = df.groupby(['sector', 'year']).agg({
    'close': ['mean', 'std'],
    'volume': 'sum'
})

你想想看,如果不用groupby,你得写多少循环?Pandas的groupby背后用的是C语言实现的哈希分组,速度比纯Python循环快几十倍。

核心要点:groupby之后一定要跟一个聚合函数(mean、sum、count等),否则返回的是一个GroupBy对象,不是DataFrame。我刚开始学的时候,经常忘了加聚合函数,打印出来一堆看不懂的东西。

4.5 时间序列数据处理:量化交易的核心

量化交易的数据,90%以上都是时间序列。Pandas对时间序列的支持,是我选择它的最大原因。

重采样(resample)是最常用的操作。把1分钟K线变成5分钟K线,或者把日线变成周线,一行代码搞定。

# 生成1分钟K线数据
idx = pd.date_range('2024-01-01', periods=100, freq='1min')
ts = pd.Series(np.random.randn(100), index=idx)

# 重采样为5分钟K线,取收盘价
resampled = ts.resample('5min').last()

# 计算滚动均值
rolling_mean = ts.rolling(window=5).mean()

时间偏移在策略回测中也很常见。比如计算过去20天的平均成交量:

# 计算过去5天的滚动平均成交量
df['volume_ma5'] = df['volume'].rolling(window=5).mean()

# 计算收益率(shift用于对齐)
df['return'] = df['close'].pct_change()

# 计算未来5天的最高价(shift(-5))
df['future_high_5'] = df['high'].shift(-5).rolling(window=5).max()
注意:用shift做未来数据对齐时,一定要小心未来函数。我曾经在回测中用了shift(-1)去预测第二天的涨跌,结果回测曲线漂亮得不像话,实盘却亏得一塌糊涂。后来才发现,shift(-1)泄露了未来信息。

知识体系总览

下面这张图,是我自己总结的Pandas在量化交易中的核心脉络。你可以把它当作一个速查地图:

Pandas量化数据分析核心脉络 Pandas 数据结构 Series(一维) DataFrame(二维) 索引与选择(loc/iloc) 布尔索引与条件筛选 合并与连接(merge/concat) 分组与聚合(groupby) 时间序列数据处理 重采样(resample) 滚动窗口(rolling) 时间偏移(shift)

嗯,这张图基本把本章的核心内容串起来了。从数据结构出发,到数据操作,最后落到时间序列这个量化交易的核心场景。你可以在实际项目中,对照这张图去梳理自己的数据处理流程。

总结一下:Series和DataFrame是基础,索引选择是日常操作,合并连接是数据整合的关键,分组聚合是统计分析的利器,时间序列处理则是量化交易的灵魂。把这五块吃透了,Pandas这块就算入门了。

专注资料整理