在带股票-日期多层索引的Pandas中计算历史前置均值
问题:按股票分组计算前置日期的P/Sales均值?
问题背景
我有一个多层索引的DataFrame,第一层为股票代码(Ticker),第二层为日期,示例代码如下:
import pandas as pd import numpy as np arrays = [ np.array(["MSFT", "MSFT", "MSFT", "MSFT", "GOOG", "GOOG", "GOOG", "GOOG"]), np.array([pd.to_datetime("2022-04-05"), pd.to_datetime("2022-04-06"), pd.to_datetime("2022-04-07"), pd.to_datetime("2022-04-08"),pd.to_datetime("2022-04-05"), pd.to_datetime("2022-04-06"), pd.to_datetime("2022-04-07"), pd.to_datetime("2022-04-08")]), ] df = pd.DataFrame(np.random.randn(8, 1), index=arrays, columns={"P/Sales"}) df
生成的DataFrame如下:
| 股票代码 | 日期 | P/Sales |
|---|---|---|
| MSFT | 2022-04-05 00:00:00 | -1.1648041378350011 |
| MSFT | 2022-04-06 00:00:00 | -2.352493882690841 |
| MSFT | 2022-04-07 00:00:00 | 0.6047960443400636 |
| MSFT | 2022-04-08 00:00:00 | 1.533852852883288 |
| GOOG | 2022-04-05 00:00:00 | 2.0738583230363195 |
| GOOG | 2022-04-06 00:00:00 | -1.887463559617715 |
| GOOG | 2022-04-07 00:00:00 | 0.49868465858420324 |
| GOOG | 2022-04-08 00:00:00 | 0.900527823711884 |
需求说明
我希望新增一列,存储对应股票当前日期之前所有日期的P/Sales列均值。目前通过嵌套循环实现该需求,但嵌套循环效率较低,想使用更高效的方法(比如expanding.mean()),但无法正确编写按股票分组且仅计算前置日期均值的语法。
当前实现代码
for ticker in df.reset_index()['Ticker'].unique(): for date in df.loc[ticker].index: filtered_df = df.loc[ticker].loc[(df.loc[ticker].index < date)] mean = np.mean(np.asarray(filtered_df['P/Sales'])) df.loc[(ticker,date),"Mean"] = mean df
运行后得到的结果如下:
| 股票代码 | 日期 | P/Sales | 均值 |
|---|---|---|---|
| MSFT | 2022-04-05 00:00:00 | -1.4361186774548342 | NaN |
| MSFT | 2022-04-06 00:00:00 | 0.5922672022039192 | -1.4361186774548342 |
| MSFT | 2022-04-07 00:00:00 | 0.4844687977324626 | -0.42192573762545754 |
| MSFT | 2022-04-08 00:00:00 | -0.4125514467122677 | -0.11979422583948417 |
| GOOG | 2022-04-05 00:00:00 | -0.30860020353979284 | NaN |
| GOOG | 2022-04-06 00:00:00 | 0.1189685515177034 | -0.30860020353979284 |
| GOOG | 2022-04-07 00:00:00 | 1.1730395696068878 | -0.09481582601104471 |
| GOOG | 2022-04-08 00:00:00 | -0.9778323004522275 | 0.3278026391949328 |
高效解决方案
可以通过按股票代码分组,结合expanding().mean()和shift()实现需求,完全替代嵌套循环,效率大幅提升:
# 按多层索引第一层(股票代码)分组,计算前置日期均值 df['Mean'] = df.groupby(level=0)['P/Sales'].expanding().mean().shift().reset_index(level=0, drop=True)
代码解析
groupby(level=0):针对多层索引的第一层(股票代码)分组,确保每个股票的计算独立进行expanding().mean():对每个分组计算从第一行到当前行的累积均值shift():将累积均值结果下移一行,这样当前行的均值就对应之前所有日期的均值,第一行自然变为NaN(符合无前置数据的逻辑)reset_index(level=0, drop=True):移除分组后新增的层级索引,保证结果能和原DataFrame的索引完美匹配
该方法运行结果和嵌套循环完全一致,但时间复杂度更低,尤其适合处理大规模数据集。
内容的提问来源于stack exchange,提问作者David Bell
相关产品推荐
相关产品推荐

