Более быстрый способ получить скользящую сумму после groupby?

Я пытаюсь получить доходность акций в день на n минут вперед, учитывая фрейм данных со строками, соответствующими доходности через определенные интервалы.

Я пробовал использовать dask и многопоточность скользящего расчета для каждой группы, но, похоже, это самый быстрый способ сделать это, что я могу понять. Однако для большого фрейма данных (миллионы строк) (252 дня и 1000 акций) на выполнение этого шага уходит до 40 минут.

ret_df.sort_values(['date','time','stock'], ascending=False, inplace=True)
gb = ret_df.groupby(['date','stock'])
forward_sum_df = gb.rolling(4, on='time', min_periods = 0)['interval_return'].sum().reset_index()

Это вернет сумму следующих 4 раз (по дате и запасу) для каждой строки в фрейме данных, как и ожидалось, но делает это довольно медленно. Спасибо за помощь!

РЕДАКТИРОВАТЬ: добавлен пример, чтобы прояснить

          date    stock            time      interval_ret
0   2017-01-03  10000001    09:30:00.000000   0.001418
1   2017-01-03  10000001    09:40:00.000000   0.000000
2   2017-01-03  10000001    09:50:00.000000   0.000000
3   2017-01-03  10000001    10:00:00.000000  -0.000474
4   2017-01-03  10000001    10:10:00.000000  -0.001417
5   2017-01-03  10000001    10:20:00.000000  -0.000944
6   2017-01-03  10000001    10:30:00.000000   0.000000
7   2017-01-03  10000001    10:40:00.000000   0.000000
8   2017-01-03  10000001    10:50:00.000000   0.000000
9   2017-01-03  10000001    11:00:00.000000  -0.000472

и так далее на складе 10000002 ... и дата 04.01.2017 ....

Например, если мой период удержания составляет 30 минут вместо 10 минут, я хотел бы суммировать 3 строки «interval_ret», сгруппированные по дате и запасу. Бывший:

        date      stock            time           interval_ret_30
0   2017-01-03  10000001    09:30:00.000000   0.001418
1   2017-01-03  10000001    09:40:00.000000   0.000000 - 0.000474
2   2017-01-03  10000001    09:50:00.000000   0.000000 - 0.000474 - 0.001417
3   2017-01-03  10000001    10:00:00.000000  -0.000474 - 0.001417 - 0.000944
4   2017-01-03  10000001    10:10:00.000000  -0.001417 - 0.000944
5   2017-01-03  10000001    10:20:00.000000  -0.000944
6   2017-01-03  10000001    10:30:00.000000   0.000000
7   2017-01-03  10000001    10:40:00.000000  -0.000472
8   2017-01-03  10000001    10:50:00.000000  -0.000472
9   2017-01-03  10000001    11:00:00.000000  -0.000472


person Shrinath Viswanathan    schedule 03.07.2019    source источник
comment
Привет, Шринат, не могли бы вы привести минимальный воспроизводимый пример?   -  person S.L    schedule 03.07.2019
comment
Отредактировал сообщение; надеюсь, это поможет прояснить, что я имею в виду. Спасибо!   -  person Shrinath Viswanathan    schedule 03.07.2019


Ответы (1)


Я не знаю, можете ли вы адаптировать это к пандам, но вы можете получить скользящие совокупные суммы для 20 миллионов значений менее чем за секунду, используя numpy:

N         = 20000000
stocks    = (np.random.random(N)*100)
window    = 4
cumStocks = np.cumsum(np.append(stocks,np.zeros(window)))
rollSum   = cumStocks[window:] - cumStocks[:-window]

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

Исходный массив cumsum дополняется нулями, чтобы сохранить исходный размер. Последние несколько элементов, которые ближе к концу массива, чем размер окна, получат скользящую сумму только оставшихся значений. Если вам не нужны эти «неполные» суммы, вы можете просто использовать cumStocks = np.cumsum(stocks), и расчет сможет сделать 100 миллионов значений менее чем за секунду.

Кажется, кто-то нашел решение этой проблемы с помощью панд здесь: https://stackoverflow.com/a/56886389/5237560

df.groupby(level=0).cumsum() - df.groupby(level=0).cumsum().shift(5)
person Alain T.    schedule 04.07.2019
comment
Спасибо за предложение! Я пробовал ваше решение, используя преобразование, чтобы адаптировать его к пандам, но кажется, что группировка по дате и запасу сильно увеличивает время. То, что я делаю сейчас (что кажется быстрее, чем мое исходное решение), - это заполнение счетчика (с ограничением для окна) для каждой группы и предотвращение прокрутки путем группировки по дате, запасу и счетчику и суммированию возврата - person Shrinath Viswanathan; 06.07.2019
comment
Большое спасибо @ alain-t цените вашу помощь; это сработало и сэкономило много времени на выполнение. Я думаю, что для случаев с большим количеством групп использование cumsum (), безусловно, является лучшим решением. - person Shrinath Viswanathan; 08.07.2019