# Logical operations on Boolean arrays#

`np.logical_and`

, `np.logical_or`

:

Sometimes we want to combine Boolean values using logical operators like AND, OR, NOT. This is straightforward for Python Booleans:

```
# Logical AND - True only if both are True
print(True and True)
print(True and False)
```

```
True
False
```

```
# Logical OR - True if either or both are True
print(True or True)
print(True or False)
print(False or False)
```

```
True
True
False
```

```
# Logical NOT - inverts truth value
print(not True)
print(not False)
```

```
False
True
```

We have to do a little more work for *arrays* of Booleans, because the Python
`and`

, `or`

, `not`

operators only return a single Boolean values, and so
do not operate as we expect on arrays:

```
import numpy as np
```

```
bool1 = np.array([True, True, False, False])
bool2 = np.array([False, True, False, True])
```

```
bool1 and bool2
```

```
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[6], line 1
----> 1 bool1 and bool2
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

```
bool1 or bool2
```

```
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[7], line 1
----> 1 bool1 or bool2
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

To do elementwise AND, OR, NOT, we can use `np.logical_and, np.logical_or, np.logical_not`

:

```
# "logical_and" True where both of bool1 and bool2 are True
np.logical_and(bool1, bool2)
```

```
array([False, True, False, False])
```

```
# "logical_or" True where either of bool1 and bool2 are True
np.logical_or(bool1, bool2)
```

```
array([ True, True, False, True])
```

```
# "logical_not" True where input array is False
np.logical_not(bool1)
```

```
array([False, False, True, True])
```

## Using the bitwise operators#

Equivalently, the `&`

, `|`

and `~`

operators are applied elementwise.

These are called *bitwise* operators, for reasons we do not need to go into here. *Iff applied to Boolean values* then:

`&`

gives the same result as`np.logical_and`

`|`

gives the same result as`np.logical_or`

`~`

gives the same result as`np.logical_not`

```
bool1 & bool2
```

```
array([False, True, False, False])
```

```
bool1 | bool2
```

```
array([ True, True, False, True])
```

```
~bool1
```

```
array([False, False, True, True])
```

## Bitwise, brackets#

**Be careful when using the bitwise operators**. The bitwise operators have
relatively high operator precedence, meaning that Python will prefer to apply
the bitwise operator *before* other operators, such as comparison operators.

For example, consider these arrays, and the Boolean arrays from their comparison:

```
first = np.array([1, 0, 1])
first == 1
```

```
array([ True, False, True])
```

```
second = np.array([0, 1, 1])
second == 1
```

```
array([False, True, True])
```

```
# This will give an error. Why?
first == 1 & second == 1
```

```
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[16], line 2
1 # This will give an error. Why?
----> 2 first == 1 & second == 1
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

The problem is that Numpy registers `&`

as having higher operator
preference
than `==`

, so it does the `&`

operation before the `==`

, meaning that the code
above is equivalent to:

```
first == (1 & second) == 1
```

```
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[17], line 1
----> 1 first == (1 & second) == 1
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

Therefore, you get the error like this:

```
# Python is doing this under the hood in the statement above.
res = 1 & second
```

```
# Python next does this, generating the error.
first == res == second
```

```
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[19], line 2
1 # Python next does this, generating the error.
----> 2 first == res == second
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

The exact reason this last statement gives an error is a little
advanced. It
is because Python *internally* translates the statement above to:

```
# Python internally translates "first == res == second) to:
(first == res) and (res == second)
```

```
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[20], line 2
1 # Python internally translates "first == res == second) to:
----> 2 (first == res) and (res == second)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

The problem with this internal translation is that `and`

does not work when
comparing arrays:

```
# "and" does not work on arrays.
np.array([True, False]) and np.array([True, False])
```

```
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[21], line 2
1 # "and" does not work on arrays.
----> 2 np.array([True, False]) and np.array([True, False])
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

The fix is to specify that you want the `==`

operation done *before* the `&`

operation, using parentheses:

```
# Guarantee the order of operations with parentheses.
(first == 1) & (second == 1)
```

```
array([False, False, True])
```

To avoid worrying about this problem, you might prefer to use `np.logical_and`

etc:

```
# The same operation using logical_and
np.logical_and(first == 1, second == 1)
```

```
array([False, False, True])
```