numpy
where function
conditional filtering
multiple conditions
python programming

Numpy where function multiple conditions

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

np.where is one of the most useful NumPy tools for vectorized conditional logic, but multiple conditions are also where many beginners get tripped up. The syntax looks close to normal Python conditionals, yet the rules are different because NumPy operates element by element across arrays. Once you understand the operators and grouping rules, multi-condition expressions become both fast and readable.

Combine comparisons with elementwise operators

Each comparison on a NumPy array produces another array of booleans. To combine those boolean arrays, use elementwise operators such as & for logical and, | for logical or, and ~ for logical not. Each comparison should be wrapped in parentheses.

python
1import numpy as np
2
3values = np.array([1, 5, 8, 12, 3])
4mask = (values > 4) & (values < 10)
5result = np.where(mask, values, -1)
6
7print(mask)
8print(result)

The output keeps the same shape as the input array. Elements that satisfy the mask keep their original value, while the rest are replaced with -1.

Do not use and or or

This is the most common mistake. Python's and and or are for single truth values, not entire arrays. NumPy arrays do not have one obvious true or false value, so Python raises an error when you try to use those operators directly.

python
1import numpy as np
2
3values = np.array([1, 2, 3, 4])
4
5# wrong:
6# mask = (values > 1) and (values < 4)
7
8# correct:
9mask = (values > 1) & (values < 4)
10print(mask)

The parentheses matter because & has different precedence from comparison operators. Without them, you can end up combining raw arrays and numbers in a way that produces wrong results or confusing exceptions.

Use named masks to keep logic readable

As conditions grow, readability becomes more important than squeezing everything into one line. Giving masks meaningful names makes the business rule easier to review and test.

python
1import numpy as np
2
3scores = np.array([45, 72, 88, 59, 91])
4
5is_pass = scores >= 60
6is_distinction = scores >= 85
7
8labels = np.where(
9    is_distinction,
10    "distinction",
11    np.where(is_pass, "pass", "fail")
12)
13
14print(labels)

This pattern is easier to maintain than repeating the comparisons in a deeply nested expression. It also makes debugging simpler because you can print each mask independently.

Choose between np.where, filtering, and np.select

np.where is ideal when you want an output array with the same shape as the input. If you want only the matching elements, boolean indexing is usually a better fit.

python
1import numpy as np
2
3values = np.array([3, 7, 2, 9, 5])
4mask = (values >= 5) & (values <= 8)
5
6same_shape = np.where(mask, values, 0)
7filtered = values[mask]
8
9print(same_shape)
10print(filtered)

For more than two branches, np.select is often clearer than stacking multiple np.where calls.

python
1import numpy as np
2
3temperatures = np.array([-5, 12, 28, 35])
4conditions = [
5    temperatures < 0,
6    (temperatures >= 0) & (temperatures < 30),
7    temperatures >= 30
8]
9choices = ["freezing", "normal", "hot"]
10
11labels = np.select(conditions, choices, default="unknown")
12print(labels)

When the logic has three or more branches, np.select usually reads closer to the intent.

Handle NaN values explicitly

Comparisons involving NaN do not behave like normal numbers. For example, np.nan > 4 is false, which means missing values can quietly fall into your fallback branch unless you check for them intentionally.

python
1import numpy as np
2
3arr = np.array([1.0, np.nan, 7.5, 3.2])
4mask = (~np.isnan(arr)) & (arr > 4)
5result = np.where(mask, arr, 0.0)
6
7print(result)

If missing values are meaningful in your pipeline, define an explicit rule for them instead of letting them ride along accidentally.

Common Pitfalls

  • Using Python and or or instead of NumPy's elementwise & and |.
  • Forgetting parentheses around each comparison in a combined condition.
  • Nesting too many np.where calls when np.select would be clearer.
  • Ignoring how NaN behaves during comparisons.
  • Using np.where when boolean indexing is the simpler tool for the job.

Summary

  • Multi-condition np.where works by combining boolean arrays with &, |, and ~.
  • Always wrap each comparison in parentheses.
  • Use named masks when the rule is complex enough to deserve explanation.
  • Prefer boolean indexing for filtering and np.select for many branches.
  • Treat NaN explicitly so missing values do not silently distort your logic.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.