滚动统计性能:pandas vs. numpy strides

Rolling statistics performance: pandas vs. numpy strides

我对在大型一维 numpy 数组上滚动 windows 计算统计数据很感兴趣。对于小 window 尺寸,使用 numpy 步幅(a la numpy.lib.stride_tricks.sliding_window_view)比 pandas 滚动 window 实施更快,但对于大 window 尺寸则相反.

考虑以下几点:

import numpy as np
from numpy.lib.stride_tricks import sliding_window_view
import pandas as pd

data = np.random.randn(10**6)
data_pandas = pd.Series(data)

window = 2
%timeit np.mean(sliding_window_view(data, window), axis=1)
# 19.3 ms ± 255 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit data_pandas.rolling(window).mean()
# 34.3 ms ± 688 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

window = 1000
%timeit np.mean(sliding_window_view(data, window), axis=1)
# 302 ms ± 8.01 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit data_pandas.rolling(window).mean()
# 31.7 ms ± 958 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

result_numpy = np.mean(sliding_window_view(data, window), axis=1)
result_pandas = data_pandas.rolling(window).mean()[window-1:]
np.allclose(result_numpy, result_pandas)
# True

对于更大的 window 大小,pandas 实现实际上 更快 ,而 numpy 实现 慢得多.

pandas 背后发生了什么,我们如何使用 numpy 获得类似的性能?

与 pandas 相比,如何在 numpy 的大型 windows 上获得相似的性能?

TL;DR: 两个版本使用非常不同的算法.

sliding_window_view 技巧可以很好地解决小 window 的滚动平均问题,但这不是一个干净的方法,也不是有效的方法,尤其是大 [=36] =].事实上,Numpy 计算一个平均值并记录一个滚动平均值,因此没有明确的信息表明用户正在大步作弊以计算其他东西。 提供的 Numpy 实现在 O(n * w) 中运行,其中 n 是数组大小,w window 大小 。 Pandas 确实有需要计算滚动平均值的信息,因此它使用了更有效的算法。 Pandas 算法运行 O(n) 时间 。有关它的更多信息,请阅读

这是一个更快的 Numpy 实现:

cumsum = np.cumsum(data)
invSize = 1. / window
(cumsum[window-1:] - np.concatenate([[0], cumsum[:-window]])) * invSize

以下是我机器上的性能结果:

Naive Numpy version:  193.2 ms
Pandas version:        33.1 ms
Fast Numpy version:     8.5 ms