Free preview

117 lessons

Essential Python for Data Science and ML

Free preview

Essential Python for Data Science and ML · 117 lessons

Learn Python properly - from basics to ML-ready

No surprise gaps

Actually remember it

Skip what you know

One subscription. All learning paths included.

Our content is best on a larger screen

Broadcasting NumPy arrays

What is broadcasting?

Explanation

Broadcasting is the mechanism that lets NumPy perform element-wise operations on arrays of different shapes without copying data.

NumPy automatically "stretches" smaller arrays so they behave as if they had the same shape as the larger one.

Scalar broadcasting is the simplest case:

import numpy as np

arr = np.array([1, 2, 3, 4])
print(arr + 10)  # [11 12 13 14]

Here, NumPy conceptually treated 10 as if it had been repeated four times - but behind the scenes, it never created [10, 10, 10, 10].

Array broadcasting works the same way. NumPy virtually lines up the shapes and stretches the smaller array as needed:

mat = np.array([[1, 2, 3],
                [4, 5, 6]])

vec = np.array([10, 20, 30])
print(mat + vec)
[[11 22 33]
 [14 25 36]]

The 1D array vec was stretched across each row of mat - no copying of data, no for loops.

Key idea

Key Point

Broadcasting is NumPy's way of making different-shaped arrays compatible so that operations work element-wise without explicit repetition.

Rules of broadcasting

Explanation

To understand what broadcasting can and cannot do, we need the rules.

NumPy compares array shapes dimension by dimension, starting from the last axis (rightmost).

If the shapes have different lengths, we conceptually pad the shorter shape with 1s on the left so the dimensions line up for comparison. (NumPy does not actually reshape the array - this is just how we reason about alignment.)

Key rules

Key Point

Two dimensions are compatible if:

  1. They are equal, or
  2. One of them is 1.

If all dimensions are compatible, broadcasting succeeds. Otherwise, it raises an error.

The resulting shape is the maximum size along each axis.


Tip

If this still feels abstract, that is fine - the key aim here is to get comfortable with the rules for checking compatibility. The next section grounds these rules in practical examples.

Example

Let us now check broadcasting compatibility for a few array shape combinations.

Example 1: Array A has shape (5, 3) and array B has shape (3,)

A and B can be broadcast. Operations like A + B and A * B behave as if B were repeated across each of A's rows.

Step by step

Details


Example 2: Array C has shape (4, 1, 6) and array D has shape (3, 6)

C and D can be broadcast. This effectively repeats D across the first axis of C.

Step by step

Details


Example 3: Array E has shape (7, 5) and array F has shape (7, 4)

E and F cannot be broadcast. Any operation like E + F will raise a ValueError.

Step by step

Details

Practice questions

4 questions

Suppose array X has shape (2, 1, 8) and array Y has shape (5, 8).

According to NumPy's broadcasting rules, can X and Y be broadcast together, and if so, what would be the resulting shape?

Select the correct answer:

+ 3 more questions

Applying broadcasting in practice

Example

Let us now look at broadcasting in action with concrete arrays.

Example 1: 1D added to 2D

a = np.ones((2, 3))
b = np.array([10, 20, 30])

print(a + b)
[[11. 21. 31.]
 [11. 21. 31.]]

Here b has shape (3,), which is padded to (1, 3) and stretched across both rows of a. The result has shape (2, 3).


Example 2: Outer sum (column + row)

c = np.array([[1],
              [2],
              [3]])   # shape (3, 1)

d = np.array([10, 20, 30])   # shape (3,)

print(c + d)
[[11 21 31]
 [12 22 32]
 [13 23 33]]

d of shape (3,) is padded to (1, 3). Comparing (3, 1) and (1, 3) - both axes have one side equal to 1, so broadcasting produces an outer sum with shape (3, 3).


Example 3: Incompatible case

e = np.ones((3, 2))   # shape (3, 2)
f = np.ones((4,))     # shape (4,)

print(e + f)   # ValueError!

Why does this fail?

  • We pad (4,) to (1, 4).
  • Shape comparison: (3, 2) vs (1, 4).
  • The last axis gives (2 vs 4) - incompatible, because neither equals the other and neither is 1.

Broadcasting fails and NumPy raises an error.

Practice questions

3 questions

A colour image can be represented as a 3D array of shape (height, width, channels).

  • The first two dimensions give the pixel grid (rows and columns).
  • The third dimension has 3 entries per pixel: the RGB intensities (red, green, blue), stored as integers between 0 and 255.

In the Python Editor, a colour image is loaded into a NumPy array called flower.

Run the starter code to visualise it.


Task

  1. Use broadcasting to scale the intensity of each RGB channel by applying the array intensity_factors to the flower array. Assign the result to a variable called transformed_flower.
  2. The pixel values must be integers. Convert transformed_flower to an integer array using the .astype("int") method on the array, and reassign the result back to transformed_flower.
    • Note: .astype() returns a new array, it does not convert in place.

Once you have completed these steps, run the code. It will output a token used to verify your implementation.

What is the token?

Tip

If you want to visualise the transformed flower run:

plt.imshow(transformed_flower)
plt.show()

Select the correct answer:

+ 2 more questions

Using keepdims when broadcasting

Explanation

When we reduce an array with operations like sum, mean, or max, NumPy removes the reduced axis. This often produces a result with fewer dimensions - for example, reducing a 2D array along rows gives a 1D vector.

In many workflows we need the reduced result to retain its shape context so it can broadcast back against the original array. This is where keepdims=True comes in - it tells NumPy to keep the reduced axes as size-1 dimensions, so the result stays broadcast-compatible.

Key idea

Key Point

keepdims=True preserves each reduced axis as a length-1 dimension, making the result broadcast-ready.

Example

Suppose we have a 2D array and want to divide each entry in a row by the sum of that row - a common step in data normalisation.

import numpy as np

array = np.array([[2, 2, 4],
                  [1, 5, 4]])

# Reduce along rows (axis=1), but keep dimensions
row_sums = np.sum(array, axis=1, keepdims=True)
print(row_sums.shape)  # (2, 1)
print(row_sums)
# [[ 8]
#  [10]]

# Broadcasts cleanly across rows
print(array / row_sums)
# [[0.25 0.25 0.5 ]
#  [0.1  0.5  0.4 ]]

Here, row_sums has shape (2, 1). This pairs perfectly with the (2, 3) shape of array, so broadcasting divides each row by its own sum without any reshaping.

What if we did not use keepdims?

Details


Row vs. column aggregations for 2D arrays

For a 2D array with shape (n, m), row sums without keepdims produce shape (n,), which pads to (1, n) and fails to align with (n, m). With keepdims=True we get (n, 1), which broadcasts cleanly.

Column sums do not have this problem - the result has shape (m,), which pads to (1, m) and is directly compatible with (n, m).

Practice questions

3 questions

Suppose an array arr has shape (6, 4, 5). What are the shapes of the following operations?

  1. arr.sum(axis=0) → ?
  2. arr.sum(axis=2, keepdims=True) → ?
  3. arr.sum(axis=(0, 1), keepdims=True) → ?

Choose the correct options below.

Select the correct answer:

+ 2 more questions