Search code examples
pythonmatrix3d2dreshape

How to reshape a numpy matrix from 2D to 3D column-wise?


I do not understand the reshape function in numpy in Python. I have got the following problem:

I want to reshape the (6, 2) array

A = np.c_[np.arange(1, 7), np.arange(6, 12)]

Which is

array([[ 1,  6],
       [ 2,  7],
       [ 3,  8],
       [ 4,  9],
       [ 5, 10],
       [ 6, 11]])

Into a (2, 3, 2) array like

array([[[ 1,  2,  3],
        [ 4,  5,  6]],

       [[ 6,  7,  8],
        [ 9, 10, 11]]])

I have tried

np.reshape(A, (2, 3, 2), order='F')

But the result was not what I am looking for. Instead, it was:

array([[[ 1,  6],
        [ 3,  8],
        [ 5, 10]],

       [[ 2,  7],
        [ 4,  9],
        [ 6, 11]]])

Solution

  • There are two problems here:

    1. The shape you want is actually (2, 2, 3), not (2, 3, 2).
    2. order="F" doesn't do what you think it does. What you actually want is to transpose the array using A.T.

    Also, you can use A.reshape(...) instead of np.reshape(A, ...).

    Here is the code:

    import numpy as np
    
    A = np.c_[np.arange(1,7), np.arange(6,12)]
    print(A)
    print("\n->\n")
    print(A.T.reshape((2, 2, 3)))
    

    Which gives:

    array([[ 1,  6],
           [ 2,  7],
           [ 3,  8],
           [ 4,  9],
           [ 5, 10],
           [ 6, 11]])
    
    ->
    
    array([[[ 1,  2,  3],
            [ 4,  5,  6]],
    
           [[ 6,  7,  8],
            [ 9, 10, 11]]])
    

    Explanation of order="F" vs. .T

    Try A.ravel(order="F") to see what the elements of the array are in the "F" order. You will get:

    array([[ 1,  6],
           [ 2,  7],
           [ 3,  8],
           [ 4,  9],
           [ 5, 10],
           [ 6, 11]])
    
    ->
    
    array([ 1,  2,  3,  4,  5,  6,  6,  7,  8,  9, 10, 11])
    

    Now if you apply a normal order after this, you get your expected result:

    A.ravel(order="F").reshape((2, 2, 3)) -> 
    
    array([[[ 1,  2,  3],
            [ 4,  5,  6]],
    
           [[ 6,  7,  8],
            [ 9, 10, 11]]])
    

    The problem is, order="F" also affects how the ravelled elements are added back into the array. Not only are the elements taken out column-wise, they are added back in column-wise as well. So:

    [[[], []], [[], []]] -> 
    [[[1], []], [[], []]] -> 
    [[[1], []], [[2], []]] -> 
    [[[1], [3]], [[2], []]] -> 
    [[[1], [3]], [[2], [4]]] -> 
    [[[1, 5], [3]], [[2], [4]]] -> 
    [[[1, 5], [3]], [[2, 6], [4]]] -> 
    [[[1, 5], [3, 7]], [[2, 6], [4]]] -> 
    [[[1, 5], [3, 6]], [[2, 6], [4, 7]]] -> 
    [[[1, 5, 8], [3, 6]], [[2, 6], [4, 7]]] -> 
    [[[1, 5, 8], [3, 6]], [[2, 6, 9], [4, 7]]] -> 
    [[[1, 5, 8], [3, 6, 10]], [[2, 6, 9], [4, 7]]] -> 
    [[[1, 5, 8], [3, 6, 10]], [[2, 6, 9], [4, 7, 11]]]
    

    Not:

    [[[1], []], [[], []]] -> 
    [[[1, 2], []], [[], []]] -> 
    [[[1, 2, 3], []], [[], []]] -> 
    [[[1, 2, 3], [4]], [[], []]] -> 
    [[[1, 2, 3], [4, 5]], [[], []]] -> 
    [[[1, 2, 3], [4, 5, 6]], [[], []]] -> 
    [[[1, 2, 3], [4, 5, 6]], [[6], []]] -> 
    [[[1, 2, 3], [4, 5, 6]], [[6, 7], []]] -> 
    [[[1, 2, 3], [4, 5, 6]], [[6, 7, 8], []]] -> 
    [[[1, 2, 3], [4, 5, 6]], [[6, 7, 8], [9]]] -> 
    [[[1, 2, 3], [4, 5, 6]], [[6, 7, 8], [9, 10]]] -> 
    [[[1, 2, 3], [4, 5, 6]], [[6, 7, 8], [9, 10, 11]]]
    

    Therefore, you end up with an odd result. If you just want to take the elements out with order="F" but not add them back in that way, transposing the array will have the same effect.