Here's one with np.lib.stride_tricks.as_strided
that gives us views
into a zeros-padded array and as such are very efficient, both memory-wise and performance-wise -
def sliding_windows(a, n=4):
length = len(a) + n
width = n + 1
z_pad = np.zeros(n,dtype=a.dtype)
ac = np.r_[z_pad, a, z_pad]
s = ac.strides[0]
strided = np.lib.stride_tricks.as_strided
return strided(ac[n:], shape=(width, length), strides=(-s,s),writeable=False)
If you need a writable version, simply make a copy with sliding_windows(a, n=4).copy()
.
Sample runs -
In [42]: a
Out[42]: array([1, 2, 3])
In [43]: sliding_windows(a, n=4)
Out[43]:
array([[1, 2, 3, 0, 0, 0, 0],
[0, 1, 2, 3, 0, 0, 0],
[0, 0, 1, 2, 3, 0, 0],
[0, 0, 0, 1, 2, 3, 0],
[0, 0, 0, 0, 1, 2, 3]])
In [44]: sliding_windows(a, n=5)
Out[44]:
array([[1, 2, 3, 0, 0, 0, 0, 0],
[0, 1, 2, 3, 0, 0, 0, 0],
[0, 0, 1, 2, 3, 0, 0, 0],
[0, 0, 0, 1, 2, 3, 0, 0],
[0, 0, 0, 0, 1, 2, 3, 0],
[0, 0, 0, 0, 0, 1, 2, 3]])
One more with array-assignment
, which should be good if you need a writable version -
def sliding_windows_arrassign(a, n=4):
pad_length = len(a) + n + 1
width = n + 1
p = np.zeros((width,pad_length),dtype=a.dtype)
p[:,:len(a)] = a
return p.ravel()[:-n-1].reshape(width,-1)
Benchmarking on larger arrays
1) 100
elements and similar n
:
In [101]: a = np.arange(1,101)
In [102]: %timeit sliding_windows(a, n=len(a)+1)
100000 loops, best of 3: 17.6 µs per loop
In [103]: %timeit sliding_windows_arrassign(a, n=len(a)+1)
100000 loops, best of 3: 8.63 µs per loop
# @Julien's soln
In [104]: %%timeit
...: n = len(a)+1
...: m = np.tile(np.hstack((a,np.zeros(n+1))),n+1)[:(n+len(a))*(n+1)]
...: m.shape = (n+1, n+len(a))
100000 loops, best of 3: 15 µs per loop
2) ~5000
elements and similar n
:
In [82]: a = np.arange(1,5000)
In [83]: %timeit sliding_windows(a, n=len(a)+1)
10000 loops, best of 3: 23.2 µs per loop
In [84]: %timeit sliding_windows_arrassign(a, n=len(a)+1)
10 loops, best of 3: 28.9 ms per loop
# @Julien's soln
In [91]: %%timeit
...: n = len(a)+1
...: m = np.tile(np.hstack((a,np.zeros(n+1))),n+1)[:(n+len(a))*(n+1)]
...: m.shape = (n+1, n+len(a))
10 loops, best of 3: 34.3 ms per loop
np.lib.stride_tricks.as_strided
would have a constant runtime irrespective of the array length owing to the memory efficiency discussed earlier.