NumPy·Lesson 11·10 min·0/4 exercises

Adding, removing and moving axes

newaxis, squeeze, moveaxis — the tools for making shapes line up.

np.newaxisnp.expand_dims.squeeze()np.swapaxesnp.moveaxis.transpose(axes)

Watch it happen

Play it through, or step back and forth yourself.

x (a stray length-1 axis)
0
0
1
2
3
5
6
7
8
shape (1, 4)

Operations leave length-1 axes lying around all the time — a[0:1], keepdims=True, loading a single image from a batch. Shape (1, 4) holds the same four numbers as (4,) but behaves differently.

The idea

Reshaping changes how many elements sit on each axis. This lesson is about changing how many axes there are and what order they're in — which sounds like bookkeeping until you meet the two situations where it's the whole job.

Removing length-1 axes

Singleton axes accumulate. a[0:1] leaves one, keepdims=True leaves one, loading a single item out of a batch leaves one.

x = np.zeros((1, 4, 1))
x.squeeze().shape          # (4,)   — drops every length-1 axis
x.squeeze(axis=0).shape    # (4, 1) — drops only that one

Prefer the explicit axis= form in real code. A bare squeeze() silently does nothing when there's nothing to drop, so a shape bug can slide straight past it; naming the axis raises instead.

Adding one

Three spellings, one meaning:

x[:, np.newaxis]           # the readable one
x[:, None]                 # np.newaxis IS None — same thing
np.expand_dims(x, axis=1)  # the function form

The position matters: x[:, None] turns (3,) into (3, 1), while x[None, :] gives (1, 3).

Why any of this matters

Here's the real reason you'll reach for it. Broadcasting lines shapes up from the right. So a (3,) array spreads across each row — and if you wanted it to spread down each column, you have to give it a second axis:

m = np.zeros((3, 3))
m + np.array([1, 2, 3])              # 1,2,3 across every row
m + np.array([1, 2, 3])[:, None]     # 1,2,3 down every column

Same three numbers, perpendicular results. Whenever a broadcast comes out transposed from what you expected, this is why.

Reordering axes

.T reverses all axes. For 2-D that's the transpose you want; for 3-D it turns (2, 3, 4) into (4, 3, 2), which is almost never what anyone means.

np.swapaxes(x, 0, 1)     # exchange two specific axes
np.moveaxis(x, 0, 2)     # take axis 0, put it at position 2
x.transpose(1, 2, 0)     # give the full new order

moveaxis is the one worth remembering, because it reads as a sentence and because of this:

photo.shape                          # (120, 160, 3) — height, width, channels
np.moveaxis(photo, -1, 0).shape      # (3, 120, 160) — channels first

Images arrive channels-last; PyTorch and most convolution code want channels-first. That one line is the conversion, and like every axis operation here it's a view — no data moves.

Practice

Write it yourself. The answer is there when you want it.

Putting the kettle on…

Starting up…

Write it yourself

not graded

Make a (1, 4, 1) of zeros and compare .squeeze() with .squeeze(axis=0). Then add [1, 2, 3] to a (3, 3) twice: once as it is, which lands across the rows, and once with [:, None], which lands down them. Finish by moving a (120, 160, 3) photo's channel axis to the front with np.moveaxis and printing the shape.

Write something and press Run — the output appears here.

Your turn

4 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.

Turn v from shape (4,) into a column of shape (4, 1).

your answer

np.zeros((1, 3, 1)) has two useless axes. Return its shape after removing all length-1 axes.

your answer

Add [10, 20, 30] down the rows of np.zeros((3, 4), dtype=np.int64) — so row 0 gains 10, row 1 gains 20, row 2 gains 30.

your answer

np.zeros((120, 160, 3)) is an image. Move the channel axis to the front and return the resulting shape.

your answer