эффективно рассчитать скользящее среднее с периодом просмотра в пандах - PullRequest
0 голосов
/ 23 ноября 2018

У меня есть пандасный фрейм данных

                     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

Мне нужно вычислить среднее значение cumm_vol на каждом 1-минутном интервале для окна последних x дней и добавить его в текущий столбец cumm_vol_LB, и это произойдет.вперед на каждый день, например, с x = 2, среднее значение cumm_vol в 09:15:00 для дат 2018-01-01 и 2018-01-02 будет равно нулю, а для 2018-01-03 будет (93228+170928) / 2 = 132078 Таким образом, ожидаемый результат будет своего рода скользящим средним:

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

В настоящее время я пытаюсь отфильтровать данные по датам периода просмотра = 2 и одновременно groupby преобразование, основанное на времени, и создание нового кадра данных.

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

Это не является оптимальным решением.В поисках лучшей практики для достижения этой цели, возможно, у pandas есть что-то встроенное для такого варианта использования, .rolling не помогло, поскольку он работает строка за строкой.

Спасибо

1 Ответ

0 голосов
/ 23 ноября 2018

При рефакторинге моего первого ответа я понял, что у панд очень хорошая обработка данных временных рядов.Вы можете прочитать здесь об этом.Кроме того, группирование данных с помощью groupby кажется довольно эффективным и не создает лишних копий данных, как я изначально думал.

Ответ A масштабируется намного лучше (линейно) с размером набора данных, чем ответ B. Я мог бывычислить случай 20k примерно за 100 мс (измеренный с %timeit в ipython).Ниже приведен отрывок данных, с которыми я тестировал.


Ответ A:

Этот подход группирует данные по минутам, а затем применяет фильтр скользящих средних по группам.Прочитайте здесь о оконных функциях в пандах.Список доступных псевдонимов смещения для указания дельт времени приведен здесь .

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 (намного медленнее):

Это был мой первоначальный ответ,Он вручную определяет строки для работы.Он включает в себя несколько операций с полноразмерными логическими индексами и, вероятно, страдает от проблем с локальностью данных .Он масштабируется во время выполнения в квадрате с размером проблемы.

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...