NumPy Cheatsheet
Broadcasting
Use this NumPy reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
The Broadcasting Rule
NumPy can operate on arrays of different shapes without copying data. Before an operation, shapes are compared right-to-left. Two dimensions are compatible when they are equal or one of them is 1. A size-1 axis is stretched to match the other.
Shape A: (3, 4) Shape B: (4,) → treated as (1, 4) Result: (3, 4) Shape A: (3, 1, 4) Shape B: (5, 1) → treated as (1, 5, 1) Result: (3, 5, 4)
import numpy as np a = np.ones((3, 4)) b = np.array([1, 2, 3, 4]) # shape (4,) → broadcasts to (3, 4) a + b # each row of a gets b added col = np.array([[10], [20], [30]]) # shape (3, 1) row = np.array([1, 2, 3, 4]) # shape (4,) → (1, 4) col + row # (3, 4) outer sum
If shapes are incompatible, NumPy raises
ValueError: operands could not be broadcast together.
Shape Compatibility Quick Reference
| A shape | B shape | Result shape | Compatible? |
|---|---|---|---|
(5,) | (5,) | (5,) | yes |
(3, 4) | (4,) | (3, 4) | yes |
(3, 4) | (3,) | error | no (3 ≠ 4) |
(3, 4) | (1, 4) | (3, 4) | yes |
(3, 4) | (3, 1) | (3, 4) | yes |
(3, 1) | (1, 4) | (3, 4) | yes |
(2, 3, 4) | (3, 4) | (2, 3, 4) | yes |
(2, 3, 4) | (3, 1) | (2, 3, 4) | yes |
(2, 3, 4) | (2, 1, 1) | (2, 3, 4) | yes |
Common Patterns
Scalar operations
a = np.array([1, 2, 3]) a * 2 # [2, 4, 6] — scalar is shape () → broadcasts to (3,) a + 10 # [11, 12, 13]
Row vector added to each row
matrix = np.ones((4, 3)) bias = np.array([1, 2, 3]) # shape (3,) → same as (1, 3) matrix + bias # (4, 3) bias added to every row
Column vector added to each column
scale = np.array([1, 2, 3, 4])[:, np.newaxis] # shape (4, 1) matrix + scale # (4, 3) scale added to every col
Outer product
a = np.array([1, 2, 3]) b = np.array([10, 20]) a[:, np.newaxis] * b[np.newaxis, :] # (3, 2) outer product np.outer(a, b) # equivalent built-in
Outer sum / pairwise distance
x = np.array([1.0, 2.0, 3.0]) y = np.array([4.0, 5.0]) diff = x[:, np.newaxis] - y[np.newaxis, :] # (3, 2) pairwise differences # Euclidean pairwise distance (2-D points) pts = np.random.rand(5, 2) d = np.sqrt(((pts[:, np.newaxis] - pts[np.newaxis, :]) ** 2).sum(axis=-1)) # d.shape == (5, 5)
Normalizing (z-score per feature)
X = np.random.rand(100, 10) # 100 samples, 10 features mu = X.mean(axis=0) # (10,) sigma = X.std(axis=0) # (10,) X_norm = (X - mu) / sigma # (100, 10) — mu/sigma broadcast over rows
Batch operations
# Add a per-sample bias to a batch of matrices batch = np.ones((32, 8, 8)) # 32 matrices of shape 8×8 bias = np.ones((32, 1, 1)) # one scalar per sample batch + bias # (32, 8, 8)
Making Arrays Broadcast-Compatible
a = np.array([1, 2, 3]) # (3,) # Insert axes to control which dimensions align a[np.newaxis, :] # (1, 3) — prepend axis a[:, np.newaxis] # (3, 1) — append axis a[None, :] # same as [np.newaxis, :] np.expand_dims(a, axis=0) # (1, 3) np.expand_dims(a, axis=-1) # (3, 1) # np.broadcast_to — read-only broadcast view np.broadcast_to(a, (4, 3)) # (4, 3) view, no copy # np.broadcast_shapes (NumPy ≥ 1.20) — compute result shape without arrays np.broadcast_shapes((3, 1), (1, 4), (3, 4)) # (3, 4)
np.broadcast and np.broadcast_arrays
# Iterate over broadcasted elements b = np.broadcast(np.array([1, 2, 3]), np.array([[10], [20]])) b.shape # (2, 3) # Return views that all share the same broadcasted shape x, y = np.broadcast_arrays(np.array([1, 2, 3]), np.array([[10], [20]])) x.shape # (2, 3) y.shape # (2, 3)
Gotchas
# (3,) and (3,) → no problem # (3, 1) and (3,) → (3, 3) ← easy mistake: shapes align from the right # Fix: be explicit a = np.ones((3, 1)) b = np.ones((3,)) (a + b).shape # (3, 3) — b treated as (1, 3), NOT (3, 1) # Comparing two column vectors c = np.array([[1], [2], [3]]) # (3, 1) d = np.array([[1], [2], [3]]) # (3, 1) (c == d).shape # (3, 3) — broadcasts! use np.array_equal(c, d) for equality
Rule of thumb: when in doubt, add explicit
np.newaxisaxes rather than relying on implicit alignment. It makes intent clear and avoids silent shape bugs.