94

For example, I would like to create a mask that masks elements with value between 40 and 60:

foo = np.asanyarray(range(100))
mask = (foo < 40).__or__(foo > 60)

Which just looks ugly. I can't write

(foo < 40) or (foo > 60)

because I end up with:

  ValueError Traceback (most recent call last)
  ...
  ----> 1 (foo < 40) or (foo > 60)
  ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Is there a canonical way of doing element-wise Boolean operations on NumPy arrays with good looking code?

2
  • 3
    I'm not really convinced this is a duplicate. The other question is primarily about pandas. Commented Oct 29, 2022 at 11:47
  • I've decided your original "ugly" way of writing is actually much clearer and less prone to the missing parentheses error, so thanks for the nice idea! Commented Aug 29, 2024 at 8:41

4 Answers 4

127

Try this:

mask = (foo < 40) | (foo > 60)

Note: the __or__ method in an object overloads the bitwise or operator (|), not the Boolean or operator.

Sign up to request clarification or add additional context in comments.

8 Comments

it doesn't work: TypeError: ufunc 'bitwise_or' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
Don't forget to properly bracket your expressions
Hmm...why are the parentheses required?
@flow2k The reason is that bitwise or has higher precedence than the comparison operators (that doesn't happen with the boolean or). For more information, please have a look at the documentation
@flow2k I think the documentation that you were looking for is here: <<This ufunc implements the C/Python operator |>>.
|
32

You can use the NumPy logical operations. In your example:

np.logical_or(foo < 40, foo > 60)

Comments

28

If you have comparisons within only Booleans, as in your example, you can use the bitwise OR operator | as suggested by Jcollado. But beware, this can give you strange results if you ever use non-Booleans, such as mask = (foo < 40) | override. Only as long as override guaranteed to be either False, True, 1, or 0, are you fine.

More general is the use of NumPy's comparison set operators, np.any and np.all. This snippet returns all values between 35 and 45 which are less than 40 or not a multiple of 3:

import numpy as np
foo = np.arange(35, 46)
mask = np.any([(foo < 40), (foo % 3)], axis=0)
print foo[mask]
OUTPUT: array([35, 36, 37, 38, 39, 40, 41, 43, 44])

It is not as nice as with |, but nicer than the code in your question.

3 Comments

It's a good idea to use np.any and np.all specifically.
If you are thinking about other solutions, do not. It will save you a lot of misery!
Note that with np.any / np.all commas are sufficient to separate array conditions (no parentheses () required inside the brackets [] ), and you can have more than two conditions (evaluated element-wise as long as axis=0 is set), provided they are of the same type (e.g. all conjunctions or all alternatives, but not a mix of conjunctions and alternatives)
9

Note that you can use ~ for elementwise negation.

arr = np.array([False, True])
~arr

OUTPUT: array([ True, False], dtype=bool)

Also & does elementwise and

arr_1 = np.array([False, False, True, True])
arr_2 = np.array([False, True, False, True])

arr_1 & arr_2

OUTPUT:   array([False, False, False,  True], dtype=bool)

These also work with Pandas Series

ser_1 = pd.Series([False, False, True, True])
ser_2 = pd.Series([False, True, False, True])

ser_1 & ser_2

OUTPUT:
0    False
1    False
2    False
3     True
dtype: bool

2 Comments

According to the numpy documentation, it seems like & does bitwise and, not elementwise.
"The & operator can be used as a shorthand for np.logical_and on boolean ndarrays." numpy.org/doc/stable/reference/generated/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.