Other ideas:
- Multi-dimensional rolling window (investigate the memory issues of this approach):
- A simple Python loop or list comprehension
Other ideas:
| #################################################################### | |
| # Data generation | |
| x = np.arange(10, dtype='float64') | |
| s = pd.Series(x) | |
| window=3 | |
| min_periods = 0 | |
| closed = 'right' | |
| # indices = np.arange(len(x), dtype='int64') | |
| indices = None | |
| #################################################################### | |
| roll_sum = pd._libs.window.roll_sum | |
| %timeit roll_sum(x, window, min_periods, indices, closed) | |
| R = s.rolling(window, min_periods) | |
| %timeit R.sum() | |
| #################################################################### | |
| roll_generic = pd._libs.window.roll_generic | |
| func = np.sum | |
| %timeit roll_generic(x, window, min_periods, indices, closed, 0, func, (), ()) | |
| %timeit R.apply(func) | |
| #################################################################### | |
| # roll_sum performance is comparable to its competitors | |
| from scipy.signal import lfilter | |
| h = np.array([1]*window, dtype=float) | |
| %timeit lfilter(h, 1.0, x) | |
| from bottleneck import move_sum | |
| %timeit move_sum(x, window, min_count=1) |
| import numpy as np | |
| from numba import guvectorize | |
| def rolling_window(a, window): | |
| shape = a.shape[:-1] + (a.shape[-1] - window + 1, window) | |
| strides = a.strides + (a.strides[-1],) | |
| return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides) | |
| def rolling_apply_unary(g): | |
| # Note: a new f() is compiled each time rolling_apply() is called! | |
| @guvectorize(['void(float64[:],int64, float64[:])'], '(n),()-> (n)') | |
| def f(arg0, window, out): | |
| transient_len = window-1 | |
| for i in range(transient_len): | |
| out[i] = np.nan | |
| for i in range(transient_len, len(arg0)): | |
| win_slice = slice(i-window+1, i+1) | |
| out[i] = g(arg0[win_slice]) | |
| return f | |
| def rolling_apply_binary(g): | |
| # Note: a new f() is compiled each time rolling_apply() is called! | |
| @guvectorize(['void(float64[:], float64[:], int64, float64[:])'], '(n),(n),()-> (n)') | |
| def f(arg0, arg1, window, out): | |
| transient_len = window-1 | |
| for i in range(transient_len): | |
| out[i] = np.nan | |
| for i in range(transient_len, len(arg0)): | |
| win_slice = slice(i-window+1, i+1) | |
| out[i] = g(arg0[win_slice], arg1[win_slice]) | |
| return f | |
| unary_foo = np.sum | |
| f1 = rolling_apply_unary(unary_foo) | |
| binary_foo = np.dot | |
| f2 = rolling_apply_binary(binary_foo) | |
| window=3 | |
| x = np.multiply.outer([1,-1],np.arange(10)) | |
| f1(x,window) | |
| f2(x,x,window) |