Python - 时间加权平均熊猫,按时间间隔分组

问题描述:

我有熊猫数据框中的时间序列。时间戳可能不均匀(每1-5分钟一次),但每隔5分钟总会有一次(分钟以0,5,10,15,20,25,30,35,40,45,50结尾的时间戳,55)。Python - 时间加权平均熊猫,按时间间隔分组

例子:

2017-01-01 2:05:00 32.90 
2017-01-01 2:07:30 29.83 
2017-01-01 2:10:00 45.76 
2017-01-01 2:15:00 16.22 
2017-01-01 2:20:00 17.33 
2017-01-01 2:25:00 23.40 
2017-01-01 2:28:45 150.12 
2017-01-01 2:30:00 100.29 
2017-01-01 2:35:00 38.45 
2017-01-01 2:40:00 67.12 
2017-01-01 2:45:00 20.00 
2017-01-01 2:50:00 58.41 
2017-01-01 2:55:00 58.32 
2017-01-01 3:00:00 59.89 

我想15分钟块的时间加权平均值。具有时间戳直接是在15分钟标记(时间戳与结束0,15,30,45分钟)结束的时间间隔中的行,所以分组如下:

Group 1 (interval 2017-01-01 2:00:00): 
    2017-01-01 2:05:00 32.90 
    2017-01-01 2:07:30 29.83 
    2017-01-01 2:10:00 45.76 
    2017-01-01 2:15:00 16.22 

Group 2 (interval 2017-01-01 2:15:00): 
    2017-01-01 2:20:00 17.33 
    2017-01-01 2:25:00 23.40 
    2017-01-01 2:28:45 150.12 
    2017-01-01 2:30:00 100.29 

Group 3 (interval 2017-01-01 2:30:00): 
    2017-01-01 2:35:00 38.45 
    2017-01-01 2:40:00 67.12 
    2017-01-01 2:45:00 20.00 

Group 4 (interval 2017-01-01 2:45:00): 
    2017-01-01 2:50:00 58.41 
    2017-01-01 2:55:00 58.32 
    2017-01-01 3:00:00 59.89 

平均必须是时间加权,所以不仅仅是一个组中所有值的标准平均值。

例如,组2的时间加权平均值不是72.785,这是所有4个值的常规平均值。相反,它应该是:

(5 minutes/15 minutes) * 17.33 = 5.776667  ==> The 5 minutes is taken from the difference between this timestamp and the previous timestamp 
+(5 minutes/15 minutes) * 23.40 = 7.8 
+(3.75 minutes/15 minutes) * 150.12 = 37.53 
+(1.25 minutes/15 minutes) * 100.29 = 8.3575 

= **59.46417** 

而且理想情况下,15分钟参数,因为这可能会在未来的60分钟(每小时)改变,但我不认为这是一个问题在这里。

此外,性能在此非常重要。由于我的数据集将有大约10k行,因此逐个遍历每条记录会非常缓慢。

我试过寻找熊猫的df.rolling()函数,但无法弄清楚如何将它直接应用到我的特定场景。

非常感谢您的帮助!

更新1:

继西蒙的辉煌的解决方案,我修改了它一点点。

我做了一些调整,它以使其适应我的具体情况:

def func(df): 
    if df.size == 0: return 
    timestep = 15*60 
    indexes = df.index - (df.index[-1] - pd.Timedelta(seconds=timestep)) 
    seconds = indexes.seconds 
    weight = [seconds[n]/timestep if n == 0 else (seconds[n] - seconds[n - 1])/timestep 
      for n, k in enumerate(seconds)] 
    return np.sum(weight*df.values) 

这是为了应付可能是空的,每隔15分钟(中缺少DB行)

这一次是棘手。我希望看到另一位评论者更有效地做到这一点,因为我有预感,有更好的方法来做到这一点。

我也跳过了一个部分,它参数化了15分钟的值,但我指出你可以在评论中做到这一点。这留给读者一个练习:D它应该被参数化,因为它现在有很多随机的'* 15'和'* 60'值散布在这个地方,看起来很笨拙。

我也很累,我的妻子想看电影,所以我没有清理我的代码。这有点混乱,应该写得更清洁 - 这可能或不值得做,取决于其他人是否可以在6行代码中重做这些。如果明天早上它还没有回答,我会回过头来,做得更好。

更新更好的解决方案1 ​​

def func(df): 
    timestep = 15*60 
    seconds = (df.index.minute*60+df.index.second)-timestep 
    weight = [k/timestep if n == 0 else (seconds[n] - seconds[n - 1])/timestep 
       for n, k in enumerate(seconds)] 
    return np.sum(weight*df.values) 

df.resample('15min', closed='right').apply(func) 
+0

这是辉煌!非常感谢,这正是我需要的! 有没有办法使用GroupBy()而不是Resample()? 原因是我有另一列我想分组,其中我没有包括在原来的问题,为简单起见。我似乎是使用表: df.groupby([pd.TimeGrouper(freq ='15Min')])) 但似乎没有办法关闭右侧的组,如resample()函数具有。 –

+0

所以基本上,我有以下的4列在我的表: “TIME | ZONE |价格1 | PRICE2” 我想有每个区的时间加权平均每十五分钟间隔每个价格 –

+0

我做一些更多的数据测试和整个事情是非常缓慢的;也许我只是不习惯Python的速度。要处理160万行(每组约3行530k组),大约需要10分钟。我在C#程序中做了同样的事情(代码长得多,因为我不得不在每一行上手动迭代),并且花了不到10秒。 –