NumPy Broadcasting Explained
- Python
- Numpy
- Vectorization

Photo by Gabriel Avalos on Unsplash
NumPy broadcasting is a feature that allows you to perform operations between arrays of different shapes. NumPy automatically “stretches” or “repeats” the smaller array to match with the larger array so the operation can work.
Broadcasting rules
NumPy checks compatibility by comparing dimensions from right to left (trailing dimensions first). Two dimensions are compatible if they satisfy one of these conditions:
- They are equal (same size)
- One of them is 1 (can be stretched)
- One dimension is missing (treated as size 1)
Step-by-step compatibility check
Let’s see how NumPy evaluates compatibility:
import numpy as np # Example: (3, 4) and (4,) a = np.random.rand(3, 4) # shape (3, 4) b = np.random.rand(4) # shape (4,) # NumPy compares from right to left: # a: (3, 4) # b: (4) ← missing dimension treated as 1, so effectively (1, 4) # # Comparison: # Position 1 (rightmost): 4 == 4 ✓ Compatible # Position 0: 3 vs 1 (missing) ✓ Compatible (one is 1) # Result: Compatible → broadcasts to (3, 4)
Compatible dimension examples
Equal dimensions:
a = np.array([[1, 2, 3], [4, 5, 6]]) # shape (2, 3) b = np.array([[7, 8, 9], [10, 11, 12]]) # shape (2, 3) # Both dimensions equal: 2==2 and 3==3 ✓ result = a + b
One dimension is 1:
# Case 1: Column vector a = np.array([[1, 2, 3], [4, 5, 6]]) # shape (2, 3) b = np.array([[10], [20]]) # shape (2, 1) # Comparison: 2==2 ✓, 3 vs 1 ✓ (one is 1) result = a + b # Case 2: Row vector a = np.array([[1, 2, 3], [4, 5, 6]]) # shape (2, 3) b = np.array([[10, 20, 30]]) # shape (1, 3) # Comparison: 2 vs 1 ✓ (one is 1), 3==3 ✓ result = a + b
Missing dimensions (treated as 1):
a = np.array([[1, 2, 3], [4, 5, 6]]) # shape (2, 3) b = np.array([10, 20, 30]) # shape (3,) → treated as (1, 3) # Comparison: 2 vs 1 (missing) ✓, 3==3 ✓ result = a + b
Incompatible dimension examples
Neither equal nor 1:
a = np.array([[1, 2, 3, 4]]) # shape (1, 4) b = np.array([[1, 2], [3, 4], [5, 6]]) # shape (3, 2) # Comparison from right to left: # Position 1: 4 vs 2 ✗ (neither equal nor 1) # This fails before checking position 0
Multiple incompatible dimensions:
a = np.random.rand(2, 3, 4) # shape (2, 3, 4) b = np.random.rand(5, 6) # shape (5, 6) → treated as (1, 5, 6) # Comparison: # Position 2: 4 vs 6 ✗ (neither equal nor 1) # Position 1: 3 vs 5 ✗ (neither equal nor 1) # Position 0: 2 vs 1 ✓ (one is 1, but doesn't matter since others fail)
Visual memory aid
Think of it like aligning arrays from the right:
Compatible: a: (8, 1, 6, 1) b: (7, 1, 5) ----------- (8, 7, 6, 5) ← Result shape Incompatible: a: (8, 4, 6, 2) b: (7, 3, 5) ↑ ↑ ↑ ✗ ✗ ✗ ← 4≠7 and 6≠3 and 2≠5, and neither is 1
Broadcasting operation examples
Addition of an array with a scalar:
import numpy as np # Scalar with array a = np.array([1, 2, 3, 4]) b = 10 result = a + b # [11, 12, 13, 14]
Addition of two arrays with different shapes:
# (4,) + (1,) → both become (4,) a = np.array([1, 2, 3, 4]) # shape (4,) b = np.array([10]) # shape (1,) result = a + b # [11, 12, 13, 14] # (3, 4) + (4,) → (4,) becomes (1, 4), then broadcasts to (3, 4) a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) # shape (3, 4) b = np.array([1, 0, 1, 0]) # shape (4,) result = a + b
Addition of two 2D arrays with different shapes:
# (3, 1) + (1, 4) → both broadcast to (3, 4) a = np.array([[1], [2], [3]]) # shape (3, 1) b = np.array([[10, 20, 30, 40]]) # shape (1, 4) result = a + b # shape (3, 4)
Row and column operations:
# Adding a row vector to each row of a matrix matrix = np.random.rand(3, 4) row_vector = np.array([1, 2, 3, 4]) result = matrix + row_vector # Adding a column vector to each column of a matrix col_vector = np.array([[1], [2], [3]]) result = matrix + col_vector
Normalizing data:
# Subtract mean from each column data = np.random.rand(100, 5) mean = data.mean(axis=0) # shape (5,) normalized = data - mean # Broadcasting happens here
When broadcasting fails
Broadcasting fails when dimensions are incompatible:
a = np.array([[1, 2, 3]]) # shape (1, 3) b = np.array([[1], [2]]) # shape (2, 1) # This works: (1, 3) + (2, 1) → (2, 3) a = np.array([[1, 2, 3, 4]]) # shape (1, 4) b = np.array([[1], [2], [3]]) # shape (3, 1) # This works: (1, 4) + (3, 1) → (3, 4) a = np.array([1, 2, 3]) # shape (3,) b = np.array([[1, 2], [3, 4]]) # shape (2, 2) # This fails: (3,) and (2, 2) are incompatible
Practical tips
- Always align from the right: Start checking from the last dimension.
- Remember the “1 rule”: Any dimension of size 1 can be broadcast to any size.
- Missing dimensions are treated as having size 1.
- Use reshape strategically: Add dimensions of size 1 using
reshape()
ornp.newaxis
to make arrays compatible.