在使用时间序列时,使用pandas滚动窗口函数时请使用center参数。

17

我正在尝试在pandas滚动函数中设置center=True,用于时间序列:

import pandas as pd
series = pd.Series(1, index = pd.date_range('2014-01-01', '2014-04-01', freq = 'D'))
series.rolling('7D', min_periods=1, center=True, closed='left')

但是输出结果是:
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-6-6b30c16a2d12> in <module>()
      1 import pandas as pd
      2 series = pd.Series(1, index = pd.date_range('2014-01-01', '2014-04-01', freq = 'D'))
----> 3 series.rolling('7D', min_periods=1, center=True, closed='left')

~\Anaconda3\lib\site-packages\pandas\core\generic.py in rolling(self, window, min_periods, freq, center, win_type, on, axis, closed)
   6193                                    min_periods=min_periods, freq=freq,
   6194                                    center=center, win_type=win_type,
-> 6195                                    on=on, axis=axis, closed=closed)
   6196 
   6197         cls.rolling = rolling

~\Anaconda3\lib\site-packages\pandas\core\window.py in rolling(obj, win_type, **kwds)
   2050         return Window(obj, win_type=win_type, **kwds)
   2051 
-> 2052     return Rolling(obj, **kwds)
   2053 
   2054 

~\Anaconda3\lib\site-packages\pandas\core\window.py in __init__(self, obj, window, min_periods, freq, center, win_type, axis, on, closed, **kwargs)
     84         self.win_freq = None
     85         self.axis = obj._get_axis_number(axis) if axis is not None else None
---> 86         self.validate()
     87 
     88     @property

~\Anaconda3\lib\site-packages\pandas\core\window.py in validate(self)
   1090             # we don't allow center
   1091             if self.center:
-> 1092                 raise NotImplementedError("center is not implemented "
   1093                                           "for datetimelike and offset "
   1094                                           "based windows")

NotImplementedError: center is not implemented for datetimelike and offset based windows

期望的输出是由以下内容生成的:

import pandas as pd
series = pd.Series(1, index = pd.date_range('2014-01-01', '2014-04-01', freq = 'D'))
series.rolling(7, min_periods=1, center=True).sum().head(10)

2014-01-01    4.0
2014-01-02    5.0
2014-01-03    6.0
2014-01-04    7.0
2014-01-05    7.0
2014-01-06    7.0
2014-01-07    7.0
2014-01-08    7.0
2014-01-09    7.0
2014-01-10    7.0
Freq: D, dtype: float64

但是使用类似于时间戳的偏移量,可以简化我的其他代码(未在此处发布)。

是否有任何替代方案?

谢谢。


你应该将这个问题发布到 github,这不是 Stack Overflow 通常可以评论的内容。 - EdChum
如果您发布期望的输出,我们可以考虑另一种解决方案。 - Bharath M Shetty
3个回答

11

尝试以下内容(已测试使用 pandas==0.23.3):

series.rolling('7D', min_periods=1, closed='left').sum().shift(-84, freq='h')

这将在7天窗口内居中您的滚动总和(通过向左移动-3.5天),并允许您使用“类似日期时间”的字符串来定义窗口大小。请注意,shift()只接受整数作为参数,因此需按小时定义。

这将产生您想要的输出:

series.rolling('7D', min_periods=1, closed='left').sum().shift(-84, freq='h')['2014-01-01':].head(10)

2014-01-01 12:00:00    4.0
2014-01-02 12:00:00    5.0
2014-01-03 12:00:00    6.0
2014-01-04 12:00:00    7.0
2014-01-05 12:00:00    7.0
2014-01-06 12:00:00    7.0
2014-01-07 12:00:00    7.0
2014-01-08 12:00:00    7.0
2014-01-09 12:00:00    7.0
2014-01-10 12:00:00    7.0
Freq: D, dtype: float64

请注意,滚动求和被分配到7天窗口的中心(使用从午夜到午夜的时间戳),因此居中的时间戳包括“12:00:00”。

另一个选项(如您在问题末尾所示)是将数据重新采样以确保具有均匀的日期时间频率,然后使用整数作为窗口大小(window = 7)和center=True。但是,您指出您代码的其他部分受益于使用“datetimelike”字符串定义window,因此这个选项可能不是理想的选择。


抱歉,我不确定这是否有效。输入和输出的索引应该是相同的,在我的测试用例中并非如此。 - Brad Solomon
1
具体而言:如果数据的频率间隔不一致,即出现了跳过随机分钟的情况,我认为它会失败。在这种情况下,选择重新采样,但可能会导致空间复杂度增加。 - Brad Solomon
答案提供了所需的输出,并且如果从时间戳中删除HH:MM:SS,则索引匹配。这只是一个更高精度的居中窗口时间戳。但是,shift可能存在不一致的数据频率问题......我还没有测试过。对于均匀频率的重新采样将解决此问题,但会在缺失数据处插入“NaN”。如果窗口中有“NaN”,则sum()的结果将为“NaN”。但是,您可以使用min_periods来仍然返回窗口中仅有偶尔出现的“NaN”周期的总和。 - pjw

4

从pandas1.3版本开始,这是直接使用pandas完成的。

* 或将会如此(该工作已经合并,但截至今天,1.3版本尚未发布;我对下面的行进行了针对pandas主分支的测试)。

import pandas as pd
series = pd.Series(1, index = pd.date_range('2014-01-01', '2014-04-01', freq = 'D'))
series.rolling(7, min_periods=1, center=True).sum().head(10)

输出符合预期:

2014-01-01    4.0
2014-01-02    5.0
2014-01-03    6.0
2014-01-04    7.0
2014-01-05    7.0
2014-01-06    7.0
2014-01-07    7.0
2014-01-08    7.0
2014-01-09    7.0
2014-01-10    7.0
Freq: D, dtype: float64

1
您可以尝试对系列/数据框进行重新采样,以将偏移窗口转换为固定宽度窗口。
# Parameters 
window_timedelta = '7D'
resample_timedelta = '1D' 

# Convert offset to window size
window_size = pd.Timedelta(structure_duration) // pd.Timedelta(resample_timedelta)

# Resample serie
series_res = series.resample(resample_timedelta, on='datetime').first() 

# Perform the sum on the resampled serie
series_res['window_sum'] = series_res.rolling(window_size, center=True, min_periods=1).sum()

注意:重新采样中的第一个hack仅适用于您知道最多每天只有1个数据点的情况。如果超过了这个数量,您可以将其替换为“sum”或任何与您的数据相关的内容。
注意2:对于缺失日期引入的NaN值不会导致总和变为NaN,Pandas在求和时会忽略它们。

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