64

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 that with good looking code?

jb.
  • 20,419
  • 16
  • 91
  • 130

4 Answers4

99

Have you tried this?

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

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

jcollado
  • 35,754
  • 6
  • 94
  • 129
  • 3
    Oh well that really was stupid of me. Of course it works :) – jb. Dec 25 '11 at 23:11
  • 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'' – Mehdi Jul 24 '15 at 12:41
  • 7
    Don't forget to properly bracket your expressions – gota Jul 18 '16 at 16:04
  • 1
    Hmm...why are the parentheses required? – flow2k Feb 05 '19 at 07:24
  • 2
    @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](https://docs.python.org/3/reference/expressions.html#operator-precedence) – jcollado Feb 05 '19 at 11:32
  • @jcollado Thanks. It appears `|` is overloaded by NumPy to work with boolean NumPy arrays (since it doesn't act a strict "bitwise" operator anymore). Strangely, I was not able to find this documented in NumPy's documentation: https://docs.scipy.org/doc/numpy-1.13.0/reference/generated/numpy.logical_and.html#numpy.logical_and Or is it? What I'm trying to get at is...is it "officially" supported syntax? – flow2k Feb 05 '19 at 19:22
  • 1
    @flow2k I think the documentation that you were looking for is [here](https://docs.scipy.org/doc/numpy/reference/generated/numpy.bitwise_or.html): <>. – jcollado Feb 06 '19 at 15:25
  • @jcollado Great! Looks like `|` not only works element-wise, it will operate *bitwise* on those elements - which makes a difference for integer arrays, but not boolean arrays, in comparison to `np.logical_or(.)`. I like the `|` syntax more, but the requirement of parentheses in Python (a source of hard-to-find bugs) still makes me a little envious of MATLAB users... – flow2k Feb 06 '19 at 22:57
19

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])

Not as nice as with |, but nicer than the code in your question.

dirkjot
  • 2,723
  • 21
  • 17
17

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

np.logical_or(foo < 40, foo > 60)
rafaelc
  • 48,227
  • 12
  • 46
  • 72
ViennaMike
  • 1,926
  • 1
  • 19
  • 34
6

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
Roko Mijic
  • 5,087
  • 3
  • 24
  • 34
  • 7
    According to the numpy documentation, it seems like `&` does [_bitwise_ and](https://docs.scipy.org/doc/numpy/reference/generated/numpy.bitwise_and.html#numpy.bitwise_and), not elementwise. – HelloGoodbye Oct 24 '16 at 16:36