newaxis, expand_dims & squeeze
Add a length-1 dimension with np.newaxis / None or np.expand_dims, and remove length-1 dimensions with np.squeeze — the key to making shapes broadcast.
Five players each have a form rating: [1.15, 1.0, 0.95, 1.05, 1.1] — shape (5,). You want to multiply each player's row of a (5, 14) scores table by his rating. NumPy refuses, because broadcasting lines up shapes from the right (14 vs 5). The fix is not to change the data, but to add an extra dimension so the ratings become a column (5, 1). Adding and removing such size-1 dimensions is a small but everyday NumPy skill.
Definition
A length-1 dimension (also called a singleton axis) is an axis of size 1. It adds no new data but changes the array's shape and number of dimensions (
ndim).np.newaxis(an alias forNone) andnp.expand_dimsinsert a length-1 axis;np.squeezeremoves length-1 axes.
- No new data —
(5,)and(5, 1)and(1, 5)all hold the same 5 numbers. - Changes shape — but shape decides how broadcasting, matrix multiplication and many functions behave.
- Views — all three return views; nothing is copied.
text
v = [1.15, 1.0, 0.95, 1.05, 1.1] shape (5,) ndim 1
v[np.newaxis, :] -> [[1.15, 1.0, 0.95, 1.05, 1.1]] shape (1, 5) a row
v[:, np.newaxis] -> [[1.15]
[1.0 ]
[0.95] shape (5, 1) a column
[1.05]
[1.1 ]]Syntax
python
a[np.newaxis, :] # new axis at the front (5,) -> (1, 5)
a[:, np.newaxis] # new axis at the end (5,) -> (5, 1)
a[:, None] # None works exactly like np.newaxis
np.expand_dims(a, axis) # insert a length-1 axis at position 'axis'
np.squeeze(a, axis=None) # remove length-1 axes (all, or only 'axis')
a.squeeze(axis=None)| Parameter | Meaning | Default |
|---|---|---|
np.newaxis / None | used inside square brackets to mark where a new axis goes | — |
axis (expand_dims) | position of the new axis in the result; an int or a tuple; negative counts from the end | — required |
axis (squeeze) | which length-1 axis/axes to remove; None = remove all of them | None |
Example — adding a dimension
python
import numpy as np
form = np.array([1.15, 1.0, 0.95, 1.05, 1.1])
print("original :", form.shape, "ndim", form.ndim)
print("[np.newaxis] :", form[np.newaxis, :].shape)
print("[:, newaxis] :", form[:, np.newaxis].shape)
print("[:, None] :", form[:, None].shape)
print("expand_dims 0:", np.expand_dims(form, axis=0).shape)
print("expand_dims 1:", np.expand_dims(form, axis=1).shape)
print("np.newaxis is None:", np.newaxis is None)text
original : (5,) ndim 1
[np.newaxis] : (1, 5)
[:, newaxis] : (5, 1)
[:, None] : (5, 1)
expand_dims 0: (1, 5)
expand_dims 1: (5, 1)
np.newaxis is None: TrueHow it works: wherever you put np.newaxis in the index, a size-1 axis appears in that position. np.expand_dims(form, axis=1) does the same as form[:, np.newaxis] but reads more clearly in some code.
Example — why it matters: per-player broadcasting
python
import numpy as np
scores = np.array([
[72, 45, 88, 31],
[56, 0, 74, 88],
[45, 32, 0, 67],
])
form = np.array([1.1, 1.0, 0.9]) # one rating per player, shape (3,)
try:
scores * form
except ValueError as e:
print("ValueError:", e)
adjusted = scores * form[:, np.newaxis] # (3, 4) * (3, 1) -> works
print(adjusted.round(1))text
ValueError: operands could not be broadcast together with shapes (3,4) (3,)
[[79.2 49.5 96.8 34.1]
[56. 0. 74. 88. ]
[40.5 28.8 0. 60.3]]How it works: comparing shapes from the right, (3, 4) vs (3,) compares 4 with 3 → fail. After form[:, np.newaxis] the shape is (3, 1): 4 vs 1 (1 stretches) and 3 vs 3 → OK. Every score in row i is multiplied by form[i].
Example — removing dimensions with squeeze
Many operations leave behind useless size-1 axes — e.g. picking one player with a slice, or an image with a single colour channel.
python
import numpy as np
one_player = np.array([[72, 45, 88, 31]]) # shape (1, 4)
print(one_player.shape, "->", one_player.squeeze().shape)
column = np.array([[72], [45], [88]]) # shape (3, 1)
print(column.shape, "->", np.squeeze(column).shape)
gray = np.zeros((1, 4, 4, 1)) # batch of 1 image, 1 channel
print(gray.shape, "-> all:", gray.squeeze().shape, " axis=0 only:", gray.squeeze(axis=0).shape)
try:
np.zeros((3, 4)).squeeze(axis=0)
except ValueError as e:
print("ValueError:", e)text
(1, 4) -> (4,)
(3, 1) -> (3,)
(1, 4, 4, 1) -> all: (4, 4) axis=0 only: (4, 4, 1)
ValueError: cannot select an axis to squeeze out which has size not equal to oneHow it works: squeeze() with no axis removes every size-1 axis. squeeze(axis=0) removes only that one. Asking to squeeze an axis whose size isn't 1 is an error.
Example — several axes at once and newaxis vs reshape
python
import numpy as np
v = np.array([72, 45, 88])
print(np.expand_dims(v, axis=(0, 2)).shape) # tuple of positions
print(v[np.newaxis, :, np.newaxis].shape) # same thing with newaxis
print(v.reshape(1, 3, 1).shape) # same thing with reshape
print(np.array_equal(v[:, np.newaxis], v.reshape(-1, 1)))text
(1, 3, 1)
(1, 3, 1)
(1, 3, 1)
TrueCommon mistakes
- ❌
np.newaxis(form)orform.newaxis. ✅ It goes inside the brackets:form[:, np.newaxis]. - ❌ Trying
form.Tto make a column. ✅.Tdoes nothing to 1D — useform[:, np.newaxis]. - ❌
a.squeeze(axis=0)when axis 0 has size 3 →ValueError: cannot select an axis to squeeze out which has size not equal to one. ✅ Checka.shapefirst. - ❌ Calling
squeeze()with no axis on data where a size-1 axis is meaningful (e.g. a batch that happens to contain one image) — it disappears too. ✅ Pass the exactaxis.
Comparison — four ways to make a (5,) array a column (5, 1)
| Way | Code | Notes |
|---|---|---|
| newaxis | v[:, np.newaxis] | most common in NumPy code |
| None | v[:, None] | identical, shorter |
| expand_dims | np.expand_dims(v, axis=1) | explicit, works with tuples of axes |
| reshape | v.reshape(-1, 1) | also works; from the broadcasting notebook |
Interview tip
Q: What is the difference between shapes (5,), (5, 1) and (1, 5)?
A: All hold 5 values. (5,) is a 1D array with no row/column orientation. (5, 1) is a 2D column (5 rows, 1 column) and broadcasts down the rows of a (5, n) array. (1, 5) is a 2D row and broadcasts across the columns of an (m, 5) array.
Key points to remember
- A length-1 (singleton) axis adds no data but changes shape and ndim — which controls broadcasting.
- np.newaxis is just None; use it inside brackets: v[:, np.newaxis] makes a column, v[np.newaxis, :] a row.
- np.expand_dims(a, axis) inserts a size-1 axis at a position (int or tuple).
- np.squeeze(a) removes all size-1 axes; squeeze(axis=k) removes only axis k and errors if its size isn't 1.
- To broadcast one value per row against a (n, m) array, turn the (n,) array into (n, 1).
- newaxis, expand_dims, squeeze and reshape(-1, 1) all return views — no data is copied.
Try it yourself
Row and column versions of a 1D array
Per-player form adjustment
Squeezing size-1 axes
Practice: bonus runs per match
Revision Flashcards
Tap a card to flip it and see the answer.
