使用pandas在滚动窗口中重新采样

11
假设我有每日数据(不是定期间隔),我想计算过去5个月中每个月的移动标准差(或任意非线性函数)。例如,对于2012年5月,我将从2012年1月到2012年5月(5个月)的期间计算stddev。对于2012年6月,该期间从2012年2月开始,以此类推。最终结果是一个具有月度值的时间序列。
无法应用滚动窗口,因为这首先是每日的,其次我需要指定值的数量(滚动窗口不会按时间框架聚合,一些帖子解决了这个问题issue,但它们与我的问题无关,因为滚动仍然是针对每一天的)。
无法应用重新采样,因为那么样本将是每5个月一次,例如我只会在2012年5月,2012年10月,2013年3月等时刻获得值... 最后,由于函数不是线性的,我不能通过首先进行每月样本,然后在其上应用5期滚动窗口来重构它。
我需要一种对时间间隔定义的滚动窗口进行重新采样的功能,与数值数量无关。
如何在pandas中实现这个功能?一种方法是将几个(例如5个)重新采样的(5个月)时间序列组合起来,每个序列相差一个月,然后将所有这些序列对齐到一个序列中...但我不知道如何实现。

你是在寻找过去五个“日历”月的标准吗(对于2012年5月:12月,1月,2月,3月,4月),还是过去五个有数据的月份(如果缺少2月,那么是11月,12月,1月,3月,4月)?你提到了包括5月,但这只有在你指的是前四个月+本月截至目前为止的情况下才有意义(如果是这种情况,我的问题仍然存在)。 - offbyone
我有一点难以理解。这个例子的目标是从1月1日到5月31日获取每日价值的调用,计算它们的标准差,并将其作为5月份的值返回吗? - Dan
澄清一下:我正在寻找5个日历月份(数据不一定均匀分布),包括当前月份,因此对于2012年5月,我从2012年1月到2012年5月(窗口长度为5个月,无论每个月只有一天还是20天)。用户@user3823992是正确的,此外我只关心每月的结果,因此我需要将其应用于2012年6月、2012年7月等。 - Mannaggia
1
如果pandas已经导入了日期和时间数据,您应该能够使用语法dft[datetime(2013, 1, 1):datetime(2013,6)]从给定的月份中选择数据。只需编写一个循环或等效循环来循环开始和结束月份值,并将您的函数应用于生成的数据框中的值即可。 (抱歉,我现在没有带有日期戳的数据集可供测试) - Dan
3个回答

5

我曾经遇到一个类似的问题,处理时间差系列数据时,我想要进行移动平均并重新取样。以下是一个例子,假设我们有100秒的数据。我们取10秒窗口的滚动平均值,然后为每5秒重新取样一次,并在每个重新取样的区间中取第一个条目。结果应该是在5秒增量下的前10秒平均值。您也可以使用月份格式来进行类似的操作:

df = pd.DataFrame(range(0,100), index=pd.TimedeltaIndex(range(0,100),'s'))
df.rolling('10s').mean().resample('5s').first()

结果:

             0
00:00:00   0.0
00:00:05   2.5
00:00:10   5.5
00:00:15  10.5
00:00:20  15.5
00:00:25  20.5
00:00:30  25.5
00:00:35  30.5
00:00:40  35.5
00:00:45  40.5
00:00:50  45.5
00:00:55  50.5
00:01:00  55.5
00:01:05  60.5
00:01:10  65.5
00:01:15  70.5
00:01:20  75.5
00:01:25  80.5
00:01:30  85.5
00:01:35  90.5

2

我用以下代码解决了一个类似的问题:

interval = 5
frames = []
for base in range(interval):
  frame = data.resample(f"{interval}min", base=base).last()
  frames.append(frame)

pd.concat(frames, axis=0).sort_index()

这里我创建了5个数据框,它们以相同的间隔重新采样,但具有不同的偏移量(基本参数)。然后我只需要连接和排序它们。通常比滚动+重新采样更有效率(唯一的开销是排序)。


1
这里有一个尝试 - 不是非常干净,但可能有效。
虚拟数据:
df = pd.DataFrame(data={'a': 1.}, 
                  index=pd.date_range(start='2001-1-1', periods=1000))

首先定义一个函数,将日期减少 n 个月。虽然还需要进一步整理,但对于 n<=12 是有效的。

from datetime import datetime    
def decrease_month(date, n):
    assert(n <= 12)

    new_month = date.month - n
    year_offset = 0
    if new_month <= 0:
        year_offset = -1
        new_month = 12 + new_month

    return datetime(date.year + year_offset, new_month, 1)

然后,为每个日期将穿过的5个滚动期添加5列新列。

for n in range(rolling_period):
    df['m_' + str(n)] = df.index.map(lambda x: decrease_month(x, n))

然后,使用 melt 函数将数据从宽格式转换为长格式,这样每个滚动周期就会有一个条目。
df_m = pd.melt(df, id_vars='a')

你应该能够按照新创建的列进行分组,每个日期将代表最近的5个月滚动期。
In [222]: df_m.groupby('value').sum()
Out[222]: 
              a
value          
2000-09-01   31
2000-10-01   59
2000-11-01   90
2000-12-01  120
2001-01-01  151
2001-02-01  150
2001-03-01  153
2001-04-01  153
2001-05-01  153
2001-06-01  153
2001-07-01  153
...

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