Skip to content

Transpose, swapaxes, newaxis, expand_dims and squeeze

The functions in this lesson do not change any values. They change how the axes of an array are arranged: swapping rows and columns, or adding and removing dimensions of size 1. You need this all the time to make shapes fit together, especially for broadcasting (lesson 10) and when working with images.

How to run the examples

Create a new file named transpose_dimensions.py in your NumpyTutorials folder (see the setup in lesson 01). Any name works; this one just matches the topic. Start the file with these lines:

import numpy as np

Then copy the examples of this page into the same file, one after another, in the order shown, and run it with python transpose_dimensions.py (with your virtual environment active). Some examples use variables created in earlier ones, so keep everything in the one file. Each Output box shows what that example prints.

1. .T on a 2D array

Transposing turns rows into columns and columns into rows.

marks = np.array([[78, 85, 92],      # student 0: Maths, Science, English
                  [88, 79, 95]])     # student 1

print("marks (students x subjects) shape", marks.shape, ":\n", marks)
print("marks.T (subjects x students) shape", marks.T.shape, ":\n", marks.T)
print("np.transpose(marks) is the same:", np.array_equal(np.transpose(marks), marks.T))

row = np.array([1, 2, 3])
print("1D .T does nothing:", row.T, row.T.shape)
Output
marks (students x subjects) shape (2, 3) :
 [[78 85 92]
 [88 79 95]]
marks.T (subjects x students) shape (3, 2) :
 [[78 88]
 [85 79]
 [92 95]]
np.transpose(marks) is the same: True
1D .T does nothing: [1 2 3] (3,)
  • marks is students x subjects, shape (2, 3). marks.T is subjects x students, shape (3, 2).
  • np.transpose(marks) is the function form of marks.T.
  • A 1D array has only one axis, so .T does nothing. To turn a 1D row into a column, add a dimension (section 5).

2. .T is a view

t = marks.T
t[0, 0] = 0
print("changed marks.T[0, 0] -> marks:\n", marks)
Output
changed marks.T[0, 0] -> marks:
 [[ 0 85 92]
 [88 79 95]]

Transposing does not copy anything; it reads the same data in a different order. So changing t[0, 0] changed marks[0, 0]. If you need an independent transposed array, use marks.T.copy().

3. transpose(axes) on a 3D array

With 3 or more dimensions, .T reverses all axes. To choose your own order, pass the axis numbers to transpose().

# 2 stores x 3 months x 4 products
data = np.arange(24).reshape(2, 3, 4)
print("data.shape                   :", data.shape)
print("data.T.shape (reverse all)   :", data.T.shape)
print("transpose(1, 0, 2).shape     :", data.transpose(1, 0, 2).shape, "(months x stores x products)")
print("month 0, both stores:\n", data.transpose(1, 0, 2)[0])
Output
data.shape                   : (2, 3, 4)
data.T.shape (reverse all)   : (4, 3, 2)
transpose(1, 0, 2).shape     : (3, 2, 4) (months x stores x products)
month 0, both stores:
 [[ 0  1  2  3]
 [12 13 14 15]]

data has axes (0 = stores, 1 = months, 2 = products). transpose(1, 0, 2) means "the new axis 0 is the old axis 1": the result is months x stores x products. Now [0] gives month 0 for both stores in one table.

4. swapaxes

When you only want to exchange two axes, swapaxes is simpler than writing out every axis:

print("swapaxes(0, 2).shape:", data.swapaxes(0, 2).shape)
print("swapaxes(1, 2).shape:", data.swapaxes(1, 2).shape)
print("2D swapaxes(0, 1) equals .T:", np.array_equal(marks.swapaxes(0, 1), marks.T))
Output
swapaxes(0, 2).shape: (4, 3, 2)
swapaxes(1, 2).shape: (2, 4, 3)
2D swapaxes(0, 1) equals .T: True

For a 2D array, swapping axes 0 and 1 is exactly the transpose.

5. np.newaxis: add a dimension

np.newaxis inside the brackets inserts a new axis of size 1 at that position.

prices = np.array([10, 20, 30])
print("prices           shape", prices.shape, ":", prices)
print("prices[np.newaxis, :] shape", prices[np.newaxis, :].shape, "(row vector):\n", prices[np.newaxis, :])
print("prices[:, np.newaxis] shape", prices[:, np.newaxis].shape, "(column vector):\n", prices[:, np.newaxis])
print("np.newaxis is None:", np.newaxis is None, "-> prices[:, None] works too")
Output
prices           shape (3,) : [10 20 30]
prices[np.newaxis, :] shape (1, 3) (row vector):
 [[10 20 30]]
prices[:, np.newaxis] shape (3, 1) (column vector):
 [[10]
 [20]
 [30]]
np.newaxis is None: True -> prices[:, None] works too
ExpressionShapeLooks like
prices(3,)plain 1D array
prices[np.newaxis, :](1, 3)a row vector: 1 row, 3 columns
prices[:, np.newaxis](3, 1)a column vector: 3 rows, 1 column

np.newaxis is just another name for None, so prices[:, None] is the same thing. You will see both in real code.

6. Why add a dimension? Column x row = table

quantities = np.array([1, 2, 3, 4])
print("price table (rows = quantity, columns = item):\n", quantities[:, np.newaxis] * prices)
Output
price table (rows = quantity, columns = item):
 [[ 10  20  30]
 [ 20  40  60]
 [ 30  60  90]
 [ 40  80 120]]

A column of shape (4, 1) times a row of shape (3,) gives a full (4, 3) table: every quantity times every price. This is broadcasting, and adding an axis is how you control it. Lesson 10 explains the rules.

7. np.expand_dims

np.expand_dims(arr, axis) does the same as np.newaxis, written as a function. Some people find it easier to read.

print("expand_dims(prices, axis=0).shape :", np.expand_dims(prices, axis=0).shape)
print("expand_dims(prices, axis=1).shape :", np.expand_dims(prices, axis=1).shape)
print("expand_dims(marks, axis=0).shape  :", np.expand_dims(marks, axis=0).shape, "(a batch of 1 table)")
Output
expand_dims(prices, axis=0).shape : (1, 3)
expand_dims(prices, axis=1).shape : (3, 1)
expand_dims(marks, axis=0).shape  : (1, 2, 3) (a batch of 1 table)

A common use is turning one item into a "batch of 1", for example one table (2, 3) into (1, 2, 3), because many machine-learning libraries expect a batch axis first.

8. np.squeeze: remove dimensions of size 1

squeeze is the opposite: it removes axes of size 1.

boxed = np.array([[[5], [6], [7]]])
print("boxed.shape                :", boxed.shape)
print("squeeze(boxed)             :", np.squeeze(boxed), np.squeeze(boxed).shape)
print("squeeze(boxed, axis=0).shape:", np.squeeze(boxed, axis=0).shape)

try:
    np.squeeze(boxed, axis=1)             # axis 1 has size 3, not 1
except ValueError as error:
    print("Error:", error)
Output
boxed.shape                : (1, 3, 1)
squeeze(boxed)             : [5 6 7] (3,)
squeeze(boxed, axis=0).shape: (3, 1)
Error: cannot select an axis to squeeze out which has size not equal to one
  • Without axis, every size-1 axis is removed: (1, 3, 1) becomes (3,).
  • With axis=0, only that axis is removed: (3, 1).
  • You cannot squeeze an axis whose size is not 1. NumPy refuses rather than losing data.

9. Real example: marks compared to each student's average

marks = np.array([[78, 85, 92],
                  [88, 79, 95],
                  [65, 90, 81]])
average = marks.mean(axis=1)                      # shape (3,)
print("average per student :", average.round(1))
print("difference from own average:\n", (marks - average[:, np.newaxis]).round(1))
Output
average per student : [85.  87.3 78.7]
difference from own average:
 [[ -7.    0.    7. ]
 [  0.7  -8.3   7.7]
 [-13.7  11.3   2.3]]

average has shape (3,), one value per student. To subtract each student's own average from their row, we need it as a column: average[:, np.newaxis] has shape (3, 1). Now every row gets its own average subtracted. Positive numbers mean "better than this student's average".

Exercises

Try these exercises on your own before moving on:

  1. Make a 2x5 array and print its transpose and both shapes.
  2. An image array has shape (height, width, channels) = (4, 6, 3). Use transpose to make it (channels, height, width).
  3. Turn np.array([1, 2, 3, 4]) into shape (4, 1) in three ways (newaxis, expand_dims, reshape).
  4. Squeeze an array of shape (1, 3, 1, 2). What is the new shape?

Summary

  • .T swaps rows and columns; it is a view, not a copy.
  • transpose(1, 0, 2) puts the axes in any order; swapaxes(a, b) exchanges two.
  • arr[:, np.newaxis] makes a column, arr[np.newaxis, :] makes a row; None works the same.
  • np.expand_dims adds an axis; np.squeeze removes size-1 axes.
  • Adding an axis is how you make shapes fit for broadcasting.

Next: Lesson 08: Concatenate, Stack and Split.