numpyreshapetransposestride

Equivalence between reshape, strides and transpose? (numpy)


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]])

Solution

  • 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),
    )