如何在前瞻性基础上使用Pandas的rolling_*函数

27 投票
5 回答
27368 浏览
提问于 2025-04-18 01:03

假设我有一个时间序列:

In[138] rng = pd.date_range('1/10/2011', periods=10, freq='D')
In[139] ts = pd.Series(randn(len(rng)), index=rng)
In[140]
Out[140]:
2011-01-10    0
2011-01-11    1
2011-01-12    2
2011-01-13    3
2011-01-14    4
2011-01-15    5
2011-01-16    6
2011-01-17    7
2011-01-18    8
2011-01-19    9
Freq: D, dtype: int64

如果我使用一些滚动函数,比如说滚动求和(rolling_sum),我可以得到我想要的效果,这种效果是向后看(即计算之前的数据):

In [157]: pd.rolling_sum(ts, window=3, min_periods=0)
Out[157]: 
2011-01-10     0
2011-01-11     1
2011-01-12     3
2011-01-13     6
2011-01-14     9
2011-01-15    12
2011-01-16    15
2011-01-17    18
2011-01-18    21
2011-01-19    24
Freq: D, dtype: float64

但是如果我想要做一个向前看的求和呢?我试过像这样做:

In [161]: pd.rolling_sum(ts.shift(-2, freq='D'), window=3, min_periods=0)
Out[161]: 
2011-01-08     0
2011-01-09     1
2011-01-10     3
2011-01-11     6
2011-01-12     9
2011-01-13    12
2011-01-14    15
2011-01-15    18
2011-01-16    21
2011-01-17    24
Freq: D, dtype: float64

但这并不是我想要的效果。我希望得到的结果是:

2011-01-10    3
2011-01-11    6
2011-01-12    9
2011-01-13    12
2011-01-14    15
2011-01-15    18
2011-01-16    21
2011-01-17    24
2011-01-18    17
2011-01-19    9

也就是说,我想要“当前”这一天加上接下来的两天的总和。现在的解决方案不够好,因为我很在意边缘的情况。我知道我可以通过设置两个额外的列,分别向后移动1天和2天,然后把这三列相加来手动解决这个问题,但我觉得应该有更优雅的解决办法。

5 个回答

0

试试这个,适用于一个大小为3的滑动窗口:

window = 3
ts.rolling(window).sum().shift(-window + 1)
1

你可以试试 bottleneck 这个模块。当 ts 很大的时候,bottleneck 的速度比 pandas 快很多。

import bottleneck as bn
result = bn.move_sum(ts[::-1], window=3, min_count=1)[::-1]

而且 bottleneck 还有其他的滚动函数,比如 move_maxmove_argminmove_rank

10

我之前也遇到过这个问题,后来发现用shift这个方法很简单。

如果你想要计算接下来10个周期的滚动总和,可以试试:

df['NewCol'] = df['OtherCol'].shift(-10).rolling(10, min_periods = 0).sum()

我们使用shift的原因是让“OtherCol”这一列的数据提前10行显示,这样我们就可以对之前的10行进行滚动求和。因为我们进行了位移,所以之前的10行实际上是未位移列的未来10行的数据。 :)

14

Pandas最近增加了一个新功能,让你可以实现向前看的滚动计算。你需要升级到pandas 1.1.0版本才能使用这个新功能

indexer = pd.api.indexers.FixedForwardWindowIndexer(window_size=3)
ts.rolling(window=indexer, min_periods=1).sum()
31

为什么不直接在反转后的序列上操作呢(然后再把结果反转回来):

In [11]: pd.rolling_sum(ts[::-1], window=3, min_periods=0)[::-1]
Out[11]:
2011-01-10     3
2011-01-11     6
2011-01-12     9
2011-01-13    12
2011-01-14    15
2011-01-15    18
2011-01-16    21
2011-01-17    24
2011-01-18    17
2011-01-19     9
Freq: D, dtype: float64

撰写回答