Lets say I have a Python Numpy array a
.
a = numpy.array([1,2,3,4,5,6,7,8,9,10,11])
I want to create a matrix of sub sequences from this array of length 5 with stride 3. The results matrix hence will look as follows:
numpy.array([[1,2,3,4,5],[4,5,6,7,8],[7,8,9,10,11]])
One possible way of implementing this would be using a for-loop.
result_matrix = np.zeros((3, 5)) for i in range(0, len(a), 3): result_matrix[i] = a[i:i+5]
Is there a cleaner way to implement this in Numpy?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
Approach #1 : Using broadcasting
–
def broadcasting_app(a, L, S ): # Window len = L, Stride len/stepsize = S nrows = ((a.size-L)//S)+1 return a[S*np.arange(nrows)[:,None] + np.arange(L)]
Approach #2 : Using more efficient NumPy strides
–
def strided_app(a, L, S ): # Window len = L, Stride len/stepsize = S nrows = ((a.size-L)//S)+1 n = a.strides[0] return np.lib.stride_tricks.as_strided(a, shape=(nrows,L), strides=(S*n,n))
Sample run –
In [143]: a Out[143]: array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) In [144]: broadcasting_app(a, L = 5, S = 3) Out[144]: array([[ 1, 2, 3, 4, 5], [ 4, 5, 6, 7, 8], [ 7, 8, 9, 10, 11]]) In [145]: strided_app(a, L = 5, S = 3) Out[145]: array([[ 1, 2, 3, 4, 5], [ 4, 5, 6, 7, 8], [ 7, 8, 9, 10, 11]])
Method 2
Starting in Numpy 1.20
, we can make use of the new sliding_window_view
to slide/roll over windows of elements.
And coupled with a stepping [::3]
, it simply becomes:
from numpy.lib.stride_tricks import sliding_window_view # values = np.array([1,2,3,4,5,6,7,8,9,10,11]) sliding_window_view(values, window_shape = 5)[::3] # array([[ 1, 2, 3, 4, 5], # [ 4, 5, 6, 7, 8], # [ 7, 8, 9, 10, 11]])
where the intermediate result of the sliding is:
sliding_window_view(values, window_shape = 5) # array([[ 1, 2, 3, 4, 5], # [ 2, 3, 4, 5, 6], # [ 3, 4, 5, 6, 7], # [ 4, 5, 6, 7, 8], # [ 5, 6, 7, 8, 9], # [ 6, 7, 8, 9, 10], # [ 7, 8, 9, 10, 11]])
Method 3
Modified version of @Divakar’s code with checking to ensure that memory is contiguous and that the returned array cannot be modified. (Variable names changed for my DSP application).
def frame(a, framelen, frameadv): """frame - Frame a 1D array a - 1D array framelen - Samples per frame frameadv - Samples between starts of consecutive frames Set to framelen for non-overlaping consecutive frames Modified from Divakar's 10/17/16 11:20 solution: https://stackoverflow.com/questions/40084931/taking-subarrays-from-numpy-array-with-given-stride-stepsize CAVEATS: Assumes array is contiguous Output is not writable as there are multiple views on the same memory """ if not isinstance(a, np.ndarray) or not (a.flags['C_CONTIGUOUS'] or a.flags['F_CONTIGUOUS']): raise ValueError("Input array a must be a contiguous numpy array") # Output nrows = ((a.size-framelen)//frameadv)+1 oshape = (nrows, framelen) # Size of each element in a n = a.strides[0] # Indexing in the new object will advance by frameadv * element size ostrides = (frameadv*n, n) return np.lib.stride_tricks.as_strided(a, shape=oshape, strides=ostrides, writeable=False)
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0