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 按天滚动时间窗口而不是单个行的主要内容,如果未能解决你的问题,请参考以下文章