数值错误:数组中有多个元素的真值不明确。请使用a.any()或a.all()。

391

假设有一个NumPy数组 x。下面这段代码:

(x > 1) and (x < 3)

出现以下错误:

ValueError: 数组中有多个元素的真值不明确。请使用 a.any() 或 a.all()

该如何修复?


2
Pandas也提供了相应的文档,详见:http://pandas.pydata.org/pandas-docs/stable/indexing.html#boolean-indexing - Greg
1
@Greg,似乎已经移至此处:在FAQ中使用if/truth语句处理pandas - wjandrea
10个回答

300
如果 ab 是布尔类型的 NumPy 数组,那么 & 操作会返回它们对应元素的逐个与操作结果:
a & b

这会返回一个布尔型数组。要将其缩减为单个的布尔,可以使用以下方法之一

(a & b).any()
或者
(a & b).all()

注意:如果ab非布尔数组,请考虑改用(a - b).any()(a - b).all()


背景

Numpy 的开发者们认为,在布尔上下文中评估数组没有一个公认的方式:它可以表示只要有一个元素为True就返回True,也可以表示只有所有元素都为True才返回True;或者仅在数组长度非零时返回True,这只是其中的三种可能性。

由于不同的用户可能会有不同的需求和假设,Numpy 开发者拒绝猜测,并决定在尝试在布尔上下文中评估数组时引发ValueError异常。将and应用于两个 Numpy 数组会导致这两个数组在布尔上下文中被评估(通过在 Python 3 中调用__bool__或在 Python 2 中调用__nonzero__)。


4
没错,原始代码是正确的。这个 bug 似乎出现在代码的其他地方。 - Homunculus Reticulli
4
解释很好。然而,这意味着NumPy效率不高:它会完全评估两个布尔数组,而一个高效的实现应该在单个循环内评估cond1(i)&&cond2(i),并且只有在cond1为真时才跳过cond2。 - Joachim W
1
@JoachimWuttke:虽然np.allnp.any能够短路,但是传递给它的参数在np.allnp.any有机会短路之前就已经被评估了。目前要做得更好,您需要编写专门的C/Cython代码类似于此 - unutbu
这不是他们可以做的最好的选择... and& 根本不是同一回事,它们甚至没有相同的优先级。 - Camion

84

我有同样的问题(即使用多条件索引,这里是在特定日期范围内查找数据)。(a-b).any()(a-b).all()似乎不起作用,至少对我来说是这样。

相反,我找到了另一种完美解决我的期望功能的方法(在尝试索引数组时,一个具有多个元素的数组的真值是模糊的)。

不要使用上面建议的代码,使用以下代码:

numpy.logical_and(a, b)

这是明确的,应该被选为答案。 - Yash Jakhotiya

69
异常的原因是and隐式调用bool,首先在左操作数上执行(如果左操作数为True),然后在右操作数上执行。因此x and y相当于bool(x) and bool(y)
但是对于numpy.ndarray(如果它包含多个元素)上的bool会抛出你见过的异常:
>>> 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()

bool()调用在and中是隐式的,但在ifwhileor中也是隐式的,因此以下任何示例都会失败:

>>> 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()
在Python中,有更多的函数和语句可以隐藏bool调用,例如2 < x < 10只是另一种写法,等同于2 < x and x < 10。而and将会调用bool: bool(2 < x) and bool(x < 10)
对于按元素处理的相应操作,and的等价函数是np.logical_and,类似地,or的等价函数是np.logical_or
对于布尔数组 - 和NumPy数组上的<<===!=>=>比较返回布尔NumPy数组 - 也可以使用按元素位运算的功能(和运算符):np.bitwise_and&运算符)。
>>> 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)

bitwise_or|运算符):

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

在NumPy文档中可以找到逻辑和二进制函数的完整列表:


1
这应该是最佳答案,因为有许多重复的问题,并且问题是由各种设置创建的(在我看到的问题中,“if arr:”版本的问题最常见),而这个答案全面展示了这些设置并解释了它们的共同点。 - Karl Knechtel
那是最有教育意义的答案。我进行了一些实验,发现错误实际上是由数组的a.__bool__()返回的。根据真值测试中描述的逻辑,Python的bool可能会调用这个函数,但我对此并不确定,因为这一部分涉及到内置类型。 - mins

3
采用 @ZF007 的答案,虽然不能完全回答你的问题,但可以解决同样的错误。我在这里发布它,因为我在 Stack Overflow 上没有找到直接解决此错误消息的答案。
当您检查数组是否为空时,会出现该错误以及其他错误。 这样做:
  • if np.array([]).size: print(1) 解决了这个错误。

1
另一种可能不那么令人困惑的方式是:if np.array([]) is not None: print(1) - loki
1
@loki 不,那并没有什么用,我已经回滚了相应的编辑。任何 np.array 都不可能是与 None 相同的对象 - 该对象是唯一的 - 因此无论它是否为空,它始终会匹配 is not None(即永远不会匹配 is None)。 - Karl Knechtel
@KarlKnechtel 很好的发现,应该测试一下。我已经把它放在答案里了。 - questionto42
我不确定是否值得单独提出来,因为我怀疑很少有人会独立想出那个想法。不过这取决于你。 - Karl Knechtel
@KarlKnechtel 不确定。我认为它只是一个你能想到的任何东西的列表,而这个还没有列出来。 - questionto42

3
如果你使用的是 pandas ,那么解决这个问题的方法是在有 NA 值时避免进行计算,正确的做法是运行以下命令:df = df.dropna(),然后再进行计算即可。

这与问题没有实质关联。收到相应错误信息的人无法确定这是否解决了问题,除非他们试图交叉手指测试(即使如此,他们可能也未经过充分测试)。 - Karl Knechtel

3

原因

无论何时代码试图将Numpy数组转换为布尔值(即检查其“真值”,如错误消息中所述),都会发生此错误。对于给定的数组a,这可能会发生:

明确地,通过使用bool(a)
隐式地 使用布尔逻辑运算符a and aa or anot a
隐式地使用 内置的anyall函数。(这些函数可以接受一个数组,无论它有多少维;但不能接受一个数组列表、元组、集合等 of arrays。)
if语句中隐式地使用if a:。虽然通常可以在if语句中使用任何Python对象,但Numpy数组故意打破了这个功能,因为否则可能会很容易地通过错误编写不正确的代码。

Numpy数组和比较(==!=<><=>=

对于Numpy数组,比较具有特殊的含义。我们将在这里考虑==运算符;其余运算符的行为类似。假设我们有:

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

然后,a == b并不意味着“给出一个TrueFalse的答案:即a是否等于b?”通常情况下它会这样解释。相反地,它将逐个元素比较这些值,并评估这些比较的布尔结果数组
>>> a == b
array([ True,  True,  True, False, False, False, False, False, False])

换句话说,它执行与数学运算符(如b = a%3)相同类型的广播。

if语句中使用此结果没有意义,因为不清楚该怎么做:我们应该进入if块,因为一些值匹配了吗?还是我们应该进入else块,因为一些值没有匹配?在这里,Numpy应用了Python之禅中的一个重要原则:“面对模棱两可的情况,拒绝猜测的诱惑。”

因此,只有当数组包含恰好一个元素时,Numpy才允许将其转换为bool。(在一些旧版本中,它也会将空数组转换为False;但是有很好的逻辑原因说明为什么这也应被视为模棱两可。)

同样地,比较 a == 4 并不会 检查数组是否等于整数(当然,任何数组都无法等于任何整数)。相反,它将在整个数组中广播比较,给出类似的结果数组:

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

修复表达式

  • 如果代码明确地转换为bool,则根据需要选择在结果上应用.any.all。正如名称所示,.any将折叠数组为单个布尔值,指示是否有任何真实值;.all将检查所有值是否都是真实的。
    >>> (a == 4).all() # `a == 4`包含一些`False`值
    False
    >>> (a == 4).any() # 还有一些`True`值
    True
    >>> a.all() # 我们也可以直接检查`a``0`不是真实的,
    False
    >>> a.any() # 但`a`中的其他值是。
    True
    
    如果目标是逐元素将a转换为布尔值,则使用a.astype(bool)或(仅适用于数字输入)a != 0
  • 如果代码使用布尔逻辑(and/or/not),则使用位运算符(分别为&/|/~):
    >>> ((a % 2) != 0) & ((a % 3) != 0) # 注意是`&`,不是`and`
    array([False,  True, False, False, False,  True, False,  True, False])
    
    请注意,位运算符还提供对布尔输入的异或^的访问;这是逻辑运算符不支持(没有xor
  • 对于需要以相同方式组合的数组列表(或其他序列)(即内置函数allany所做的),请构建相应的(N+1)维数组,并沿轴0使用np.allnp.any
    >>> a = np.arange(100) # 用于更复杂计算的较大数组
    >>> sieves = [a % p for p in (2, 3, 5, 7)]
    >>> all(sieves) # 不起作用
    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) # 替代:
    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])
    

修复if语句

首先,请记住,如果代码有一个使用错误表达式的if语句(例如if (a % 3 == 0) or (a % 5 == 0):),那么两个条件都需要修复。

通常,显式转换为bool(使用上述的.all().any())将避免异常:

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

但它可能不会做想要的事情

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

如果目标是在每个满足条件的值上进行操作,那么自然的方法就是使用掩码作为掩码。例如,要在条件为真的情况下分配新值,只需使用计算出的掩码索引到原始数组中,并进行赋值:
>>> 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])

这种索引技术也适用于查找满足条件的值。在之前的sieves示例的基础上:

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

(练习:研究代码并理解为什么这个结果“不完全”是100以下的质数列表;然后修复它。)

使用Pandas

Pandas库依赖于Numpy,并在Numpy的数组类型之上实现了其DataFrame类型。所有相同的推理都适用,因此Pandas Series(和DataFrame)对象不能用作布尔值:请参见系列的真值是模棱两可的。使用a.empty、a.bool()、a.item()、a.any()或a.all()

解决问题的Pandas接口有点更复杂 - 最好通过阅读该Q&A来理解。该问题特别涵盖了Series,但逻辑通常也适用于DataFrames。如果您需要更具体的指导,请参见带有数据框的条件


0

这个错误信息也会在进行if语句比较时出现,其中涉及到一个数组和一个布尔值或整数。例如:

... code snippet ...

if dataset == bool:
    ....

... code snippet ...

此子句具有数据集作为数组,布尔值为“开放式门”... TrueFalse

如果该函数被包装在一个try语句中,您将会收到带有except Exception as error:的消息,但没有错误类型:

一个具有多个元素的数组的真实价值是模棱两可的,请使用a.any() 或 a.all()


0
通常情况下,当您比较两个单个数字时,Python正则代码可以正确地工作,但在数组内部,有一些数字(超过一个数字)应该并行处理。
例如,假设如下:
a = np.array([1, 2, 3])
b = np.array([2, 3, 4])

你想要检查 if b >= a: 吗?
因为,ab 不是单个数字,实际上你的意思是如果 b 的每个元素都大于相应的 a 中的数字,那么你应该使用以下命令:
if (b >= a).all():
 print("b is greater than a!")

-1
最简单的答案是使用"&"代替"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])

-1

对我而言,这个错误发生在测试中,出现错误的代码如下:

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

这段代码返回:

ValueError:具有多个元素的数组的真值不明确。使用a.any()或a.all()

因为我不能用列表断言 numpy 的 arrange 方法返回的对象。

解决方案是将 numpy 的 arrange 对象转换为列表,我的选择是使用 toList() 方法,如下所示:

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

这并没有正确理解问题。Numpy数组可以与列表进行比较,并且结果可以被断言。问题更微妙:首先,pixels列表为空,这不允许正确地广播。其次,如果pixels列表的大小/形状适合广播,则将pixelsself.pixels进行比较的结果将是一个Numpy数组,这将打破assertEqual内部的条件逻辑。但是,确实可以编写类似于self.assertTrue ((numpy.arange(1,10)==range(1,10)).all())的测试。 - Karl Knechtel
但是,总的来说,因为这是测试代码,所以展示的.toList方法更有意义。这将防止测试在被测试的代码返回错误形状的数组时引发异常。 - Karl Knechtel
但是,总的来说,因为这是测试代码,所以展示的.toList方法更有意义。这样做可以防止测试在被测试的代码返回形状错误的数组时引发异常。 - Karl Knechtel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接