滚动统计性能: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
我对在大型一维 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