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

Hom*_*lli 190 python numpy

我刚刚在代码中发现了一个逻辑错误,导致了各种各样的问题.我无意中做了一个按位AND而不是逻辑AND.

我更改了代码:

r = mlab.csv2rec(datafile, delimiter=',', names=COL_HEADERS)
mask = ((r["dt"] >= startdate) & (r["dt"] <= enddate))
selected = r[mask]
Run Code Online (Sandbox Code Playgroud)

至:

r = mlab.csv2rec(datafile, delimiter=',', names=COL_HEADERS)
mask = ((r["dt"] >= startdate) and (r["dt"] <= enddate))
selected = r[mask]
Run Code Online (Sandbox Code Playgroud)

令我惊讶的是,我收到了相当神秘的错误消息:

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

为什么在使用按位操作时没有发出类似的错误 - 我该如何解决这个问题?

unu*_*tbu 146

r是一个numpy(rec)数组.所以r["dt"] >= startdate也是一个(布尔)数组.对于numpy数组,该&操作返回elementwise和两个布尔数组.

该NumPy的开发者觉得有没有人通常理解的方式来评估布尔上下文中的数组:这可能意味着True,如果任何元素 True,或者它可能意味着True,如果所有元素True,或者True如果该数组有非0的长度,只是说出三种可能性.

由于不同的用户可能有不同的需求和不同的假设,NumPy开发人员拒绝猜测,而是在尝试在布尔上下文中评估数组时决定引发ValueError.应用于and两个numpy数组会导致在布尔上下文中计算两个数组(通过__bool__在Python3或__nonzero__Python2中调用).

你的原始代码

mask = ((r["dt"] >= startdate) & (r["dt"] <= enddate))
selected = r[mask]
Run Code Online (Sandbox Code Playgroud)

看起来不错.但是,如果你想要and,那么而不是a and b使用(a-b).any()(a-b).all().

  • 你是对的。原始代码是正确的。该错误似乎位于代码中的其他地方。 (3认同)
  • 很好的解释。然而,这意味着 NumPy 非常低效:它完全评估两个布尔数组,而有效的实现将在一个循环内评估 cond1(i)&amp;&amp;cond2(i) ,并跳过 cond2 除非 cond1 为真。 (2认同)
  • @unutbu我认为你的第二段错了.这不是关于`和`.`和`是Python的,而不是Numpy的.为了处理它,Python要求数组的`__bool__`(Py3)或`__nonzero__`(Py2)方法.*这就是NumPy开发人员认为的含糊不清的东西.没有? (2认同)

Yeq*_*ang 40

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

或者,我找到了另一种解决方案,它可以完美地满足我所需的功能(/sf/ask/885323001/ -when-trying-t).

不使用上面建议的代码,只需使用一个numpy.logical_and(a,b)就行了.在这里,您可能希望将代码重写为

(a-b).any()


MSe*_*ert 24

异常的原因是and隐式调用bool.首先在左操作数和(如果左操作数是True)然后在右操作数上.所以x and y相当于bool(x) and bool(y).

但是boolon numpy.ndarray(如果它包含多个元素)将抛出您看到的异常:

>>> 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()
Run Code Online (Sandbox Code Playgroud)

bool()通话中隐含的and,而且在if,while,or,所以任何的下面的示例也将失败:

>>> 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()
Run Code Online (Sandbox Code Playgroud)

Python中有更多隐藏bool调用的函数和语句,例如2 < x < 10只是另一种编写方式2 < x and x < 10.而and将调用bool:bool(2 < x) and bool(x < 10).

元素方面等价物and将是np.logical_and功能,同样可以使用np.logical_or等同的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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

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


Kar*_*tel 5

原因

每当代码尝试将 Numpy 数组转换为布尔值(即检查其真值,如错误消息中所述)时,都会发生此错误。对于给定的 array a,可能会发生这种情况:

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])
Run Code Online (Sandbox Code Playgroud)

然后,a == b 并不意味着“给出一个TrueFalse答案:a等于b?”,就像通常的意思一样。相反,它将逐个元素比较值,并计算出这些比较的布尔结果数组:

>>> a == b
array([ True,  True,  True, False, False, False, False, False, False])
Run Code Online (Sandbox Code Playgroud)

换句话说,它执行与数学运算符(如)相同类型的广播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])
Run Code Online (Sandbox Code Playgroud)

修复表达式

  • 如果代码显式转换为bool,请根据需要选择应用.any.all应用于结果。正如名字所暗示的那样,.any将数组折叠为单个布尔值,指示任何值是否为真;.all将检查所有值是否为真。
    >>> (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
    
    Run Code Online (Sandbox Code Playgroud) 如果目标是按a元素转换为布尔值,请使用a.astype(bool), or (仅适用于数字输入)a != 0
  • 如果代码使用布尔逻辑 ( // and) ornot使用按位运算符( &//|~
    >>> ((a % 2) != 0) & ((a % 3) != 0) # N.B. `&`, not `and`
    array([False,  True, False, False, False,  True, False,  True, False])
    
    Run Code Online (Sandbox Code Playgroud)^请注意,按位运算符还提供对布尔输入的异或的 访问;逻辑运算符不支持此功能(没有xor
  • 对于需要以相同方式组合的数组列表(或其他序列)(即内置函数all和 的any作用),请改为构建相应的 (N+1) 维数组,并沿轴 0使用np.allnp.any
    >>> 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])
    
    Run Code Online (Sandbox Code Playgroud)

定影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
Run Code Online (Sandbox Code Playgroud)

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

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

如果目标是对条件为 true 的每个值进行操作,那么自然的方法就是使用结果数组作为掩码。例如,要在条件为真的任何地方分配新值,只需使用计算出的掩码索引到原始数组,然后分配:

>>> 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])
Run Code Online (Sandbox Code Playgroud)

此索引技术对于查找满足条件的值也很有用。基于前面的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])
Run Code Online (Sandbox Code Playgroud)

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

使用熊猫

Pandas 库将 Numpy 作为依赖项,并DataFrame在 Numpy 的数组类型之上实现其类型。所有相同的推理都适用,因此 Pandas Series(和DataFrame)对象不能用作 boolean:请参阅系列的真值不明确。使用 a.empty、a.bool()、a.item()、a.any() 或 a.all()

用于解决该问题的 Pandas 界面有点复杂 - 最好通过阅读问答来理解。该问题具体涉及系列,但逻辑通常也适用于数据帧。另请参阅数据帧的 If 条件,以获取将条件逻辑应用于数据帧的更具体指导。