Boolean Masks
Combine Two Masks
Build a combined boolean mask from two conditions (lower and upper bound) and
use it to filter a list. The first loop produces the mask with and; the
second loop applies it with if m. The trace shows combined growing one
boolean at a time, then filtered collecting only the values that pass both
conditions.
By hand
First loop: for each v append v > lo and v < hi to combined — Python's
and short-circuits if the first condition is False. Second loop: zip the
values and mask together; append the value only when m is True.
naive.py
Replay: real traced execution (multi-file project)
values = [1, 5, 3, 8, 4, 9]
lo = 2
hi = 6
combined = []
for v in values:
combined.append(v > lo and v < hi)
filtered = []
for v, m in zip(values, combined):
if m:
filtered.append(v)
print('RESULT:', (combined, filtered))
values ← [1, 5, 3, 8, 4, 9]
1values = [1, 5, 3, 8, 4, 9]2lo = 2values this step[1, 5, 3, 8, 4, 9]valueslo ← 2
1values = [1, 5, 3, 8, 4, 9]2lo = 23hi = 6values this step2lohi ← 6
2lo = 23hi = 64combined = []values this step6hicombined ← []
3hi = 64combined = []5for v in values:values this step[]combinedv ← 1, combined ← [False]
pass 1 of 64combined = []5for v in values:6 combined.append(v > lo and v < hi)7filtered = []values this step1v[] → [False]combinedAll 6 passes — pass 1 is the card above pass vcombined1 1 [] → [False] 2 1 → 5 [False] → [False, True] 3 5 → 3 [False, True] → [False, True, True] 4 3 → 8 [False, True, True] → [False, True, True, False] 5 8 → 4 [False, True, True, False] → [False, True, True, False, True] 6 4 → 9 [False, True, True, False, True] → [False, True, True, False, True, False] for v in values:
4combined = []5for v in values:6 combined.append(v > lo and v < hi)filtered ← []
6 combined.append(v > lo and v < hi)7filtered = []8for v, m in zip(values, combined):values this step[]filteredv ← 1, m ← False
pass 1 of 27filtered = []8for v, m in zip(values, combined):9 if m:10 filtered.append(v)values this step9 → 1vFalsemm ← True, v ← 5
pass 2 of 27filtered = []8for v, m in zip(values, combined):9 if m:10 filtered.append(v)values this stepFalse → Truem1 → 5vfiltered ← [5], v ← 3
pass 1 of 27filtered = []8for v, m in zip(values, combined):9 if m:10 filtered.append(v)11print('RESULT:', (combined, filtered))values this step[] → [5]filtered5 → 3vfiltered ← [5, 3], m ← False, v ← 8
pass 2 of 27filtered = []8for v, m in zip(values, combined):9 if m:10 filtered.append(v)11print('RESULT:', (combined, filtered))values this step[5] → [5, 3]filteredTrue → Falsem3 → 8vm ← True, v ← 4
7filtered = []8for v, m in zip(values, combined):9 if m:values this stepFalse → Truem8 → 4vif m:
8for v, m in zip(values, combined):9 if m:10 filtered.append(v)filtered ← [5, 3, 4]
9 if m:10 filtered.append(v)11print('RESULT:', (combined, filtered))values this step[5, 3] → [5, 3, 4]filteredm ← False, v ← 9
7filtered = []8for v, m in zip(values, combined):9 if m:values this stepTrue → Falsem4 → 9vif m:
8for v, m in zip(values, combined):9 if m:10 filtered.append(v)for v, m in zip(values, combined):
7filtered = []8for v, m in zip(values, combined):9 if m:stdout ← RESULT: ([False, True, True, False, True, False], [5, 3, 4])
10 filtered.append(v)11print('RESULT:', (combined, filtered))values this stepRESULT: ([False, True, True, False, True, False], [5, 3, 4])stdout
With NumPy
(a > lo) & (a < hi) applies both comparisons elementwise and combines them
with bitwise AND. The result is a boolean array, passed directly to a[mask]
for boolean indexing.
library.py
import numpy as np
values = [1, 5, 3, 8, 4, 9]
lo, hi = 2, 6
a = np.array(values)
mask = (a > lo) & (a < hi)
filtered = a[mask]
print('mask: shape:', mask.shape, 'dtype:', mask.dtype, 'values:', mask.tolist())
print('filtered: shape:', filtered.shape, 'dtype:', filtered.dtype, 'values:', filtered.tolist())
print('RESULT:', (mask.tolist(), filtered.tolist()))
mask: shape: (6,) dtype: bool values: [False, True, True, False, True, False]
filtered: shape: (3,) dtype: int64 values: [5, 3, 4]
RESULT: ([False, True, True, False, True, False], [5, 3, 4])
Implementation notes
- Use
&(bitwise AND) and|(bitwise OR) on NumPy boolean arrays, not Python'sand/or. Python'sand/oroperate on the truthiness of the whole array (raising an error for ambiguous multi-element arrays). - Parentheses are required:
(a > lo) & (a < hi). Without them,&binds tighter than>and<, soa > lo & a < hiis parsed asa > (lo & a) < hi— a comparison chain, not two masked conditions. - The combined mask
(a > lo) & (a < hi)is equivalent toa[a > k]fromfilter-with-maskgeneralized to a range. - Shape, dtype, and values are shown explicitly here because
ndarray.__repr__output varies with NumPy version and print options.