Очень легко написать функцию, которая вычисляет максимальную просадку временного ряда. Требуется немного подумать, чтобы записать его в O(n)
время вместо O(n^2)
. Но это не так уж плохо. Это будет работать:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def max_dd(ser):
max2here = pd.expanding_max(ser)
dd2here = ser - max2here
return dd2here.min()
Позвольте настроить короткий ряд, чтобы воспроизвести его:
np.random.seed(0)
n = 100
s = pd.Series(np.random.randn(n).cumsum())
s.plot()
plt.show()
Как и ожидалось, max_dd(s)
завершает показ чего-то около -17.6. Хорошо, отлично, грандиозно. Теперь скажите, что я заинтересован в вычислении прокрутки этой серии. То есть для каждого шага я хочу вычислить максимальную просадку из предыдущего субсектора указанной длины. Это легко сделать с помощью pd.rolling_apply
. Он работает так:
rolling_dd = pd.rolling_apply(s, 10, max_dd, min_periods=0)
df = pd.concat([s, rolling_dd], axis=1)
df.columns = ['s', 'rol_dd_10']
df.plot()
Это прекрасно работает. Но он чувствует себя очень медленно. Есть ли особенно гладкий алгоритм в pandas или другой инструментарий, чтобы сделать это быстро? Я сделал попытку написать что-то на заказ: он отслеживает всевозможные промежуточные данные (местоположения наблюдаемых максимумов, местоположения ранее найденных просадок), чтобы сократить множество избыточных вычислений. Это экономит время, но не много, и не так много, как должно быть возможно.
Я думаю, это из-за всех накладных расходов на Python/Numpy/ Pandas. Но я в настоящее время недостаточно свободно владею в Cython, чтобы действительно знать, как начать атаковать это с этого угла. Я надеялся, что кто-то пробовал это раньше. Или, возможно, кто-то может захотеть взглянуть на мой "ручной" код и быть готовым помочь мне преобразовать его в Cython.
Изменить: Для тех, кто хочет просмотреть все функции, упомянутые здесь (и некоторые другие!), Посмотрите на ноутбук iPython по адресу: http://nbviewer.ipython.org/gist/8one6/8506455
Он показывает, как связаны некоторые из подходов к этой проблеме, проверяет, что они дают одинаковые результаты, и показывает их время выполнения на данных разных размеров.
Если кому-то интересно, в моем сообщении, на который я ссылался в своем сообщении, есть "tppoke", это rolling_dd_custom
. Я думаю, что это может быть очень быстрое решение, если оно реализовано в Cython.