How does NumPy's transpose() method permute the axes of an array?
Asked Answered
S

6

123
In [28]: arr = np.arange(16).reshape((2, 2, 4))

In [29]: arr
Out[29]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])


In [32]: arr.transpose((1, 0, 2))
Out[32]: 
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],

       [[ 4,  5,  6,  7],
        [12, 13, 14, 15]]])

When we pass a tuple of integers to the transpose() function, what happens?

To be specific, this is a 3D array: how does NumPy transform the array when I pass the tuple of axes (1, 0 ,2)? Can you explain which row or column these integers refer to? And what are axis numbers in the context of NumPy?

Secunda answered 16/8, 2015 at 10:51 Comment(3)
docs.scipy.org/doc/numpy/reference/generated/…Taxaceous
0 is first axis, 1 is second, 2 is third, etc... The axes parameter to transpose provides the new order you want them arranged in, in your example: first the second, then the first, then the third.Braga
In the 3d display there are blocks, rows and columns. Your transpose has switched the order of the blocks and rows, leaving columns unchanged. What used to be the 2nd row of the 1st block becomes the 1st row of the 2nd block.Schneider
P
286

To transpose an array, NumPy just swaps the shape and stride information for each axis. Here are the strides:

>>> arr.strides
(64, 32, 8)

>>> arr.transpose(1, 0, 2).strides
(32, 64, 8)

Notice that the transpose operation swapped the strides for axis 0 and axis 1. The lengths of these axes were also swapped (both lengths are 2 in this example).

No data needs to be copied for this to happen; NumPy can simply change how it looks at the underlying memory to construct the new array.


Visualising strides

The stride value represents the number of bytes that must be travelled in memory in order to reach the next value of an axis of an array.

Now, our 3D array arr looks this (with labelled axes):

enter image description here

This array is stored in a contiguous block of memory; essentially it is one-dimensional. To interpret it as a 3D object, NumPy must jump over a certain constant number of bytes in order to move along one of the three axes:

enter image description here

Since each integer takes up 8 bytes of memory (we're using the int64 dtype), the stride value for each dimension is 8 times the number of values that we need to jump. For instance, to move along axis 1, four values (32 bytes) are jumped, and to move along axis 0, eight values (64 bytes) need to be jumped.

When we write arr.transpose(1, 0, 2) we are swapping axes 0 and 1. The transposed array looks like this:

enter image description here

All that NumPy needs to do is to swap the stride information for axis 0 and axis 1 (axis 2 is unchanged). Now we must jump further to move along axis 1 than axis 0:

enter image description here

This basic concept works for any permutation of an array's axes. The actual code that handles the transpose is written in C and can be found here.

Phrasal answered 16/8, 2015 at 11:29 Comment(0)
B
12

As explained in the documentation:

By default, reverse the dimensions, otherwise permute the axes according to the values given.

So you can pass an optional parameter axes defining the new order of dimensions.

E.g. transposing the first two dimensions of an RGB VGA pixel array:

 >>> x = np.ones((480, 640, 3))
 >>> np.transpose(x, (1, 0, 2)).shape
 (640, 480, 3)
Bauxite answered 16/8, 2015 at 11:23 Comment(2)
hi,thank you for your answer. However, I still can not understand this problem, as you can see, arr's shape is (2,2,4) and arr.transpose(1,0,2) is (2,2,4) too.To be specify,this is a 3-d array,how to transform the array when I pass (1,0,2),can you explain which row or column the 1,0,2 refers to ? And what is axis numbers in terms of numpy?Secunda
@FrankHu visualize in 3d space, so x, y axis rotation is what happened here. (1,0,2) was transposed from (0,1,2), so, first 2 axis get switched. 0 is the index number of axis. Say, x,y,z maps to 0,1,2Consignment
O
6

In C notation, your array would be:

int arr[2][2][4]

which is an 3D array having 2 2D arrays. Each of those 2D arrays has 2 1D array, each of those 1D arrays has 4 elements.

So you have three dimensions. The axes are 0, 1, 2, with sizes 2, 2, 4. This is exactly how numpy treats the axes of an N-dimensional array.

So, arr.transpose((1, 0, 2)) would take axis 1 and put it in position 0, axis 0 and put it in position 1, and axis 2 and leave it in position 2. You are effectively permuting the axes:

0 -\/-> 0
1 -/\-> 1
2 ----> 2

In other words, 1 -> 0, 0 -> 1, 2 -> 2. The destination axes are always in order, so all you need is to specify the source axes. Read off the tuple in that order: (1, 0, 2).

In this case your new array dimensions are again [2][2][4], only because axes 0 and 1 had the same size (2).

More interesting is a transpose by (2, 1, 0) which gives you an array of [4][2][2].

0 -\ /--> 0
1 --X---> 1
2 -/ \--> 2

In other words, 2 -> 0, 1 -> 1, 0 -> 2. Read off the tuple in that order: (2, 1, 0).

>>> arr.transpose((2,1,0))
array([[[ 0,  8],
        [ 4, 12]],

       [[ 1,  9],
        [ 5, 13]],

       [[ 2, 10],
        [ 6, 14]],

       [[ 3, 11],
        [ 7, 15]]])

You ended up with an int[4][2][2].

You'll probably get better understanding if all dimensions were of different size, so you could see where each axis went.

Why is the first inner element [0, 8]? Because if you visualize your 3D array as two sheets of paper, 0 and 8 are lined up, one on one paper and one on the other paper, both in the upper left. By transposing (2, 1, 0) you're saying that you want the direction of paper-to-paper to now march along the paper from left to right, and the direction of left to right to now go from paper to paper. You had 4 elements going from left to right, so now you have four pieces of paper instead. And you had 2 papers, so now you have 2 elements going from left to right.

Sorry for the terrible ASCII art. ¯\_(ツ)_/¯

Overcurious answered 15/4, 2018 at 23:57 Comment(2)
Am I right in thinking the tuple you give to transpose() isn't a mathematical permutation or anything? It's literally just instructions saying "make the axes now be in these positions"? E.g. for .transpose(p, q, r, s) you're saying "put axis p as 0th, q as 1st, r as 2nd and s as 3rd"? Or seen another way b = a.transpose(axes) means b.shape == tuple(a.shape[i] for i in axes)?Goodwife
The idea of swapping axis with ASCII art clarifies my understanding. Thanks a lotKippie
J
4

It seems the question and the example originates from the book Python for Data Analysis by Wes McKinney. This feature of transpose is mentioned in Chapter 4.1. Transposing Arrays and Swapping Axes.

For higher dimensional arrays, transpose will accept a tuple of axis numbers to permute the axes (for extra mind bending).

Here "permute" means "rearrange", so rearranging the order of axes.

The numbers in .transpose(1, 0, 2) determines how the order of axes are changed compared to the original. By using .transpose(1, 0, 2), we mean, "Change the 1st axis with the 2nd." If we use .transpose(0, 1, 2), the array will stay the same because there is nothing to change; it is the default order.

The example in the book with a (2, 2, 4) sized array is not very clear since 1st and 2nd axes has the same size. So the end result doesn't seem to change except the reordering of rows arr[0, 1] and arr[1, 0].

If we try a different example with a 3 dimensional array with each dimension having a different size, the rearrangement part becomes more clear.

In [2]: x = np.arange(24).reshape(2, 3, 4)

In [3]: x
Out[3]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])

In [4]: x.transpose(1, 0, 2)
Out[4]: 
array([[[ 0,  1,  2,  3],
        [12, 13, 14, 15]],

       [[ 4,  5,  6,  7],
        [16, 17, 18, 19]],

       [[ 8,  9, 10, 11],
        [20, 21, 22, 23]]])

Here, original array sizes are (2, 3, 4). We changed the 1st and 2nd, so it becomes (3, 2, 4) in size. If we look closer to see how the rearrangement exactly happened; arrays of numbers seems to have changed in a particular pattern. Using the paper analogy of @RobertB, if we were to take the 2 chunks of numbers, and write each one on sheets, then take one row from each sheet to construct one dimension of the array, we would now have a 3x2x4-sized array, counting from the outermost to the innermost layer.

[ 0,  1,  2,  3] \ [12, 13, 14, 15]

[ 4,  5,  6,  7] \ [16, 17, 18, 19]

[ 8,  9, 10, 11] \ [20, 21, 22, 23]

It could be a good idea to play with different sized arrays, and change different axes to gain a better intuition of how it works.

Joletta answered 30/12, 2019 at 21:52 Comment(1)
I use this command to get the channels from .tif images: (red, green, blue, alpha) = np.transpose(img, axes=(2,0,1)) What modifications should I do in np.transpose, If I want to read 1-channel .tif for instance: gray = np.transpose(????)Coheman
Z
3

I ran across this in Python for Data Analysis by Wes McKinney as well.

I will show the simplest way of solving this for a 3-dimensional tensor, then describe the general approach that can be used for n-dimensional tensors.

Simple 3-dimensional tensor example

Suppose you have the (2,2,4)-tensor

[[[ 0  1  2  3]
  [ 4  5  6  7]]

 [[ 8  9 10 11]
  [12 13 14 15]]]

If we look at the coordinates of each point, they are as follows:

[[[ (0,0,0)  (0,0,1)  (0,0,2)  (0,0,3)]
  [ (0,1,0)  (0,1,1)  (0,1,2)  (0,1,3)]]

 [[ (1,0,0)  (1,0,1) (1,0,2) (0,0,3)]
  [ (1,1,0)  (1,1,1) (1,1,2) (0,1,3)]]

Now suppose that the array above is example_array and we want to perform the operation: example_array.transpose(1,2,0)

For the (1,2,0)-transformation, we shuffle the coordinates as follows (note that this particular transformation amounts to a "left-shift":

(0,0,0)  ->  (0,0,0)
(0,0,1)  ->  (0,1,0)
(0,0,2)  ->  (0,2,0)
(0,0,3)  ->  (0,3,0)
(0,1,0)  ->  (1,0,0)
(0,1,1)  ->  (1,1,0)
(0,1,2)  ->  (1,2,0)
(0,1,3)  ->  (1,3,0)
(1,0,0)  ->  (0,0,1)
(1,0,1)  ->  (0,1,1)
(1,0,2)  ->  (0,2,1)
(0,0,3)  ->  (0,3,0)
(1,1,0)  ->  (1,0,1)
(1,1,1)  ->  (1,1,1)
(1,1,2)  ->  (1,2,1)
(0,1,3)  ->  (1,3,0)

Now, for each original value, place it into the shifted coordinates in the result matrix.

For instance, the value 10 has coordinates (1, 0, 2) in the original matrix and will have coordinates (0, 2, 1) in the result matrix. It is placed into the first 2d tensor submatrix in the third row of that submatrix, in the second column of that row.

Hence, the resulting matrix is:

array([[[ 0,  8],
        [ 1,  9],
        [ 2, 10],
        [ 3, 11]],

       [[ 4, 12],
        [ 5, 13],
        [ 6, 14],
        [ 7, 15]]])

General n-dimensional tensor approach

For n-dimensional tensors, the algorithm is the same. Consider all of the coordinates of a single value in the original matrix. Shuffle the axes for that individual coordinate. Place the value into the resulting, shuffled coordinates in the result matrix. Repeat for all of the remaining values.

Zantos answered 1/7, 2021 at 20:3 Comment(0)
T
-2

To summarise a.transpose()[i,j,k] = a[k,j,i]

a = np.array( range(24), int).reshape((2,3,4))
a.shape gives (2,3,4)
a.transpose().shape gives (4,3,2)  shape tuple is reversed.

when is a tuple parameter is passed axes are permuted according to the tuple. For example

a = np.array( range(24), int).reshape((2,3,4))

a[i,j,k] equals a.transpose((2,0,1))[k,i,j]

axis 0 takes 2nd place

axis 1 takes 3rd place

axis 2 tales 1st place

of course we need to take care that values in tuple parameter passed to transpose are unique and in range(number of axis)

Thoracotomy answered 28/2, 2018 at 21:42 Comment(2)
You haven't addressed the OP's question, which is what happens when you transpose with axes specified.Overcurious
added response to the question when axes is specified.Thoracotomy

© 2022 - 2024 — McMap. All rights reserved.