What is Broadcasting?
Why NumPy lets arrays of different shapes work together, what 'virtual stretching' means, and the simplest case: scalar broadcasting.
Watch the lesson
1 video on CodeWithMunnaX
Element-wise operations combine two arrays position by position, so normally both arrays need the same shape. But real analysis is full of "big array + small array" problems:
- Add a fixed bonus of 10 runs to every score in a
(5, 14)squad table (5 players × 14 matches). - Multiply every match column by that match's pitch difficulty (14 values, one per match).
- Scale every player's row by his own form rating (5 values, one per player).
None of these pairs have the same shape. Writing loops would work, but it would be slow and ugly. Broadcasting is NumPy's answer: it makes the smaller array behave as if it had been repeated to the bigger shape — without actually repeating it in memory.
Definition
Broadcasting is NumPy's set of rules for performing element-wise operations on arrays of different shapes by virtually stretching the smaller array (along dimensions of size 1, or missing dimensions) to match the larger one, without copying data.
- Element-wise operations —
+,-,*,/,**,%, comparisons like>and==, and ufuncs likenp.maximum. Broadcasting applies to all of them. - Different shapes — the shapes don't have to be equal; they only have to be compatible (the exact rules are in the next topic).
- Virtually stretching — NumPy pretends the small array is repeated. Internally it just re-reads the same values again and again.
- Without copying data — no big temporary array is built for the small operand, so broadcasting is both fast and memory efficient.
Syntax
There is no special function — broadcasting happens automatically whenever you use an element-wise operator on two arrays (or an array and a scalar):
python
result = big_array + small_array # any element-wise operator
result = big_array * 10 # scalar is the simplest "small array"
# helpers for checking / visualising broadcasting
np.broadcast_shapes(shape1, shape2) # result shape, or ValueError
np.broadcast_to(array, shape) # read-only stretched VIEW of array| Function / parameter | Meaning | Default |
|---|---|---|
np.broadcast_shapes(*shapes) | returns the shape the operation would produce; raises ValueError if incompatible | — |
np.broadcast_to(array, shape) | returns a read-only view of array stretched to shape (no copy) | — |
array | the small array to stretch | required |
shape | the target shape (tuple) | required |
Example — scalar broadcasting (Rule 1)
The simplest broadcast: an array with a scalar (a single number, shape ()).
python
import numpy as np
runs = np.array([50, 80, 30, 100])
print(runs + 10)
print(runs * 2)
print(runs > 40)text
[ 60 90 40 110]
[100 160 60 200]
[ True True False True]How it works: the scalar 10 has shape () — zero dimensions. NumPy treats it as compatible with any shape and pairs it with every element. The same works for 2D arrays:
python
scores = np.array([[72, 45, 88],
[56, 0, 74]])
print(scores + 10)text
[[82 55 98]
[66 10 84]]All 6 positions got +10. You have been using broadcasting since your very first array + 10 — now it has a name.
Example — seeing the "virtual stretch"
np.broadcast_to shows you exactly what NumPy pretends the small array looks like:
python
stretched = np.broadcast_to(np.array([1.1, 0.9, 1.0]), (5, 3))
print(stretched)
print(stretched.strides)text
[[1.1 0.9 1. ]
[1.1 0.9 1. ]
[1.1 0.9 1. ]
[1.1 0.9 1. ]
[1.1 0.9 1. ]]
(0, 8)How it works: the strides (0, 8) tell how many bytes NumPy jumps to move one step along each axis. Moving to the next column jumps 8 bytes (one float64). Moving to the next row jumps 0 bytes — it re-reads the very same 3 numbers. That is the "virtual stretch": 5 rows on screen, but only 3 values in memory. The view is read-only; writing to it raises ValueError: assignment destination is read-only.
Why broadcasting matters
| Without broadcasting | With broadcasting |
|---|---|
| Write a loop over rows / columns | One expression: scores * multiplier |
Or build a full-size copy with np.tile first | No copy — the small array is reused |
| Slow (Python loop) and memory hungry | Fast (C loop) and memory efficient |
| Easy to get index bugs | Shape rules are checked for you |
Python list vs NumPy
python
[50, 80, 30] + [10] # list: [50, 80, 30, 10] (joins lists!)
np.array([50, 80, 30]) + 10 # NumPy: [60 90 40] (adds to each element)With lists, + means concatenate and * 2 means repeat the list. With NumPy arrays, the same operators are math, broadcast element by element.
Common mistakes
- ❌ Thinking broadcasting creates a full copy of the small array. ✅ It is virtual — no extra memory for the stretched operand (only the result array is new).
- ❌ Expecting
[1, 2, 3] + 10to work on a plain Python list. ✅ Convert tonp.arrayfirst — lists don't broadcast. - ❌ Writing into
np.broadcast_to(...)output. ✅ It is a read-only view; use.copy()if you really need a writable full-size array.
Interview tip
Q: Does broadcasting copy the smaller array to match the bigger one? A: No. NumPy uses a stride of 0 along the stretched dimensions, so the same values are re-read for every row/column. Only the output array is allocated. That is why broadcasting is memory efficient and fast.
Key points to remember
- Broadcasting lets element-wise operations work on arrays of different (but compatible) shapes.
- The smaller array is stretched virtually — NumPy re-reads the same values (stride 0) instead of copying them.
- Scalar broadcasting (array + 10) is the simplest case: a scalar has shape () and fits any shape.
- Broadcasting works for every element-wise operator: + - * / ** %, comparisons and ufuncs like np.maximum.
- np.broadcast_shapes() tells you the result shape; np.broadcast_to() shows the stretched view.
- Python lists do not broadcast — [1, 2] + [10] concatenates; convert to np.array first.
Try it yourself
Scalar broadcasting on 1D and 2D arrays
See the virtual stretch with np.broadcast_to
Practice: bonus runs for the whole squad
Revision Flashcards
Tap a card to flip it and see the answer.
