How do I select elements of a NumPy array by condition?
Short answer
Build a boolean array from a condition and use it to index, or pass a list of positions for fancy indexing. Both return a copy, and boolean masks combine with the bitwise operators, not and or.
The short version
Index a NumPy array with a boolean array to keep the elements where it is True, or with a list of integer positions to pick those positions (fancy indexing). Both return a new array (a copy), not a view. Combine boolean conditions with the bitwise operators &, | and ~, each condition in brackets, because Python's and and or do not work element by element.
Boolean masking
import numpy as np
arr = np.array([10, 20, 30, 40])
print(arr[arr > 20]) # [30 40]
The expression arr > 20 produces a boolean array [False, False, True, True], and indexing with it keeps the True positions. This is the array equivalent of filtering.
Combining conditions
Use bitwise operators, and bracket each condition.
arr[(arr > 15) & (arr < 35)] # [20 30]
Writing arr > 15 and arr < 35 raises ValueError: The truth value of an array is ambiguous, because and tries to collapse a whole array to one True or False. Always use & and | on arrays.
Fancy indexing by position
Passing a list or array of integers selects those positions, in the order given, which also lets you repeat or reorder elements.
arr[[0, 2]] # [10 30]
arr[[2, 2, 0]] # [30 30 10], repeats and reorders
Both return copies
Unlike a basic slice, both boolean masking and fancy indexing return an independent copy, so modifying the result does not touch the original. If you want to modify the original at those positions, assign back into it using the same index.
arr[arr > 20] = 0 # sets matching elements in arr itself
print(arr) # [10 20 0 0]
Assigning through the mask writes back to arr, which is the idiomatic way to update selected elements in place.
Two-dimensional selection
For a 2D array, you can mask the whole thing (which flattens the result) or select rows and columns with integer arrays.
grid = np.array([[1, 2], [3, 4]])
grid[grid > 2] # [3 4], flattened
grid[[0, 1], [1, 0]] # [2 3], element (0,1) and (1,0)
The paired lists in the last example select specific coordinates, which is powerful but easy to misread, so comment it when you use it.
Why this matters for performance
Masking and fancy indexing run in fast compiled code across the whole array, so they replace explicit Python loops over elements. Reaching for a boolean mask instead of a for loop is one of the core habits that makes NumPy code both short and quick.