Fast numpy roll_product

Мне нужна функция roll_product или функция expanding_product.

Есть разные pandasrolling_XXXX а также expanding_XXXX функции, но я был удивлен, обнаружив отсутствие expanding_product() функция.

Чтобы заставить вещи работать, я использовал эту довольно медленную альтернативу

pd.expanding_apply(temp_col, lambda x : x.prod())

Мои массивы часто содержат 32 000 элементов, так что это оказывается узким местом. Я испытал желание попробовать log(), cumsum(), а также exp(), но я подумал, что должен спросить здесь, так как может быть гораздо лучшее решение.

2 ответа

Решение

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

Вот оригинальная версия exp/sum/log:

def rolling_prod1(xs, n):
    return np.exp(pd.rolling_sum(np.log(xs), n))

И вот версия, которая берет накопленный продукт, сдвигает его (предварительно заполняя nans), а затем делит его обратно.

def rolling_prod2(xs, n):
    cxs = np.cumprod(xs)
    nans = np.empty(n)
    nans[:] = np.nan
    nans[n-1] = 1.
    a = np.concatenate((nans, cxs[:len(cxs)-n]))
    return cxs / a

Обе функции возвращают одинаковый результат для этого примера:

In [9]: xs
Out[9]: array([ 1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])

In [10]: rolling_prod1(xs, 3)
Out[10]: array([  nan,   nan,    6.,   24.,   60.,  120.,  210.,  336.,  504.])

In [11]: rolling_prod2(xs, 3)
Out[11]: array([  nan,   nan,    6.,   24.,   60.,  120.,  210.,  336.,  504.])

Но вторая версия намного быстрее:

In [12]: temp_col = np.random.rand(30000)

In [13]: %timeit rolling_prod1(temp_col, 3)
1000 loops, best of 3: 694 µs per loop

In [14]: %timeit rolling_prod2(temp_col, 3)
10000 loops, best of 3: 162 µs per loop

Первые результаты показывают, что это быстрое приближение для расширения_продукта

np.exp(pd.expanding_sum(np.log(temp_col)))

Для roll_product потребуются повторные деления, которые могут привести к нестабильности чисел (как указано @AmiTavory в удаленном ответе)

Другие вопросы по тегам