I want to find a concise way to sample n consecutive elements with stride m from a numpy array. The simplest case is with sampling 1 element with stride 2, which means getting every other element in a list, which can be done like this:
>>> a = np.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> a[::2]
array([0, 2, 4, 6, 8])
However, what if I wanted to slice n consecutive elements with a stride of m where n and m can be any integers? For example, if I wanted to slice 2 consecutive elements with a stride of 3 I would get something like this:
array([0, 1, 3, 4, 6, 7, 9])
Is there a pythonic and concise way of doing this? Thank you!
CodePudding user response:
If a
is long enough you could reshape, slice, and ravel
a.reshape(-1,3)[:,:2].ravel()
But a
has to be (9,) or (12,). And the result will still be a copy.
The suggested:
np.lib.stride_tricks.as_strided(a, (4,2), (8*3, 8)).ravel()[:-1]
is also a copy. The as_strided
part is a view, but ravel
will make a copy. And there is the ugliness of that extra element.
sliding_window_view
was added as a safer version:
In [81]: np.lib.stride_tricks.sliding_window_view(a,(3))
Out[81]:
array([[0, 1, 2],
[1, 2, 3],
[2, 3, 4],
[3, 4, 5],
[4, 5, 6],
[5, 6, 7],
[6, 7, 8],
[7, 8, 9]])
In [82]: np.lib.stride_tricks.sliding_window_view(a,(3))[::3,:2]
Out[82]:
array([[0, 1],
[3, 4],
[6, 7]])
Again ravel
will make a copy. This omits the "extra" 9
.
np.resize
does a reshape
with padding (repeating a
as needed):
In [83]: np.resize(a, (4,3))
Out[83]:
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[9, 0, 1]])
In [84]: np.resize(a, (4,3))[:,:2]
Out[84]:
array([[0, 1],
[3, 4],
[6, 7],
[9, 0]])
CodePudding user response:
This code might be useful, I tested it on the example in the question (n=2, m=3)
import numpy as np
def get_slice(arr, n, m):
b = np.array([])
for i in range(0, len(arr), m):
b = np.concatenate((b, arr[i:i n]))
return b
sliced_arr = get_slice(np.arange(10), n=2, m=3)
print(sliced_arr)
Output
[0. 1. 3. 4. 6. 7. 9.]