Boolean Masks
Overview
A comparison over an array gives an array of answers, and that array selects.
What You Will Learn in This Lesson
By the end of this lesson, you will be able to:
- Read a comparison on an array as producing one answer per element.
- Use a mask inside square brackets to select the elements you want.
- Combine conditions with
&and|, and say whyandfails. - Count matches without selecting them.
Why This Matters
This is filtering, and it is the same idea you will use for the rest of the course — a mask over a numpy array in this unit, a mask over a whole DataFrame in unit 6. It also contains the single most common numpy error message people meet, and knowing why it happens turns it from a mystery into a typo.
Step 1: A comparison produces an array
scores > 85 is not one True or False. It is one per element — which is what makes the next section work.
One answer per element
The comparison is elementwise, like every other numpy operation. What comes back is an array of booleans the same shape as the one you started with.
import numpy as np
scores = np.array([92, 88, 79])
print(scores > 85)
print((scores > 85).dtype)
Counting without selecting
Because True counts as 1, summing a mask counts the matches. .any() and .all() answer the two other questions you usually have.
import numpy as np
scores = np.array([92, 88, 79])
mask = scores > 85
print(mask.sum()) # how many
print(mask.any()) # is there at least one
print(mask.all()) # are they all
Mini Practice #1: Count before you select
Try It YourselfPrint how many scores are below 80, whether any are, and whether all are — without selecting a single element.
Step 2: Index with the mask to select
Putting the mask inside the brackets keeps only the elements it marked. This is filtering, and it is the same shape of operation you will use on tables later.
Selecting
The result is a new array holding just the elements where the mask was True. Its length is the number of matches, not the length of the original.
import numpy as np
scores = np.array([92, 88, 79])
print(scores[scores > 85])
print(len(scores[scores > 85]))
Two conditions, and the error everybody hits
Use & for and, | for or, and bracket each condition. Python's own and needs to reduce each side to a single True or False, and an array refuses — which is exactly what the error says.
import numpy as np
scores = np.array([92, 88, 79, 60])
print(scores[(scores > 70) & (scores < 90)])
print(scores[(scores < 70) | (scores > 90)])
Leaving the brackets off gives a confusing error, because & binds tighter than >. scores > 70 & scores < 90 is parsed as scores > (70 & scores) < 90.
What "and" actually says
Run this deliberately once. The message — the truth value of an array with more than one element is ambiguous — is numpy telling you it cannot answer a yes/no question about a whole array, and pointing you at &.
import numpy as np
scores = np.array([92, 88])
try:
scores[(scores > 70) and (scores < 90)]
except ValueError as exc:
print("ValueError:", exc)
Mini Practice #2: Two conditions
Try It YourselfSelect the scores that are at least 70 and at most 90. Then try it once with and instead of & and read the error.
Step 3: Assigning through a mask
A mask works on the left of an assignment too, which is how you fix a set of values in one line.
Capping outliers
Everything the mask marks gets the new value. The rest is untouched.
import numpy as np
scores = np.array([92, 105, 79])
scores[scores > 100] = 100
print(scores)
Remember the previous lesson: this edits in place. Take a .copy() first if the caller still needs the original.
Step 4: When to Use It
Selecting values by a condition
A comparison gives a mask, and indexing with it keeps the matching values — no loop.
passed = scores[scores >= 60]
Counting and replacing
(mask).sum() counts matches; np.where builds a new array from a condition.
When Not to Use It
Do not combine masks with and, or or not — they raise on arrays. Use &, | and ~, with each comparison in parentheses.
End-of-Lesson Exercises
Exercise 1: The scores that passed
Write passing(scores, mark) that returns a list of the scores that are at least mark, using a mask rather than a loop.
scores >= mark gives you an array of True and False. Put that inside the square brackets.
Exercise 2: How many in the band
Write in_band(scores, low, high) that returns how many scores are between low and high inclusive. Count the mask; do not select the elements and measure the result.
Build one mask with (scores >= low) & (scores <= high), then .sum() it. Wrap the result in int() so it comes back as a plain number.