【问题标题】:Resample in a rolling window using pandas使用 pandas 在滚动窗口中重新采样
【发布时间】:2014-09-12 13:58:50
【问题描述】:

假设我有每日数据(不规则间隔),我想计算过去每个月的移动标准差(或任意非线性函数) 5个月。例如,对于 2012 年 5 月,我将计算从 2012 年 1 月到 2012 年 5 月(5 个月)期间的 stddev。对于 2012 年 6 月,该周期从 2012 年 2 月开始,依此类推。最终结果是具有月值的时间序列。

无法应用滚动窗口,因为这首先是每天,其次我需要指定值的数量(滚动窗口不会按时间范围汇总,一些帖子解决了这个问题issue但它们与我的问题无关,因为新的一天仍然会滚动)。

无法应用重采样,因为这样的样本将是每 5 个月一次,例如,我将只有 2012 年 5 月、2012 年 10 月、2013 年 3 月的值...最后,作为函数是非线性我无法通过首先进行每月样本然后对其应用 5 个周期的滚动窗口来重建它。

所以我需要一种重采样功能,应用于由时间间隔(而不是值的数量)定义的滚动窗口。

如何在熊猫中做到这一点?一种方法是组合几个(在本例中为 5 个)重采样(5 个月)时间序列,每个时间序列都有一个月的偏移量,然后将所有这些序列对齐为一个......但我不知道如何实现这一点。

【问题讨论】:

  • 您是在查找过去五个 日历 个月(2012 年 5 月:Dec、Jan、Feb、Mar、Apr)或过去五个月的 std有数据(如果缺少 2 月,例如:11 月、12 月、1 月、3 月、4 月)?您也提到包括 May,但这没有意义,除非您指的是前四个月 + MTD(在这种情况下我的问题仍然存在)
  • 我在跟随时遇到了一些麻烦。示例中的目标是调用从 1 月 1 日到 5 月 31 日的每日值,计算这些值的标准差并将其作为 5 月的值返回吗?
  • 澄清:我正在寻找 5 个日历月(数据不一定是均匀间隔的),包括当月,所以对于 2012 年 5 月,我从 2012 年 1 月到 2012 年 5 月(窗口的长度是 5 个月,不管我每个月只有一天还是 20 天)。用户@user3823992 是正确的,另外我只关心每月的结果,所以我需要在 2012 年 6 月、2012 年 7 月等申请。
  • 如果 pandas 已经为您导入了日期和时间数据,您应该能够使用语法 dft[datetime(2013, 1, 1):datetime(2013,6)] 从给定月份获取选择数据。只需编写一个循环或等效程序来循环开始和结束月份的值,并将您的函数应用于结果数据框中的值。 (抱歉,我现在没有方便自己测试的日期戳数据集)

标签: python pandas resampling


【解决方案1】:

这是一个尝试 - 不是超级干净,但它可能会起作用。

虚拟数据:

df = pd.DataFrame(data={'a': 1.}, 
                  index=pd.date_range(start='2001-1-1', periods=1000))

首先定义一个函数来减少日期n 的月数。这需要清理,但适用于 n

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
...

【讨论】:

    【解决方案2】:

    我在处理 timedelta 系列时遇到了类似的问题,我想取一个移动平均线,然后重新采样。这是一个我有 100 秒数据的示例。我取 10 秒窗口的滚动平均值,然后每 5 秒重新采样一次,在每个重新采样箱中取第一个条目。结果应该是前 10 秒的平均值,以 5 秒为增量。您可以使用月份格式而不是秒来执行类似的操作:

    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
    

    【讨论】:

      【解决方案3】:

      我用下面的代码解决了类似的问题:

      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 个数据帧,它们以相同的间隔重新采样,但具有不同的偏移量(基本参数)。然后我只需要连接和排序它们。通常应该比滚动 + 重采样更有效(唯一的开销是排序)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-17
        • 2022-09-28
        • 2013-06-09
        • 2013-12-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多