在熊猫中有效计算具有回溯期的滚动平均值

时间:2018-11-23 16:27:32

标签: python pandas

我有一个熊猫数据框

                     cumm_vol  cumm_vol_LB
datetime                                  
2018-01-01 09:15:00     93228            0
2018-01-01 09:16:00    124353            0
2018-01-01 09:17:00    184578            0
2018-01-01 09:18:00    237003            0
2018-01-01 09:19:00    264303            0
2018-01-01 09:20:00    310503            0
2018-01-02 09:15:00    170928            0
2018-01-02 09:16:00    261528            0
2018-01-02 09:17:00    358653            0
2018-01-02 09:18:00    438678            0
2018-01-02 09:19:00    559503            0
2018-01-02 09:20:00    626178            0
2018-01-03 09:15:00    175953            0
2018-01-03 09:16:00    294078            0
2018-01-03 09:17:00    395853            0
2018-01-03 09:18:00    447078            0
2018-01-03 09:19:00    486903            0
2018-01-03 09:20:00    523578            0
2018-01-04 09:15:00     82727            0
2018-01-04 09:16:00    129077            0
2018-01-04 09:17:00    162752            0
2018-01-04 09:18:00    194852            0
2018-01-04 09:19:00    239027            0
2018-01-04 09:20:00    291677            0

我必须计算最近x天窗口中每1分钟间隔的cumm_vol的平均值,并将其添加到当前的cumm_vol_LB列中,例如,每天向前滚动一次。如果x = 2,则cumm_vol在日期2018年1月1日和2018年1月2日的 cumm_vol cumm_vol_LB datetime 2018-01-01 09:15:00 93228 0 2018-01-01 09:16:00 124353 0 2018-01-01 09:17:00 184578 0 2018-01-01 09:18:00 237003 0 2018-01-01 09:19:00 264303 0 2018-01-01 09:20:00 310503 0 2018-01-02 09:15:00 170928 0 2018-01-02 09:16:00 261528 0 2018-01-02 09:17:00 358653 0 2018-01-02 09:18:00 438678 0 2018-01-02 09:19:00 559503 0 2018-01-02 09:20:00 626178 0 2018-01-03 09:15:00 175953 132078 2018-01-03 09:16:00 294078 192940.5 2018-01-03 09:17:00 395853 271615.5 2018-01-03 09:18:00 447078 337840.5 2018-01-03 09:19:00 486903 523203 2018-01-03 09:20:00 523578 468340.5 .......... 的平均值为零,而对于2018年1月3日的平均值为(93228 + 170928)/ 2 = 132078 因此,预期输出将是一种滚动平均值:

groupby

我目前正在尝试的方法是过滤回溯期= 2的日期的数据,并同时根据时间for dateix,date in enumerate(dates): nifty_datewise = nifty_data.groupby('date').get_group(date) nifty_datatemp = groupbytime(nifty_data, nifty_datewise, dates, dateix) nifty_main = nifty_main.append(nifty_datatemp) def groupbytime(nifty_datafrm, nifty_datewise, dates, dateix): if dateix-2>=0: nifty_data = nifty_datafrm.loc[dates[dateix-2]: dates[dateix]] datesNew = nifty_data["date"].dt.date.unique() lookback_df = pd.DataFrame() for datei,date in enumerate(datesNew): nifty_df = nifty_data.groupby('date').get_group(date) lookback_df = lookback_df.append(nifty_df) nifty_datewise["cumm_vol_LB"] = lookback_df.groupby('time')['cumm_vol'].transform('mean') return nifty_datewise else: return nifty_datewise 进行转换并构建新的数据框。

.rolling

这似乎不是最佳解决方案。寻找最佳实践来实现这一目标,也许熊猫已经为这种用例内置了某些功能,for (i=0; i<n-1; i++) { for (j=0; j<n-i-1; j++) { digit=a[j]%10; while (a[j]>0) { if (a[j]%10!=digit) same=0; a[j]/=10; } digit1=a[j+1]%10; while (a[j+1]>0) { if (a[j+1]%10!=digit1) same1=0; a[j+1]/=10; } if (same==0 && same1==1) { temp=a[j+1]; a[j+1]=a[j]; a[j]=temp; } } } 却无济于事,因为它逐行工作。

谢谢

1 个答案:

答案 0 :(得分:1)

重构我的第一个答案时,我发现熊猫对时间序列数据有很好的处理能力。您可以阅读here。而且,将数据与groupby分组似乎非常有效,并且不会像我最初想象的那样创建多余的数据副本。

答案A在数据集大小上的缩放比例(线性)要比答案B好得多。我可以在大约100毫秒内计算20k大小写(用%timeit中的ipython进行度量)。在下面找到我正在测试的数据的摘录。


答案A:

此方法按分钟对数据进行分组,然后对各组应用移动平均滤波器。阅读here有关熊猫窗口功能的信息。 here给出了用于指定时间增量的可用偏移别名的列表。

def assign_rolling_average(x, dt):
    x.cumm_vol_LB = x.cumm_vol.rolling(window=dt).mean()
    return x

dt='3D' # width of rolling average window: 3 days
# Group data by the time.
g = df.groupby(lambda x: x.time())   
# Apply the moving average filter on all groups. 
df = g.apply(assign_rolling_average, dt=dt)

答案B(慢得多):

这是我最初的答案。它手动标识要操作的行。它涉及具有全长逻辑索引的多个操作,并且可能会受到data locality problems的影响。它在运行时按问题大小呈二次方缩放。

from datetime import timedelta

# Time delta: fix here the width of the time window
dt = timedelta(days=3)

# Iterate over the rows
for idx in df.index:
    date, time = idx.date(), idx.time()
    mask = ((df.index.time == time)         # Same time of the day
            & (df.index.date <= date)       # Not later than today 
            & (df.index.date >= (date-dt))) # Not older than (today - dt)
    df.loc[idx, 'cumm_vol_LB'] = df.loc[mask, 'cumm_vol'].mean()

这是我测试过的数据框:

import pandas as pd
df = pd.DataFrame([["2018-01-01 09:15:00",  93228, 0],
                   ["2018-01-01 09:16:00", 124353, 0],
                   ["2018-01-01 09:17:00", 184578, 0],
                   ["2018-01-01 09:18:00", 237003, 0],
                   ["2018-01-01 09:19:00", 264303, 0],
                   ["2018-01-01 09:20:00", 310503, 0],
                   ["2018-01-02 09:15:00", 170928, 0],
                   ["2018-01-02 09:16:00", 261528, 0],
                   ["2018-01-02 09:17:00", 358653, 0],
                   ["2018-01-02 09:18:00", 438678, 0],
                   ["2018-01-02 09:19:00", 559503, 0],
                   ["2018-01-02 09:20:00", 626178, 0],
                   ["2018-01-03 09:15:00", 175953, 0],
                   ["2018-01-03 09:16:00", 294078, 0],
                   ["2018-01-03 09:17:00", 395853, 0],
                   ["2018-01-03 09:18:00", 447078, 0],
                   ["2018-01-03 09:19:00", 486903, 0],
                   ["2018-01-03 09:20:00", 523578, 0],
                   ["2018-01-04 09:15:00",  82727, 0],
                   ["2018-01-04 09:16:00", 129077, 0],
                   ["2018-01-04 09:17:00", 162752, 0],
                   ["2018-01-04 09:18:00", 194852, 0],
                   ["2018-01-04 09:19:00", 239027, 0],
                   ["2018-01-04 09:20:00", 291677, 0]],
                  columns = ['datetime', 'cumm_vol', 'cumm_vol_LB']
                  )
df = df.set_index('datetime')
df.index = pd.to_datetime(df.index)