ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Asked Answered
Y

11

403

Let x be a NumPy array. The following:

(x > 1) and (x < 3)

Gives the error message:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

How do I fix this?

Yak answered 8/4, 2012 at 12:56 Comment(2)
Pandas offers documentation for this as wellAlgonkian
@Algonkian That seems to have moved here: Using if/truth statements with pandas in the FAQRotative
T
305

If a and b are Boolean NumPy arrays, the & operation returns the elementwise-and of them:

a & b

That returns a Boolean array. To reduce this to a single Boolean value, use either

(a & b).any()

or

(a & b).all()

Note: if a and b are non-Boolean arrays, consider (a - b).any() or (a - b).all() instead.


Rationale

The NumPy developers felt there was no one commonly understood way to evaluate an array in Boolean context: it could mean True if any element is True, or it could mean True if all elements are True, or True if the array has non-zero length, just to name three possibilities.

Since different users might have different needs and different assumptions, the NumPy developers refused to guess and instead decided to raise a ValueError whenever one tries to evaluate an array in Boolean context. Applying and to two numpy arrays causes the two arrays to be evaluated in Boolean context (by calling __bool__ in Python3 or __nonzero__ in Python2).

Tinctorial answered 8/4, 2012 at 13:8 Comment(4)
You're right. The original code was correct. The bug appears to lie somewhere else in the code.Yak
Excellent explanation. It implies, however, that NumPy is quite inefficient: it fully evaluates both boolean arrays, whereas an efficient implementation would evaluate cond1(i)&&cond2(i) inside one single loop, and skip cond2 unless cond1 is true.Aldon
@JoachimWuttke: Although np.all and np.any are capable of short-circuiting, the argument passed to it is evaluated before np.all or np.any has a chance to short-circuit. To do better, currently, you'd have to write specialized C/Cython code similar to this.Tinctorial
That's not the best move they could do... and and & are not the same thing at all, and they do not even have the same priority.Rotgut
H
86

I had the same problem (i.e. indexing with multi-conditions, here it's finding data in a certain date range). The (a-b).any() or (a-b).all() seem not working, at least for me.

Alternatively I found another solution which works perfectly for my desired functionality (The truth value of an array with more than one element is ambigous when trying to index an array).

Instead of using suggested code above, use:

numpy.logical_and(a, b)
Hymenium answered 16/12, 2012 at 16:36 Comment(1)
This is explicit and should be the selected answer.Decompound
G
73

The reason for the exception is that and implicitly calls bool. First on the left operand and (if the left operand is True) then on the right operand. So x and y is equivalent to bool(x) and bool(y).

However the bool on a numpy.ndarray (if it contains more than one element) will throw the exception you have seen:

>>> import numpy as np
>>> arr = np.array([1, 2, 3])
>>> bool(arr)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

The bool() call is implicit in and, but also in if, while, or, so any of the following examples will also fail:

>>> arr and arr
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

>>> if arr: pass
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

>>> while arr: pass
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

>>> arr or arr
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

There are more functions and statements in Python that hide bool calls, for example 2 < x < 10 is just another way of writing 2 < x and x < 10. And the and will call bool: bool(2 < x) and bool(x < 10).

The element-wise equivalent for and would be the np.logical_and function, similarly you could use np.logical_or as equivalent for or.

For boolean arrays - and comparisons like <, <=, ==, !=, >= and > on NumPy arrays return boolean NumPy arrays - you can also use the element-wise bitwise functions (and operators): np.bitwise_and (& operator)

>>> np.logical_and(arr > 1, arr < 3)
array([False,  True, False], dtype=bool)

>>> np.bitwise_and(arr > 1, arr < 3)
array([False,  True, False], dtype=bool)

>>> (arr > 1) & (arr < 3)
array([False,  True, False], dtype=bool)

and bitwise_or (| operator):

>>> np.logical_or(arr <= 1, arr >= 3)
array([ True, False,  True], dtype=bool)

>>> np.bitwise_or(arr <= 1, arr >= 3)
array([ True, False,  True], dtype=bool)

>>> (arr <= 1) | (arr >= 3)
array([ True, False,  True], dtype=bool)

A complete list of logical and binary functions can be found in the NumPy documentation:

Gloomy answered 3/6, 2017 at 15:46 Comment(3)
This should be the top answer, simply because there are many duplicate questions and the problem is created with a variety of setups (in the questions I've seen, the if arr: version of the problem is most common), and this is the answer that comprehensively shows those setups and explains what they have in common.Audiphone
That's the most instructive answer. I experimented a bit and saw the error is actually returned by a.__bool__() on the array. This function may be called by Python's bool, according to the logic described in Truth Value Testing, but I'm not so sure as this section pertains to built-in types.Robena
This should really be the top answer! Only this answered the posted question, the others just focused on explaining but did not give a proper answer like this!Gyrostatic
A
6

Cause

This error occurs any time that the code attempts to convert a Numpy array to boolean (i.e., to check its truth value, as described in the error message). For a given array a, this can occur:

Numpy arrays and comparisons (==, !=, <, >, <=, >=)

Comparisons have a special meaning for Numpy arrays. We will consider the == operator here; the rest behave analogously. Suppose we have

import numpy as np
>>> a = np.arange(9)
>>> b = a % 3
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8])
>>> b
array([0, 1, 2, 0, 1, 2, 0, 1, 2])

Then, a == b does not mean "give a True or False answer: is a equal to b?", like it would usually mean. Instead, it will compare the values element by element, and evaluate to an array of boolean results for those comparisons:

>>> a == b
array([ True,  True,  True, False, False, False, False, False, False])

In other words, it does the same kind of broadcasting that mathematical operators (like b = a % 3) do.

It does not make sense to use this result for an if statement, because it is not clear what to do: should we enter the if block, because some of the values matched? Or should we enter the else block, because some of the values didn't match? Here, Numpy applies an important principle from the Zen of Python: "In the face of ambiguity, refuse the temptation to guess."

Thus, Numpy will only allow the array to be converted to bool if it contains exactly one element. (In some older versions, it will also convert to False for an empty array; but there are good logical reasons why this should also be treated as ambiguous.)

Similarly, comparing a == 4 will not check whether the array is equal to the integer (of course, no array can ever be equal to any integer). Instead, it will broadcast the comparison across the array, giving a similar array of results:

>>> a == 4
array([False, False, False, False,  True, False, False, False, False])

Fixing expressions

  • If the code is explicitly converting to bool, choose between applying .any or .all to the result, as appropriate. As the names suggest, .any will collapse the array to a single boolean, indicating whether any value was truthy; .all will check whether all values were truthy.
    >>> (a == 4).all() # `a == 4` contains some `False` values
    False
    >>> (a == 4).any() # and also some `True` values
    True
    >>> a.all() # We can check `a` directly as well: `0` is not truthy,
    False
    >>> a.any() # but other values in `a` are.
    True
    
    If the goal is to convert a to boolean element-wise, use a.astype(bool), or (only for numeric inputs) a != 0.
  • If the code is using boolean logic (and/or/not), use bitwise operators (&/|/~, respectively) instead:
    >>> ((a % 2) != 0) & ((a % 3) != 0) # N.B. `&`, not `and`
    array([False,  True, False, False, False,  True, False,  True, False])
    
    Note that bitwise operators also offer access to ^ for an exclusive-or of the boolean inputs; this is not supported by logical operators (there is no xor).
  • For a list (or other sequence) of arrays that need to be combined in the same way (i.e., what the built-ins all and any do), instead build the corresponding (N+1)-dimensional array, and use np.all or np.any along axis 0:
    >>> a = np.arange(100) # a larger array for a more complex calculation
    >>> sieves = [a % p for p in (2, 3, 5, 7)]
    >>> all(sieves) # won't work
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: The truth value of an array with more than one element is ambiguous.
     Use a.any() or a.all()
    >>> np.all(np.array(sieves), axis=0) # instead:
    array([False,  True, False, False, False, False, False, False, False,
           False, False,  True, False,  True, False, False, False,  True,
           False,  True, False, False, False,  True, False, False, False,
           False, False,  True, False,  True, False, False, False, False,
           False,  True, False, False, False,  True, False,  True, False,
           False, False,  True, False, False, False, False, False,  True,
           False, False, False, False, False,  True, False,  True, False,
           False, False, False, False,  True, False, False, False,  True,
           False,  True, False, False, False, False, False,  True, False,
           False, False,  True, False, False, False, False, False,  True,
           False, False, False, False, False, False, False,  True, False,
           False])
    

Fixing if statements

First, keep in mind that if the code has an if statement that uses a broken expression (like if (a % 3 == 0) or (a % 5 == 0):), then the expression will also need to be fixed.

Generally, an explicit conversion to bool (using .all() or .any() as above) will avoid an exception:

>>> a = np.arange(20) # enough to illustrate this
>>> if ((a % 3 == 0) | (a % 5 == 0)).any():
...     print('there are fizzbuzz values')
... 
there are fizzbuzz values

but it might not do what is wanted:

>>> a = np.arange(20) # enough to illustrate this
>>> if ((a % 3 == 0) | (a % 5 == 0)).any():
...     a = -1
... 
>>> a
-1

If the goal is to operate on each value where the condition is true, then the natural way to do that is to use the result array as a mask. For example, to assign a new value everywhere the condition is true, simply index into the original array with the computed mask, and assign:

>>> a = np.arange(20)
>>> a[(a % 3 == 0) | (a % 5 == 0)] = -1
>>> a
array([-1,  1,  2, -1,  4, -1, -1,  7,  8, -1, -1, 11, -1, 13, 14, -1, 16,
       17, -1, 19])

This indexing technique is also useful for finding values that meet a condition. Building on the previous sieves example:

>>> a = np.arange(100)
>>> sieves = [a % p for p in (2, 3, 5, 7)]
>>> a[np.all(np.array(sieves), axis=0)]
array([ 1, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
       73, 79, 83, 89, 97])

(Exercise: study the code and understand why this result isn't quite a list of primes under 100; then fix it.)

Using Pandas

The Pandas library has Numpy as a dependency, and implements its DataFrame type on top of Numpy's array type. All the same reasoning applies, such that Pandas Series (and DataFrame) objects cannot be used as boolean: see Truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

The Pandas interface for working around the problem is a bit more complicated - and best understood by reading that Q&A. The question specifically covers Series, but the logic generally applies to DataFrames as well. Please also see If condition with a dataframe for more specific guidance with applying conditional logic to a DataFrame.

Audiphone answered 3/2, 2023 at 16:5 Comment(0)
T
3

if you work with pandas what solved the issue for me was that i was trying to do calculations when I had NA values, the solution was to run:

df = df.dropna()

And after that the calculation that failed.

Theurich answered 3/3, 2019 at 18:57 Comment(1)
This cannot meaningfully be related to the question. Nobody who gets the corresponding error message will be able to tell whether this solves the problem, except by trying it with crossed fingers (and then they might not have properly tested it).Audiphone
L
3

Taking up @ZF007's answer, this is not answering your question as a whole, but can be the solution for the same error. I post it here since I have not found a direct solution as an answer to this error message elsewhere on Stack Overflow.

The error, among others, appears when you check whether an array was empty or not.

  • if np.array([1,2]): print(1) --> ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all().

  • if np.array([1,2])[0]: print(1) --> no ValueError, but: if np.array([])[0]: print(1) --> IndexError: index 0 is out of bounds for axis 0 with size 0.

  • if np.array([1]): print(1) --> no ValueError, but again will not help at an array with many elements.

  • if np.array([]): print(1) --> DeprecationWarning: The truth value of an empty array is ambiguous. Returning False, but in future this will result in an error. Use 'array.size > 0' to check that an array is not empty.

  • if np.array([]).size is not None: print(1): Taking up a comment by this user, this does not work either. This is since no np.array can ever be the same object as None - that object is unique - and thus will always match is not None (i.e. never match is None) whether or not it's empty.

Doing so:

  • if np.array([]).size: print(1) solved the error.
Luzern answered 30/11, 2020 at 23:59 Comment(5)
Another possibly less confusing way could be: if np.array([]) is not None: print(1)Aerometeorograph
@Aerometeorograph no, that is not useful, and I rolled back the corresponding edit. No np.array can ever be the same object as None - that object is unique - and thus will always match is not None (i.e. never match is None) whether or not it's empty.Audiphone
@KarlKnechtel Good catch, should have tested it. I put it in the answer.Luzern
I'm not sure it's worth calling out separately, because I doubt many people would come up with that idea independently. Up to you, though.Audiphone
@KarlKnechtel Not sure either. I think it is just a list of anything you can think of, and this one was still missing.Luzern
B
0

This typed error-message also shows while an if-statement comparison is done where there is an array and for example a bool or int. See for example:

... code snippet ...

if dataset == bool:
    ....

... code snippet ...

This clause has dataset as array and bool is euhm the "open door"... True or False.

In case the function is wrapped within a try-statement you will receive with except Exception as error: the message without its error-type:

The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Bargeboard answered 13/3, 2020 at 13:55 Comment(0)
U
0

Normally, when you compare two single digits the Python regular codes work correctly, but inside an array there are some digits (more than one number) that should be processed in parallel.

For example, let us assume the following:

a = np.array([1, 2, 3])
b = np.array([2, 3, 4])

And you want to check if b >= a: ?

Because, a and b are not single digits and you actually mean if every element of b is greater than the similar number in a, then you should use the following command:

if (b >= a).all():
 print("b is greater than a!")
Underproof answered 23/8, 2022 at 9:51 Comment(0)
C
0

There are examples that perfectly answer the question. But still, I wanted to go deeper and understand more about this error and where this error has originated from.

In this expression (x > 1) and (x < 3) we are performing logical and operations between two NumPy arrays so let's assume the NumPy array returned after (x > 1) is numpy_array1 and similarly after (x < 3) we got numpy_array2 and this can be written as numpy_array1 and numpy_array2.

When we perform logical and operations between two objects they are passed to the bool function to get the boolean value. So numpy_array1 and numpy_array2 is evaluated as bool(numpy_array1) and bool(numpy_array2)

bool(numpy_array1) will call __bool__ method of NumPy array and in the bool method some comparison is performed.

  • If the array is empty, then raise DeprecationWarning and return False.
  • If the array has one element return a boolean value.
  • If there is more than one element, then raise Value Error (ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all())

This can be understood further with a simple program:

import warnings

class Plan:
    def __init__(self):
        self.values = []
        
    def append(self, val):
        self.values.append(val)
        
    def empty(self):
        self.values.clear()
        
    def __bool__(self):
        size = len(self.values)
        if size == 0:
            warnings.warn("The truth value of an empty array is ambiguous. Returning False, but in future this will result in an error.", DeprecationWarning)
            return False
        if size == 1:
            return bool(self.values[0])
        raise ValueError("ValueError: The truth value of an array with more than one element is ambiguous.")

Lets instantiate the Plan class and use the object of it:

plan = Plan()
bool(plan)

DeprecationWarning: The truth value of an empty array is ambiguous. Returning False, but in future this will result in an error.
  warnings.warn("The truth value of an empty array is ambiguous. Returning False, but in future this will result in an error.", DeprecationWarning)
False


plan = Plan()
plan.append(4)
bool(plan)
True


plan = Plan()
plan.append(False)
bool(plan)
False


plan = Plan()
plan.append(False)
plan.append(False)
bool(plan)
ValueError: ValueError: The truth value of an array with more than one element is ambiguous.

This is the reason when we call if numpy_arr will throw ValueError when the numpy_arr has more than one element because if numpy_arr will be evaluated as if bool(numpy_arr).

Custard answered 2/2 at 10:10 Comment(0)
A
-1

For me, this error occurred on testing, code with error below:

pixels = []
self.pixels = numpy.arange(1, 10)
self.assertEqual(self.pixels, pixels)

This code returned:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Because i cannot assert with a list the object returned by method arrange of numpy.

Solution as transform the arrange object of numpy to list, my choice was using the method toList(), as following:

pixels = []
self.pixels = numpy.arange(1, 10).toList()
self.assertEqual(self.pixels, pixels)
Algiers answered 27/5, 2022 at 21:11 Comment(2)
This doesn't quite understand the problem correctly. A numpy array can be compared to a list, and the result can be asserted. The problems are more subtle: first, the pixels list is empty, which does not allow for proper broadcasting. Second, if the pixels list had the right size/shape for broadcasting, the result of comparing pixels to self.pixels would be a Numpy array, which would break the conditional logic inside assertEqual. However, it would be possible to write a test like self.assertTrue((numpy.arange(1, 10) == range(1, 10)).all()).Audiphone
But yes; in general, because this is testing code, the .toList approach shown does make more sense. That will prevent the test from raising an exception when, say, the code under test returns an array with the wrong shape.Audiphone
A
-1

Simplest answer is use "&" instead of "and".

>>> import numpy as np
>>> arr = np.array([1, 4, 2, 7, 5])
>>> arr[(arr > 3) and (arr < 6)]   # this will fail
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> arr[(arr > 3) & (arr < 6)]   # this will succeed
array([4, 5])
Artur answered 31/12, 2022 at 16:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.