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

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.

TaskCode
Choose x or ynp.where(cond, x, y)
Find indicesnp.where(cond)[0]
Many casesnp.select(conds, choices, default=...)
Bound valuesnp.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

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.