1

i am sorry for the very naive question. I have these lines of numpy code, with a transpose at the beginning, and strides and reshape are used afterward. I wonder if by re-ordering indices in reshape and stride, I could get rid of the first transpose function? Please, can someone help me with that?

import numpy as np

# Input data
array = np.array([[1, 60],
                  [2, 70],
                  [3, 80],
                  [4, 90]])
depth = 3

# Function
n_rows, n_cols = array.shape
array = array.T    # transpose function here
shape = array.shape[:-1] + (array.shape[-1] - depth + 1, depth - 1)
strides = array.strides + (array.strides[-1],)
array = np.lib.stride_tricks.as_strided(array, shape=shape, strides=strides)
reshaped = np.reshape(
    np.swapaxes(array, 0, 1),
    (n_rows-depth+1, (depth - 1) * n_cols),
)

Input and result are then:

array
Out[5]: 
array([[ 1, 60],
       [ 2, 70],
       [ 3, 80],
       [ 4, 90]])


reshaped
Out[6]: 
array([[ 1,  2, 60, 70],
       [ 2,  3, 70, 80]])

3 Answers 3

2

do everything with as_strided

You can't just do all that with just strides.

An array (let's say a 2d array here) is a base address addr, a dtype, a shape (H,W)s, and strides (st1, st2) such as Arr[i,j] is the data of type dtype found at address addr + st1*i + st2*j

Of course, you can have different views of the same array, by not changing base address, and changing the rest. That is what transpose, reshape (sometimes), index (sometimes), and as_strided, do.

For example, Arr.T is the array such as Arr.T[i,j] is at address addr + st2*i + st1*j. So Arr.T is the same as as_strided(Arr, shape=(W,H), strides=(st2, st1)).

Likewise, Arr[:,::2] is the array such as Arr[:,::2][i,j] is at address addr+st1*i+st2*j*2, so it is the same as as_strided(Arr, shape=(H,W//2), strides=(st1, st2*2))

I could add many more examples. But there is one thing common to all of them: at the end, addresses of the new view elements newView[i,j] can be computed by a formula addr + α.i + β.j. And there is no such α, β that could lead to your resulting array. You can see it very easily. reshaped[0,0] is 1, so the one at address addr + st1*0 + st2*0 in the original array. And should be at newAddr + α.0 + β.0 in the new one.
So far so good, `newAddr = addr=

reshaped[0,1] is 2, so element at address addr + st1*1 + st2*0 in the original array. And it should be at address addr + α*0 + β*1 in the new one. So, β=st1. So far, so good.

reshaped[0,2] is 60. So element at address addr + st1*0 + st2*1 in the original array. And it should be at address attr + α*0 + β*2 in the new one. So β=st2//2. Here, we have a problem. Because β=st1. So should be st2=2st1. But you can't control that. You can choose the new view strides, of course. But not the old one. And in your example, we know it is not the case. If st2 were 2st1, then array[0,1] should be the same as array[2,0], which is obviously not the case.

(For α, it is easy, we can see that α=st1 works in all cases).

So, long story short, there isn't a possible α, β that leads to as_strided(Arr, shape=(W,H), strides=(α,β) to be what you want.

do just transpose+as_strided with a single as_strided

There, of course, it is possible. array.T is just the same as array, with shape and strides inverted.

So, if you remove array = array.T, just

shape = array.shape[-1:0:-1] + (array.shape[0] - depth + 1, depth-1)
strides = array.strides[::-1] + (array.strides[0],)

That is, do the exact same thing, but assuming that what you called array.shape[0] or array.strides[0] is now array.shape[-1] or array.strides[-1] now that you got rid of array=array.T

So, alltogether

import numpy as np

# Input data
array = np.array([[1, 60],
                  [2, 70],
                  [3, 80],
                  [4, 90]])
depth = 3

# Function
n_rows, n_cols = array.shape
shape = array.shape[-1:0:-1] + (array.shape[0] - depth + 1, depth-1)
strides = array.strides[::-1] + (array.strides[0],)
array = np.lib.stride_tricks.as_strided(array, shape=shape, strides=strides)
reshaped = np.reshape(
    np.swapaxes(array, 0, 1),
    (n_rows-depth+1, (depth - 1) * n_cols),
)
1
  • Thanks a lot for your help and this clear explanation @chrslg!
    – pierre_j
    Commented Jul 3 at 16:21
1

I like the sliding_window_view wrapper for as_strided. It's easier and safer to use.

In [353]: arr = np.array([[1, 60],
     ...:                   [2, 70],
     ...:                   [3, 80],
     ...:                   [4, 90]])

In [354]: W = np.lib.stride_tricks.sliding_window_view(arr,(2,2))

In [355]: W
Out[355]: 
array([[[[ 1, 60],
         [ 2, 70]]],


       [[[ 2, 70],
         [ 3, 80]]],


       [[[ 3, 80],
         [ 4, 90]]]])

In [356]: W.shape
Out[356]: (3, 1, 2, 2)

sliding_window_view produces more than what you need, but can be easily indexed:

In [357]: W[:2,0]
Out[357]: 
array([[[ 1, 60],
        [ 2, 70]],

       [[ 2, 70],
        [ 3, 80]]])

Now it's just a mater of tranposing (I had to play with this), and a final reshape:

In [358]: W[:2,0].transpose(0,2,1)
Out[358]: 
array([[[ 1,  2],
        [60, 70]],

       [[ 2,  3],
        [70, 80]]])

In [359]: W[:2,0].transpose(0,2,1).reshape(2,4)
Out[359]: 
array([[ 1,  2, 60, 70],
       [ 2,  3, 70, 80]])

Or with an intial transpose, just subset and reshape:

In [366]: W = np.lib.stride_tricks.sliding_window_view(arr.T,(2,2))

In [367]: W[0,:2].reshape(2,4)
Out[367]: 
array([[ 1,  2, 60, 70],
       [ 2,  3, 70, 80]])

So I still need one transpose/swap.

1
  • Thanks for proposing this solution @hpaulj! Simplifying the code by using this wrapped does seem appealing. I will investigate!
    – pierre_j
    Commented Jul 3 at 16:22
0
import numpy as np

# Input data
array = np.array([[1, 60],
                  [2, 70],
                  [3, 80],
                  [4, 90]])
depth = 3

n_rows,n_cols =array.shape 
print(array.shape)#(4, 2)

shape = (n_rows - depth +1, depth, n_cols) 
print(shape)#(2, 3, 2)

strides = (array.strides[0],array.strides[0],array.strides[1])
print(strides)#(8, 8, 4)
array_strided = np.lib.stride_tricks.as_strided(x =array, shape=shape, strides=strides)
print(array_strided)
'''
[[[ 1 60]
  [ 2 70]
  [ 3 80]]

 [[ 2 70]
  [ 3 80]
  [ 4 90]]]
'''
print(array_strided.shape)#(2, 3, 2)

windowed_subarray = array_strided[:,:-1,:]
print(windowed_subarray)
'''
[[[ 1 60]
  [ 2 70]]

 [[ 2 70]
  [ 3 80]]]

'''
flattened  =array_strided[:,:-1,:].reshape(-1,(depth -1)* n_cols)
print(flattened)
'''
[[ 1 60  2 70]
 [ 2 70  3 80]]
'''

Not the answer you're looking for? Browse other questions tagged or ask your own question.