Kodeclik Blog
How to check for values within a range in numpy arrays
Here are seven different ways to check whether values in a NumPy array are within a specified range!
Method 1: Use np.logical_and()
Our first method uses numpy’s logical_and() function to combine two conditions:
import numpy as np
a = np.array([1, 3, 7, 9, 10, 13, 14, 17, 29])
lower_bound = 7
upper_bound = 20
result = np.logical_and(a >= lower_bound, a <= upper_bound)
within_range = a[result]
print("Values within range:", within_range)
print("Indices of values within range:", np.where(result)[0])
This approach essentially creates a boolean mask and then uses it to filter the original array. The output will be:
Values within range: [ 7 9 10 13 14 17]
Indices of values within range: [2 3 4 5 6 7]
Method 2: Use np.where()
The np.where() function can be used to find the indices of elements that meet a certain condition:
import numpy as np
a = np.array([1, 3, 7, 9, 10, 13, 14, 17, 29])
lower_bound = 7
upper_bound = 20
indices = np.where((a >= lower_bound) & (a <= upper_bound))
within_range = a[indices]
print("Values within range:", within_range)
print("Indices of values within range:", indices[0])
This method directly returns the indices of elements within the specified range. The output will be:
Values within range: [ 7 9 10 13 14 17]
Indices of values within range: [2 3 4 5 6 7]
Method 3: Use boolean indexing
Boolean indexing allows you to directly filter the array based on a condition:
import numpy as np
a = np.array([1, 3, 7, 9, 10, 13, 14, 17, 29])
lower_bound = 7
upper_bound = 20
mask = (a >= lower_bound) & (a <= upper_bound)
within_range = a[mask]
print("Values within range:", within_range)
print("Indices of values within range:", np.nonzero(mask)[0])
This approach creates a boolean mask and uses it to directly index the array, returning only the values that satisfy the condition. The output will be (again):
Values within range: [ 7 9 10 13 14 17]
Indices of values within range: [2 3 4 5 6 7]
Method 4: Use np.clip() with np.isclose()
This method uses np.clip() to limit the values to the range and then compares the result with the original array:
import numpy as np
a = np.array([1, 3, 7, 9, 10, 13, 14, 17, 29])
lower_bound = 7
upper_bound = 20
clipped = np.clip(a, lower_bound, upper_bound)
mask = np.isclose(a, clipped)
within_range = a[mask]
print("Values within range:", within_range)
print("Indices of values within range:", np.where(mask)[0])
The output is:
Values within range: [ 7 9 10 13 14 17]
Indices of values within range: [2 3 4 5 6 7]
This approach is particularly useful when you want to identify values that are exactly within the range, including the boundaries. The np.isclose() function is used to handle potential floating-point precision issues.
The np.clip() method is useful when you want to consider the boundary values as part of the range and need to handle potential floating-point precision issues.
Method 5: Use np.piecewise()
The np.piecewise() function allows you to define different behaviors for different conditions:
import numpy as np
a = np.array([1, 3, 7, 9, 10, 13, 14, 17, 29], dtype=float) lower_bound = 7
upper_bound = 20
def in_range(x):
return (lower_bound <= x) & (x <= upper_bound)
result = np.piecewise(a, [in_range(a)], [lambda x: x, np.nan])
print("Values within range:", result[~np.isnan(result)])
print("Indices of values within range:", np.where(in_range(a))[0])
The output will be:
Values within range: [ 7. 9. 10. 13. 14. 17.]
Indices of values within range: [2 3 4 5 6 7]
The np.piecewise() method provides more flexibility in defining conditions and can be extended to handle more complex scenarios where you might want to apply different operations to different ranges of values.
Method 6: Use np.searchsorted()
np.searchsorted() performs a binary search on a sorted array and returns the indices where elements should be inserted to maintain order. This can be leveraged to check if values fall within a range:
import numpy as np
a = np.array([1, 3, 7, 9, 10, 13, 14, 17, 29])
lower_bound = 7
upper_bound = 20
# Sort the array if it's not already sorted
a_sorted = np.sort(a)
# Find insertion points for lower and upper bounds
left_index = np.searchsorted(a_sorted, lower_bound, side='left')
right_index = np.searchsorted(a_sorted, upper_bound, side='right')
# Extract values within the range
within_range = a_sorted[left_index:right_index]
# Find original indices
original_indices = np.where(np.isin(a, within_range))[0]
print("Values within range:", within_range)
print("Indices of values within range:", original_indices)
This method is particularly efficient for large sorted arrays, as it uses binary search which has a time complexity of O(log n).
Note that It assumes the input array is sorted. If your array isn't sorted, you need to sort it first. The 'side' parameter determines whether to return the leftmost (side='left') or rightmost (side='right') insertion point. For unsorted arrays, you can use the 'sorter' parameter to provide the indices that would sort the array, allowing you to use searchsorted() without actually sorting the original array.
The output is (again):
Values within range: [ 7 9 10 13 14 17]
Indices of values within range: [2 3 4 5 6 7]
This method is particularly useful when you're working with large sorted datasets and need to perform range queries efficiently. It's faster than iterating through the array, especially for large datasets, due to its use of binary search.
Method 7: Use elementwise multiplication and the unpacking operator
Here is an interesting approach to do the filtering for us:
import numpy as np
a = np.array([1, 3, 7, 9, 10, 13, 14, 17, 29])
lower_bound = 7
upper_bound = 20
# Printing initial array
print("Initial array:", a)
# Find elements in range 7 to 20
result = a[(a > lower_bound) * (a < upper_bound)]
# Printing result
print("Values within range (exclusive):", result)
# If you want to include the boundaries:
result_inclusive = a[(a >= lower_bound) * (a <= upper_bound)]
print("Values within range (inclusive):", result_inclusive)
This method leverages NumPy's element-wise multiplication of boolean arrays. Here's how it works. First, the condition(a > lower_bound) creates a boolean array where True represents elements greater than the lower bound. Similarly, (a < upper_bound) creates a boolean array where True represents elements less than the upper bound. These boolean arrays are multiplied element-wise. In NumPy, True is treated as 1 and False as 0 in arithmetic operations. The resulting array of 1s and 0s is used as a filter to index the original array, effectively selecting only the elements that satisfy both conditions.
This approach is extremely concise and readable. It's efficient, as it uses NumPy's vectorized operations. It doesn't require any additional NumPy functions beyond basic array operations.
In summary, we have seen seven different methods to do our task. Method 1 utilizes numpy's logical_and() function to create a boolean mask, which is then used to filter the original array and find values within the specified range. Method 2 employs np.where() to directly locate the indices of elements meeting the given conditions, while Method 3 uses boolean indexing to filter the array based on the specified range.
Method 4 combines np.clip() with np.isclose() to identify values within the range, including boundary values, and is particularly useful for handling potential floating-point precision issues. Method 5 leverages np.piecewise() to define different behaviors for various conditions, offering more flexibility for complex scenarios. Method 6 utilizes np.searchsorted() to perform a binary search on a sorted array, making it efficient for large datasets, especially when conducting range queries.
Finally, Method 7 employs elementwise multiplication and the unpacking operator to create a concise and readable approach for filtering the array. This method uses NumPy's vectorized operations, making it efficient and requiring only basic array operations without additional NumPy functions.
Which method is your favorite?
Want to learn Python with us? Sign up for 1:1 or small group classes.