Есть ли способ оптимизировать Cumprod в Python? - PullRequest
0 голосов
/ 05 октября 2018

У меня есть кадр данных pandas df, и я хотел бы выполнить следующий расчет в функции.Самая длинная линия - это комок.Мне было интересно, есть ли способ ускорить это?Как в numpy, это разные способы достижения одного и того же результата, например, np.inner против np.einsum, и мне было интересно, можно ли здесь сделать что-то подобное.

import pandas as pd

In [122]: import numpy as np

In [123]: df = pd.DataFrame(np.random.randn(100000, 1000))

In [124]: %time ((1+df).cumprod(axis=0)-1)
CPU times: user 5.22 s, sys: 884 ms, total: 6.1 s
Wall time: 6.12 s

Ответы [ 2 ]

0 голосов
/ 05 октября 2018

Если вы хотите использовать другие модули для ускорения ваших расчетов, я могу порекомендовать numba.Numba компилирует код Python в LLVM и специально стремится ускорить числовые вычисления, используя numpy.

Поскольку numba еще не поддерживает использование kwargs, как axis=0 с np.cumprod, ваш код будетвыглядят так:

import numpy as np
import pandas as pd
import numba as nb

@nb.njit(parallel=True)
def nb_cumprod(arr):
    y = np.empty_like(arr)
    for i in range(arr.shape[1]):
        y[:, i] = np.cumprod(1 + arr[:, i]) - 1
    return y

arr = np.random.randn(100000, 1000)
df = pd.DataFrame(arr)

x = ((1 + df).cumprod(axis=0) - 1)
y = np.cumprod(1 + arr, axis=0) - 1
z = nb_cumprod(arr)

print(np.allclose(x, z))

И некоторые временные интервалы показывают, что numba примерно в 4 раза быстрее, чем использование cumprod в DataFrame, и примерно в 3,7 раза быстрее, чем использование numpy:

%timeit ((1 + df).cumprod(axis=0) - 1)
# 6.83 s ± 482 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit np.cumprod(1 + arr, axis=0) - 1
# 6.38 s ± 509 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit nb_cumprod(arr)
# 1.71 s ± 158 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Вы можетеиспользуйте дополнительные опции, такие как fastmath=True, чтобы еще больше повысить производительность, но это даст слегка другие результаты.

0 голосов
/ 05 октября 2018

Вы можете выполнять вычисления в NumPy вместо Pandas.Для ваших входных размеров это будет порядка ~ 5%, не очень интересно, но лучше, чем ничего.Для меньших входов усиление намного более значимо.

import pandas as pd
import numpy as np

arr = np.random.randn(100000, 1000)
df = pd.DataFrame(arr)

x = ((1 + df).cumprod(axis=0) - 1)
y = np.cumprod(1 + arr, axis=0) - 1

print(np.allclose(x, y))

Учитывая, что это тот же результат, время:

arr = np.random.randn(100000, 1000)
df = pd.DataFrame(arr)

%timeit ((1 + df).cumprod(axis=0) - 1)
# 3.64 s ± 76.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit np.cumprod(1 + arr, axis=0) - 1
# 3.42 s ± 19 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

показывает вышеупомянутый прирост скорости для ваших входов.

Для меньших входов разница больше, например:

arr = np.random.randn(1000, 10)
df = pd.DataFrame(arr)

%timeit ((1 + df).cumprod(axis=0) - 1)
# 469 µs ± 4.13 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit np.cumprod(1 + arr, axis=0) - 1
# 36.6 µs ± 427 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

, показывающая, что в этом случае вычисления в NumPy в ~ 13 раз быстрее, чем в Pandas.


РЕДАКТИРОВАТЬ:

Как предполагает @hpaulj, np.multiply.accumulate() может получить немного быстрее.

# for shape = (100000, 1000)
%timeit np.multiply.accumulate(1 + arr, axis=0) - 1
# 3.38 s ± 79.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

и, для небольших входных данных:

# for shape = (1000, 10)
%timeit np.multiply.accumulate(1 + arr, axis=0) - 1
# 35.8 µs ± 423 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

НоКак всегда, такого рода микро-эталоны следует брать с крошкой соли, особенно когда наблюдаются такие небольшие различия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...