2

I have a question regarding the equality operator (==) regarding lists. When I run my script, I get an error in an if statement

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

The if statement looks like this

if variable_a == variable_b:

I know that that this is caused by an array of boolean values being evaluated by the if statement, however to debug I printed the inputs to the function. I know they are from the type list.

print(variable_a)
print(variable_b)

I get an output that looks like this

[[1,2],3]
[3,4]

But then I try the following in the Console

[[1,2],3] == [3,4]

which returns False. This is not an array of boolean values so I don't know why the if statement fails. I am using python 3.9.

Edit: I just ran

print(type(variable_a))
print(type(variable_b))

which both returned

<class 'list'>

So I am definitely sure that I am comparing lists and not numpy arrays or pandas dataframes.

Ignatius Reilly
  • 1,594
  • 2
  • 6
  • 15
  • What do you mean by "the Console"? – Scott Hunter Mar 03 '23 at 20:47
  • I am using PyCharm and am using the Python Console to directly execute code – TriforceFiction Mar 03 '23 at 20:48
  • 6
    If you're getting that error you're dealing with numpy arrays, not lists. – Barmar Mar 03 '23 at 20:52
  • 1
    Do you know they're of type `list` or that they print out in a form that looks like lists? What is `type(variable_a)` and `type(variable_b)`? – Silvio Mayolo Mar 03 '23 at 20:54
  • 1
    Those can't be lists, or you wouldn't get that error. I'm not sure what type they are, but FWIW, NumPy produces [similar errors](/q/10062954/4518341), only NumPy arrays wouldn't print like that. Please make a [mre]. BTW, welcome to Stack Overflow! Check [How to ask a good question](/help/how-to-ask) for more tips. You can [edit] to clarify. – wjandrea Mar 03 '23 at 20:54
  • You are confusing built in `list` objects with `numpy.ndarray` objects – juanpa.arrivillaga Mar 03 '23 at 20:59
  • 2
    Welcome to Stack Overflow. For reference for future questions, please read [ask] and [mre]. We don't provide a debugging service; in order to explain a code behaviour, we need code **in the question itself** that we can **copy and paste, without adding or changing anything**, to reproduce the **exact problem, directly**. – Karl Knechtel Mar 03 '23 at 22:39

2 Answers2

4

This is a curious interaction between the built-in list type's comparison, and Numpy's comparison - which is implemented by each Numpy datatype, not just arrays.

A truly minimal example:

>>> import numpy as np
>>> a = np.int64(1)
>>> [a] == [[a, a]]
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()

The comparison involves the following chain of events:

  • [a] and [[a, a]] are both lists, so the list __eq__ method is used.
  • First, the identities and lengths of the lists are checked. They're separate lists of the same length, so the code can't bail out early.
  • So we must compare the lists elementwise, and check that each pair is equal.
  • The first elements of the lists are a and [a, a], so we compare those.
  • The call a.__eq__([a, a]) succeeds: a is a Numpy scalar, and [a, a] is iterable, so Numpy broadcasts the comparison across the list.
  • Now the overall list comparison needs to check whether the equality comparison returned a truthy result.However, the result of the __eq__ call was a Numpy array (array([True, True])), which doesn't allow conversion to boolean (i.e., the standard problem). So an exception is raised.

But notice:

>>> [a] == [[a]]
True

When a is compared to [a], the resulting array has exactly one total element. Numpy treats this case specially.

>>> [[a]] == [[a, a]]
False

When [a] is compared to [a, a], both are lists, so Numpy doesn't get to interfere yet. The list equality method is called again, and it checks the lengths of the lists first. Since the lengths differ, False is returned before any Numpy broadcasting can occur.

Also of note:

>>> [[a, a]] == [a]
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()

Even though the list is clearly "not equal to" the Numpy scalar, the list comparison operation will return NotImplemented rather than False, allowing the Numpy scalar's __eq__ to be tried after that.

>>> [1] == [[a,a]]
False

This succeeds because the ordinary integer 1 will be compared to a list of Numpy objects, and the integer doesn't implement any broadcasting.

>>> [a] == [[1, 1]]
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()

The Numpy scalar will be compared to a list of integers, and broadcast across that; and the scalar value is indeed comparable to integer.

>>> [a] == [['a', 'b']]
<stdin>:1: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
False
>>> [a] == [['a', 1]]
False
>>> [a] == [[1.0, 1]]
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()

The current behaviour is dependent upon having a list of numeric types.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • 2
    Meh, I'd say my `[[0,0]] == [a]` is even more minimal. – Kelly Bundy Mar 03 '23 at 23:09
  • I'd suggest adding `a = np.int32(1)` to the top of this answer (or to the question, I guess), so it's what `a` is without the other answer.\ – Kaia Mar 03 '23 at 23:12
  • I incorporated the relevant changes while editing in some other useful information. – Karl Knechtel Mar 03 '23 at 23:20
  • 2
    Another fun one: An iterable of two numeric values that doesn't fail: `[a] == [b'00']`. – Kelly Bundy Mar 03 '23 at 23:23
  • 2
    Yep. It doesn't fail with `bytes` or `set`, but it does with `list`, `tuple` and - curiously - `bytearray`. The exact rule is probably in the documentation *somewhere*.... – Karl Knechtel Mar 03 '23 at 23:26
  • Do you know why that is? I would expect scalars to always act like scalars, so for them to do broadcasting is totally unexpected to me, but I'm not a NumPy expert. On the other hand, if it were a 0d array like `np.array(1)`, then broadcasting would make sense. – wjandrea Mar 04 '23 at 19:15
  • 1
    @wjandrea my first thought was "so that either the scalar or the array can be on the left-hand side". But that clearly doesn't matter, because `__eq__` will try it the other way around if the first way returns `NotImplemented`. My best guess is that they wanted the user to be able to omit, as much as possible, calls to create an array explicitly from nested lists. It seems very un-Pythonic to me that way, though. Just too... implicit. – Karl Knechtel Mar 04 '23 at 23:25
3

The mistake was that I used a numpy.float32 instead of the normal float datatype as the values in my list. It was indeed a list, but I didn't check the datatype of the floats inside the list. If you want to recreate the error, you can do it like this

a = np.float32(1)
b = np.float32(2)
c = np.float32(3)
d = [[a,b],c]
e = [b,c]
d == e
wjandrea
  • 28,235
  • 9
  • 60
  • 81
  • Beside the point, but the question has ints, not floats. Were you maybe using `int32`, not `float32`? – wjandrea Mar 03 '23 at 22:37
  • 3
    Oh, that **is** interesting, actually. I can reproduce this problem, while there are many other similar lists that *won't* cause the problem. – Karl Knechtel Mar 03 '23 at 22:42
  • Smaller case: `[[0,0]] == [np.int32(0)]` – Kelly Bundy Mar 03 '23 at 23:00
  • This is really interesting. I'd be interested if anybody knows why this behavior happens. Nice find! – Kaia Mar 03 '23 at 23:03
  • 3
    Since this is the actual MRE for the problem, it should be edited into the question (rather, the question edited *around* presenting that MRE) rather than being a separate answer. – Karl Knechtel Mar 04 '23 at 00:13