4. Pandas进阶与因子计算:从数据清洗到因子构建
各位同学,欢迎来到第四章。说实话,前几章我们还在玩一些基础操作,像是给数据排排队、筛筛选选。但从这一章开始,我们要真正进入量化因子的核心战场了。
我个人习惯把Pandas比作一把瑞士军刀——平时切个水果还行,但真要上战场,你得知道怎么用它的每一把刀刃。滚动窗口、分组聚合、因子构建,这些就是量化分析中最锋利的几把刀。
4.1 DataFrame高级操作:别只会loc和iloc
很多新手拿到DataFrame,翻来覆去就是df.loc[]和df.iloc[]。嗯,这当然没错,但实战中远远不够。
条件筛选与布尔索引
我在项目中遇到过这样一个场景:需要筛选出某只股票在过去20天里,涨幅超过5%且成交量放大1.5倍以上的所有交易日。用链式条件写起来其实很简洁:
import pandas as pd
import numpy as np
# 模拟数据
dates = pd.date_range('2024-01-01', periods=100, freq='D')
df = pd.DataFrame({
'close': np.random.randn(100).cumsum() + 100,
'volume': np.random.randint(10000, 50000, 100)
}, index=dates)
# 计算涨跌幅和量比
df['return'] = df['close'].pct_change()
df['volume_ratio'] = df['volume'] / df['volume'].rolling(5).mean()
# 条件筛选
condition = (df['return'] > 0.05) & (df['volume_ratio'] > 1.5)
result = df[condition]
print(f"符合条件的交易日数: {len(result)}")
多级索引与stack/unstack
做因子分析时,数据往往是三维的:时间、股票、因子值。Pandas的多级索引就是为这个场景设计的。
# 构建多级索引DataFrame
arrays = [
['2024-01-01', '2024-01-01', '2024-01-02', '2024-01-02'],
['000001.SZ', '600001.SH', '000001.SZ', '600001.SH']
]
index = pd.MultiIndex.from_arrays(arrays, names=['date', 'stock'])
df_multi = pd.DataFrame({
'close': [10.5, 20.3, 10.8, 20.1],
'volume': [10000, 20000, 12000, 18000]
}, index=index)
# unstack:将股票从行索引转到列索引
df_unstacked = df_multi.unstack('stock')
print(df_unstacked)
说白了,unstack就是把数据从「长格式」变成「宽格式」。我刚开始做因子分析时,经常在这两个格式之间来回转换,后来发现——其实大部分计算在长格式下更高效。
4.2 滚动窗口计算:时间序列的显微镜
滚动窗口,说白了就是给数据加一个「滑动窗口」,只看窗口内的数据。这在量化里太常用了——移动平均、波动率、动量因子,全都离不开它。
基础滚动操作
# 20日滚动均值
df['ma_20'] = df['close'].rolling(window=20).mean()
# 滚动标准差(波动率)
df['volatility_20'] = df['close'].rolling(window=20).std()
# 滚动最大值(用于计算最高价突破)
df['high_20'] = df['close'].rolling(window=20).max()
rolling()默认会返回NaN,记得用dropna()或者fillna()处理。
自定义滚动函数
有时候内置的均值、标准差不够用。比如我想计算「过去20天里,涨幅超过3%的天数占比」,这就需要自定义函数了:
def up_ratio(series):
"""计算上涨比例"""
return (series > 0.03).mean()
df['up_ratio_20'] = df['return'].rolling(window=20).apply(up_ratio)
你想想看,这种自定义滚动函数,其实就是把「人脑的判断逻辑」翻译成了代码。我经常用这种方式来构建一些「非标准」的因子,效果往往出奇的好。
4.3 分组聚合:多股票场景下的必备技能
单只股票的分析只是热身。真正的量化策略,面对的是成百上千只股票。这时候,groupby就是你的救星。
按股票分组计算因子
# 假设我们有多个股票的数据
df_all = pd.DataFrame({
'stock': ['000001.SZ']*100 + ['600001.SH']*100,
'date': pd.date_range('2024-01-01', periods=100, freq='D').tolist() * 2,
'close': np.random.randn(200).cumsum() + 100,
'volume': np.random.randint(10000, 50000, 200)
})
# 按股票分组,计算每只股票的20日滚动均值
df_all['ma_20'] = df_all.groupby('stock')['close'].transform(
lambda x: x.rolling(20).mean()
)
这里有个关键点:transform和apply的区别。我刚开始用的时候也搞混过——transform返回的是和原DataFrame一样长度的结果,而apply返回的是聚合后的结果。做因子计算时,99%的情况用transform就够了。
多因子分组聚合
# 按行业和日期分组,计算行业内各因子的排名
df_all['rank_momentum'] = df_all.groupby(['industry', 'date'])['momentum'].rank(pct=True)
嗯,这里要注意:rank(pct=True)返回的是百分位排名,范围是0到1。我习惯用这个来做「截面标准化」——说白了,就是看某只股票在同行中处于什么位置。
4.4 因子构建实战:从想法到代码
好了,前面铺垫了这么多,终于到了最核心的部分——因子构建。我给大家演示一个完整的因子构建流程,从原始数据到最终因子值。
案例:构建「量价背离因子」
这个因子的逻辑很简单:当价格上涨但成交量萎缩时,说明上涨动力不足,后续可能反转。反之亦然。
def build_volume_price_divergence_factor(df, window=20):
"""
构建量价背离因子
正值表示量价背离(上涨缩量),负值表示量价配合
"""
# 计算价格变化方向
df['price_direction'] = np.sign(df['close'].pct_change(window))
# 计算成交量变化方向
df['volume_direction'] = np.sign(
df['volume'] / df['volume'].rolling(window).mean() - 1
)
# 量价背离因子 = 价格方向 × (-成交量方向)
# 上涨(+1)且缩量(-1) → 背离(+1)
# 上涨(+1)且放量(+1) → 配合(-1)
df['divergence_factor'] = df['price_direction'] * (-df['volume_direction'])
return df
# 应用因子
df = build_volume_price_divergence_factor(df)
print(df[['close', 'volume', 'divergence_factor']].tail(10))
因子标准化处理
构建完因子后,通常还需要做标准化处理,让不同股票的因子值具有可比性:
def standardize_factor(df, factor_name, group_col='date'):
"""
截面标准化:在每个时间截面上,将因子值转换为Z-score
"""
df[factor_name + '_zscore'] = df.groupby(group_col)[factor_name].transform(
lambda x: (x - x.mean()) / x.std()
)
return df
# 应用标准化
df = standardize_factor(df, 'divergence_factor')
你想想看,为什么要做截面标准化?因为不同股票的因子值分布可能完全不同。比如大盘股的波动率天然比小盘股低,如果不做标准化,你拿到的因子值其实没有可比性。
4.5 本章知识体系总览
为了让大家更直观地理解本章的知识结构,我画了一张图:
这张图把本章的四个核心模块串起来了。你从左上角开始,先掌握DataFrame的高级操作,然后学习滚动窗口计算,接着搞定分组聚合,最后把这些技能组合起来构建因子。每一步都是下一步的基础。
说实话,因子挖掘这个领域,入门容易精通难。但只要你把Pandas的这些进阶操作练熟了,至少能解决80%的日常需求。剩下的20%,靠的是你对市场的理解和不断的试错。
好了,这一章的内容就到这里。记住:代码可以复制,但经验需要积累。多动手、多思考,你也能成为因子挖掘的高手。
公众号:蓝海资料掘金营,微信deep3321