Но здесь меня интересует, как numba превосходит Pandas в вычислении экспоненциальных скользящих средних.
Ваша версия выглядит быстрее только потому, что вы передаете ей массив NumPy, а неСтруктура данных Pandas:
>>> s = pd.Series(np.random.random(10000))
>>> %timeit ewm(s, alpha=0.5)
82 ms ± 10.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>> %timeit ewm(s.values, alpha=0.5)
26 µs ± 193 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit s.ewm(alpha=0.5).mean()
852 µs ± 5.44 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
В целом, сравнение операций NumPy и Pandas проводится по принципу «яблоки с апельсинами».Последний построен поверх первого и почти всегда будет торговать скоростью для гибкости.(Но, принимая это во внимание, Pandas все еще быстр и стал со временем все больше полагаться на операции Cython.) Я не совсем уверен, что именно с Numba / Jit ведет себя лучше с NumPy.Но если вы сравниваете обе функции с помощью серии Pandas, сама Pandas выходит быстрее.
Как Pandas вычисляет EMA под капотом?
Когда вы звоните df.ewm()
(пока не вызывая такие методы, как .mean()
или .cov()
), промежуточный результат представляет собой истинный класс EWM
, который находится в pandas/core/window.py
.
>>> ewm = pd.DataFrame().ewm(alpha=0.1)
>>> type(ewm)
<class 'pandas.core.window.EWM'>
Независимо от того, передаете ли вы com
,span
, halflife
или alpha
, Панды отобразят это обратно на com
и будут использовать его.
Когда вы вызываете сам метод, например ewm.mean()
это соответствует ._apply()
, который в этом случае служит маршрутизатором для соответствующей функции Cython:
cfunc = getattr(_window, func, None)
В случае .mean()
, func
- это "ewma"._window
- это модуль Cython pandas/libs/window.pyx
.
Это подводит вас к сути вещей с помощью функции ewma()
, где находится основная массаработы выполняется:
weighted_avg = ((old_wt * weighted_avg) +
(new_wt * cur)) / (old_wt + new_wt)
Если вы хотите более справедливое сравнение, вызовите эту функцию напрямую со значениями NumPy:
>>> from pandas._libs.window import ewma
>>> %timeit ewma(s.values, 0.4, 0, 0, 0)
513 µs ± 10.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
(Помните, что требуется толькоcom; для этого вы можете использовать pandas.core.window._get_center_of_mass()
.