Pandas 按天滚动时间窗口而不是单个行

Posted

技术标签:

【中文标题】Pandas 按天滚动时间窗口而不是单个行【英文标题】:Pandas rolling time window by days instead of individual rows 【发布时间】:2021-12-08 18:07:30 【问题描述】:

我有一个大型数据集,我需要在 N 天的滚动时间窗口内计算一些统计数据。每天有多个条目。而且我需要计算 N 天时间范围内所有行的统计信息,即两端的每一天。

带有时间偏移的 pandas.DataFrame.rolling() 方法几乎可以工作。此方法的时间偏移向后看以包括 N 天时间范围内的所有天,但向前看,窗口按行滚动,即直到一天结束。

我已经编写了一个循环来执行此操作,但它在我的大型数据集上运行得非常慢。有没有一种有效的方法来使用rolling()、使用某种索引器或其他方式?

我需要计算的检验统计量是计数、均值和标准差。我修改后的例子:

#Create test dataframe:
import pandas as pd
l1=[1,2,3,4,5,6,7,8,9,10,11,12]
                                                            
ts=[pd.Timestamp('2017-01-01'),
    pd.Timestamp('2017-01-02'),
    pd.Timestamp('2017-01-03'),
    pd.Timestamp('2017-01-04'),
    pd.Timestamp('2017-01-05'),
    pd.Timestamp('2017-01-05'),
    pd.Timestamp('2017-01-05'),
    pd.Timestamp('2017-01-06'),
    pd.Timestamp('2017-01-06'),
    pd.Timestamp('2017-01-07'),
    pd.Timestamp('2017-01-07'),
    pd.Timestamp('2017-01-08')]

In [58]: df=pd.DataFrame('t':ts, 'c':l1, 'm':l1, 's':l1).set_index('t')
In [59]: df
Out[59]:
             c   m   s
t                    
2017-01-01   1   1   1
2017-01-02   2   2   2
2017-01-03   3   3   3
2017-01-04   4   4   4
2017-01-05   5   5   5
2017-01-05   6   6   6
2017-01-05   7   7   7
2017-01-06   8   8   8
2017-01-06   9   9   9
2017-01-07  10  10  10
2017-01-07  11  11  11
2017-01-08  12  12  12
 
In [60]: df.rolling('3D').agg('c':'count', 'm':'mean', 's':'std')
Out[60]:
              c     m         s
t                             
2017-01-01  1.0   1.0       NaN
2017-01-02  2.0   1.5  0.707107
2017-01-03  3.0   2.0  1.000000
2017-01-04  3.0   3.0  1.000000
2017-01-05  3.0   4.0  1.000000  #incorrect for day-end window boundary
2017-01-05  4.0   4.5  1.290994  #incorrect for day-end window boundary
2017-01-05  5.0   5.0  1.581139
2017-01-06  5.0   6.0  1.581139  #incorrect for day-end window boundary
2017-01-06  6.0   6.5  1.870829
2017-01-07  6.0   7.5  1.870829  #incorrect for day-end window boundary
2017-01-07  7.0   8.0  2.160247
2017-01-08  5.0  10.0  1.581139

但是在以同一天的所有行结束的窗口上计算,结果将是:

              c     m         s
t                             
2017-01-01  1.0   1.0       NaN
2017-01-02  2.0   1.5  0.707107
2017-01-03  3.0   2.0  1.000000
2017-01-04  3.0   3.0  1.000000
2017-01-05  5.0   5.0  1.581139  #This is what it should be
2017-01-05  5.0   5.0  1.581139  #This is what it should be
2017-01-05  5.0   5.0  1.581139
2017-01-06  6.0   6.5  1.870829  #This is what it should be
2017-01-06  6.0   6.5  1.870829
2017-01-07  7.0   8.0  2.160247  #This is what it should be
2017-01-07  7.0   8.0  2.160247
2017-01-08  5.0  10.0  1.581139

请注意,每一天的最后一行是正确的,对于同一天的所有其他行的答案都是正确的。

【问题讨论】:

【参考方案1】:

如果你的数据总是正面的,你可以在滚动后进行变换:

# if your index is not always on the day, e.g. 2017-01-01 01:00:00
# use `pd.Grouper(freq='D')` instead of `level` 
df.rolling('3D').sum().groupby(level='t').transform('max')

输出:

              a
t              
2017-01-01  1.0
2017-01-02  2.0
2017-01-03  3.0
2017-01-04  3.0
2017-01-05  5.0
2017-01-05  5.0
2017-01-05  5.0
2017-01-06  6.0
2017-01-06  6.0
2017-01-07  7.0
2017-01-07  7.0
2017-01-08  5.0

编辑:一般情况下,按天汇总并映射回来:

s = df.groupby(pd.Grouper(freq='D')).sum().rolling('3D').sum()
df.index.floor('D').to_series().map(s['a'])

输出:

t
2017-01-01    1.0
2017-01-02    2.0
2017-01-03    3.0
2017-01-04    3.0
2017-01-05    5.0
2017-01-05    5.0
2017-01-05    5.0
2017-01-06    6.0
2017-01-06    6.0
2017-01-07    7.0
2017-01-07    7.0
2017-01-08    5.0
Name: t, dtype: float64

【讨论】:

Quang Hoang 在我修改之前的第一个答案对于原始问题是正确的。它适用于一般情况,只需进行一次更改。我们需要改变的只是使用“last”而不是“max”的转换。我发现这适用于其他统计操作,如 mean() 和 std()。我发现我可以使用 agg() 将它们组合在一起,如下所示: df.rolling('3D').agg(['count','mean','std']).groupby(level='t').transform ('最后的')。最好的部分是它必须全部被cythonized,因为它在我的大型数据集上运行得非常快。

以上是关于Pandas 按天滚动时间窗口而不是单个行的主要内容,如果未能解决你的问题,请参考以下文章

pandas 基于值而不是计数的窗口滚动计算

pandas 中的前瞻性滚动窗口 - 参差不齐的索引

在内置“滚动”之外对 Pandas Dataframe 进行滚动窗口操作的最佳方法?

熊猫滚动适用于可变窗口长度

Pandas。滚动指定时间窗口和win_type

如何从 Pandas 中的 DatetimeIndex 获取滚动窗口内的持续时间