如何在前瞻性基础上使用Pandas滚动函数

26

假设我有一个时间序列:

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_*函数,例如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天,并将三列相加,但肯定有更加优雅的解决方案。


我找到了这个线程,大约是一年前的,所以看起来目前还没有“官方”的支持。但那里发布的中间解决方案也不能满足我边缘的需求(例如我的例子中第18/19行仍然为空白)。 - user2543645
Pandas 1.1增加了对FixedForwardWindowIndexer的前瞻窗口操作的“支持”,但是当我尝试将其应用于这些数据时,我的解释器崩溃了...也许它还不支持前瞻日期时间窗口,但最终会支持。 - cs95
5个回答

31

为什么不直接在反转的Series上操作(并颠倒答案):

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

2
聪明的回答,谢谢。虽然这只适用于您在一个范围内拥有所有日期的情况。在您的情况下,您没有指定频率。当您指定频率时,例如freq='D',此解决方案将不再起作用。因此,如果我有缺失数据或可变采样频率,则无法使用此方法。看起来这将是一个真正有价值的功能,需要正确实现。有人知道它是否在计划中吗? - user2543645
@user2543645 我在这里创建了一个问题(https://github.com/pydata/pandas/issues/6772)。我没有意识到在某些情况下这不起作用,很有趣。您可以先重新采样(以获得一致的频率),然后再反向滚动吗? - Andy Hayden
我认为这应该可以工作,但前提是您在要聚合的频率上重新采样。如果您的数据稀疏,这可能会显着增加您的数据集大小。例如,如果您有几天的每小时观测数据收集在不同的年份中,您将不得不添加数千个空值。如果有一种更本地的支持方式,能够真正像rolling_*一样运行,只是在另一个方向上,那就太好了。 - user2543645

12
Pandas最近增加了一个新功能,使您能够实现前向滚动。您需要升级到Pandas 1.1.0才能获得新功能。 新功能链接。
indexer = pd.api.indexers.FixedForwardWindowIndexer(window_size=3)
ts.rolling(window=indexer, min_periods=1).sum()

1
只有在您拥有一系列连续的日期时,此方法才有效。您不能使用rolling()可以使用的“#D”窗口。 - geominded

9

我曾苦苦思索,后来发现了一种使用shift的简单方法。

如果想要计算接下来10个期间的滚动总和,请尝试:

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

我们使用 shift 函数使得 "OtherCol" 在比它原本所在的行数上提前了 10 行,然后对前面的 10 行进行滚动求和。由于我们进行了 shift 操作,之前的 10 行实际上是未经过 shift 的列的未来 10 行。 :)


2
这似乎是一个不错的方法,但我认为你会失去最初的10行,是吗? - loco.loop

1
也许你可以尝试使用“瓶颈”模块。当“ts”很大时,“瓶颈”比“pandas”快得多。
import bottleneck as bn
result = bn.move_sum(ts[::-1], window=3, min_count=1)[::-1]

bottleneck 还有其他滚动函数,例如 move_maxmove_argminmove_rank


0

尝试使用这个来实现滚动窗口大小为3:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接