Conditions: where, select, clip
Reviewed & published by Brayan K
Conditional functions in NumPy let you choose, locate, and bound array values element by element without writing a single loop — the workhorses are np.where, np.select, and np.clip.
Part of the free NumPy course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
In this lesson you'll use np.where for if/else choices and for finding indices, np.select for many conditions, and np.clip to cap values into a range.
What You'll Learn in This Lesson
- Choose element-wise with np.where(cond, x, y)
- Find indices with np.where(cond)
- Handle many cases with np.select
- Bound values with np.clip
- Combine conditions with boolean masks
- Replace if/else loops with vectorized logic
1 Choosing Values: np.where(condition, x, y)
np.where(condition, x, y) walks through a boolean condition element by element. Where the condition is True it takes the value from x; where it is False it takes the value from y. It's the vectorized version of an if/else.
import numpy as np
scores = np.array([45, 80, 30, 95, 60])
# Pass if >= 50, else Fail
result = np.where(scores >= 50, "Pass", "Fail")
print(result) # ['Fail' 'Pass' 'Fail' 'Pass' 'Pass']
# x and y can be arrays too: double the small numbers, keep the big ones
nums = np.array([1, 8, 3, 10])
print(np.where(nums < 5, nums * 2, nums)) # [ 2 8 6 10]
# ✅ Expected output:
# ['Fail' 'Pass' 'Fail' 'Pass' 'Pass']
# [ 2 8 6 10]Expected output: ['Fail' 'Pass' 'Fail' 'Pass' 'Pass'] and [2 8 6 10] (only values under 5 were doubled).
2 Finding Indices: np.where(condition)
Call np.where with only the condition (no x or y) and it returns the indices where the condition is True, as a tuple of arrays. For a 1D array, use np.where(cond)[0] to get the index array.
import numpy as np
arr = np.array([10, 25, 5, 40, 15])
# Which positions are greater than 12?
idx = np.where(arr > 12)
print(idx) # (array([1, 3, 4]),)
print(idx[0]) # [1 3 4]
# Use the indices to grab the matching values
print(arr[idx]) # [25 40 15]
# ✅ Expected output:
# (array([1, 3, 4]),)
# [1 3 4]
# [25 40 15]Expected output: (array([1, 3, 4]),), then [1 3 4], then the values [25 40 15].
3 Many Cases: np.select
np.where handles one if/else. For three or more cases use np.select: pass a list of conditions and a matching list of choices, plus an optional default. The first condition that is True wins for each element.
import numpy as np
scores = np.array([95, 82, 67, 50, 30])
conditions = [scores >= 90, scores >= 70, scores >= 60]
grades = ["A", "B", "C"]
result = np.select(conditions, grades, default="F")
print(result) # ['A' 'B' 'C' 'F' 'F']
# ✅ Expected output:
# ['A' 'B' 'C' 'F' 'F']Expected output: ['A' 'B' 'C' 'F' 'F'] — 50 and 30 fall through to the default "F".
4 Bounding Values (np.clip) & Boolean Masks
np.clip(arr, a_min, a_max) squeezes every value into a range: anything below a_min becomes a_min, anything above a_max becomes a_max. You can also combine conditions with & (and), | (or), and ~ (not) — wrap each comparison in parentheses — then feed the mask to np.where or use it to select elements directly.
import numpy as np
data = np.array([-5, 0, 50, 120, 80])
clipped = np.clip(data, 0, 100) # keep everything between 0 and 100
print(clipped) # [ 0 0 50 100 80]
# None gives a one-sided clip (floor at 0, no upper limit)
print(np.clip(data, 0, None)) # [ 0 0 50 120 80]
# ✅ Expected output:
# [ 0 0 50 100 80]
# [ 0 0 50 120 80]Expected output: [0 0 50 100 80], then the one-sided clip [0 0 50 120 80].
import numpy as np
temps = np.array([15, 22, 30, 8, 27])
# Comfortable if between 18 and 26 (inclusive)
mask = (temps >= 18) & (temps <= 26)
print(mask) # [False True False False False]
labels = np.where(mask, "ok", "no")
print(labels) # ['no' 'ok' 'no' 'no' 'no']
print(temps[mask]) # [22] -> direct masking keeps only matches
# ✅ Expected output:
# [False True False False False]
# ['no' 'ok' 'no' 'no' 'no']
# [22]Expected output: the mask [False True False False False], ['no' 'ok' 'no' 'no' 'no'], and temps[mask] equal to [22].
🎯 YOUR TURN: Fill in the Blanks
Replace each ___ so negatives become 0 and everything else is kept unchanged.
import numpy as np
data = np.array([-3, 5, -1, 8, 0])
# Where the value is negative, use 0, otherwise keep the value
fixed = np.___(data < 0, ___, data) # fill in the function and the replacement value
print(fixed) # Expected output: [0 5 0 8 0]Expected output: [0 5 0 8 0]. (Answers: where, 0.)
⚠️ Common Errors & Quick Reference
❌ ValueError: The truth value of an array ... is ambiguous
You used Python's and/or on arrays.
✅ Fix: use & and | with each comparison in parentheses, e.g. (a > 0) & (a < 10).
❌ ValueError: shape mismatch in np.select
Your conditions list and choices list have different lengths.
✅ Fix: give one choice per condition, and use default= for the fall-through case.
| Task | Code |
|---|---|
| Choose x or y | np.where(cond, x, y) |
| Find indices | np.where(cond)[0] |
| Many cases | np.select(conds, choices, default=...) |
| Bound values | np.clip(arr, lo, hi) |
| Combine masks | (a > 0) & (a < 10) |
🏆 Mini Challenge: Normalize Survey Ratings
Ratings should sit between 1 and 5, but a few are out of range. Clip them into bounds, then label each "low" (≤2), "mid" (3), or "high" (≥4).
import numpy as np
raw = np.array([0, 3, 6, 4, 1])
ratings = np.clip(raw, 1, 5) # clip into the valid 1..5 range
print(ratings) # [1 3 5 4 1]
conditions = [ratings <= 2, ratings == 3]
choices = ["low", "mid"]
labels = np.select(conditions, choices, default="high")
print(labels)
# ['low' 'mid' 'high' 'high' 'low']
# ✅ Expected output:
# [1 3 5 4 1]
# ['low' 'mid' 'high' 'high' 'low']Expected output: [1 3 5 4 1] then ['low' 'mid' 'high' 'high' 'low'].
❓ Frequently Asked Questions
Lesson complete — you can shape data with conditions!
You used np.where to choose values and find indices, np.select for multi-way logic, and np.clip to bound values — all combined with boolean masks.
Practice quiz
What does np.where(condition, x, y) do?
- Sorts the array
- Returns indices only
- Picks x where condition is True, else y
- Always returns x
Answer: Picks x where condition is True, else y. It chooses element-wise: x where True, y where False.
For nums = [1, 8, 3, 10], what is np.where(nums < 5, nums * 2, nums)?
- [2 16 6 20]
- [2 8 6 10]
- [1 8 3 10]
- [2 8 6 20]
Answer: [2 8 6 10]. Values under 5 (1 and 3) are doubled; 8 and 10 stay unchanged.
What does np.where(condition) with a single argument return?
- The indices where the condition is True
- A boolean mask
- The matching values
- A single True or False
Answer: The indices where the condition is True. With only the condition, np.where returns the indices that satisfy it.
For arr = [10, 25, 5, 40, 15], what is np.where(arr > 12)[0]?
- [25 40 15]
- [10 5]
- (array([1, 3, 4]),)
- [1 3 4]
Answer: [1 3 4]. [0] unwraps the tuple to the index array [1 3 4].
When should you use np.select instead of np.where?
- Only for 2D arrays
- When you have more than two cases
- Only for sorting
- When there is just one condition
Answer: When you have more than two cases. np.select handles many condition/choice pairs, like if/elif/else.
In np.select, what happens when no condition matches an element?
- It raises an error
- It uses the default value
- It uses the first choice
- It returns 0
Answer: It uses the default value. Unmatched elements fall through to the default argument.
What does np.clip([-5, 0, 50, 120, 80], 0, 100) return?
- [0 0 50 100 80]
- [-5 0 50 120 80]
- [0 0 50 120 80]
- [5 0 50 100 80]
Answer: [0 0 50 100 80]. Below 0 becomes 0 and above 100 becomes 100.
Which operators combine boolean conditions element-wise?
- and / or
- is / not
- + / -
- & / |
Answer: & / |. Use & for and, | for or, each comparison in parentheses.
For scores = [95, 82, 67, 50, 30] with grades A/B/C and default F, what does np.select give?
- ['A' 'A' 'A' 'A' 'A']
- ['F' 'F' 'F' 'F' 'F']
- ['A' 'B' 'C' 'F' 'F']
- ['A' 'B' 'C' 'D' 'F']
Answer: ['A' 'B' 'C' 'F' 'F']. 50 and 30 miss all conditions and fall to the default 'F'.
Why can't you use Python's 'and'/'or' on NumPy arrays?
- They are too slow
- They don't work element-wise and raise a ValueError
- They reverse the array
- They only work on 1D arrays
Answer: They don't work element-wise and raise a ValueError. and/or expect a single truth value and raise an ambiguity ValueError.
Continue this course
- Previous: Matrix Operations & Dot Products
- Next: Sorting, Searching & Counting — Order data and find values with sort, argsort, and searchsorted
- Quick reference: NumPy cheat sheet › Indexing & Slicing
Frequently asked questions
What does np.where do?
np.where(condition, x, y) goes element by element through a boolean condition and picks the value from x where the condition is True and from y where it is False. It is a fast, vectorized replacement for an if/else over an array.
How is np.where(condition) with one argument different?
When you call np.where with only the condition and no x or y, it returns the indices where the condition is True (as a tuple of index arrays). That makes it a quick way to locate matching elements.
When should I use np.select instead of np.where?
Use np.select when you have more than two cases. You pass a list of conditions and a matching list of choices, plus an optional default. It is like a vectorized if/elif/elif/else chain, whereas np.where only handles a single if/else.
What does np.clip do?
np.clip(arr, a_min, a_max) bounds every value into a range. Anything below a_min becomes a_min and anything above a_max becomes a_max; values already inside the range are unchanged. It is perfect for capping outliers or keeping values in a valid range.