Boolean masks

NumPy · Essentials · 10 min

Filtering with masks replaces if-statements inside loops and is the basis of pandas filtering.

A comparison like a > 0 gives a boolean array of the same shape. Use it as an index and you get only the elements where the mask is True: a[a > 0]. This is boolean indexing (or masking).

The result is always a 1-D copy of the selected elements, in row-major order, even when a is 2-D. Since it's a copy, modifying the result does not touch a. But assigning through the mask does: a[a < 0] = 0 writes zeros into a in place. That pattern, clipping or replacing values by condition, is extremely common.

Build compound conditions with & (and), | (or), ~ (not), and wrap each comparison in parentheses because these operators bind tighter than < or ==.

Because True counts as 1, masks double as counters:

  • (a > 0).sum() counts matches; (a > 0).mean() gives the fraction
  • mask.any() and mask.all() answer "is there any / are all"
  • np.nonzero(mask) (or np.flatnonzero) returns the positions

For "choose between two values depending on a condition", use np.where(cond, x, y), which returns a new array with x where the condition holds and y elsewhere. With a single argument, np.where(cond) returns indices, like np.nonzero.

A mask can also select along one axis: m[m[:, 0] > 5] keeps the rows whose first column exceeds 5, and m[:, mask] keeps columns. The mask length must match that axis.

Example

import numpy as np

t = np.array([12.5, -3.0, 7.1, -0.4, 22.0])
print(t[t > 0])                 # [12.5  7.1 22. ]
print((t < 0).sum())            # 2
print((t > 10).mean())          # 0.4

t[t < 0] = 0                    # assign through the mask
print(t)                        # [12.5  0.   7.1  0.  22. ]

x = np.arange(10)
print(x[(x % 2 == 0) & ~(x > 6)])      # [0 2 4 6]
print(np.where(x < 5, "low", "high"))  # ['low' ... 'high']

rows = np.array([[1, 9], [6, 2], [8, 3]])
print(rows[rows[:, 0] > 5])     # [[6 2] [8 3]]  rows whose col 0 > 5
print(np.nonzero(rows[:, 0] > 5)[0])   # [1 2]

Remember

  • a[mask] returns a 1-D copy of the selected elements
  • a[mask] = value modifies the original in place
  • Combine conditions with & | ~ and parentheses
  • np.where(cond, x, y) picks element-wise between two options

Watch out for

  • Expecting a[mask] to keep the 2-D shape: it always flattens the selection.

Check yourself

  1. `b = a[a > 5]; b[:] = 0`. What happens to `a`?
  2. What does `np.where(np.array([3, -1, 2]) > 0, 1, -1)` return?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: numpy.org/doc/stable/user/basics.indexing.html#boolean-array-indexing