将numpy数组转换为bool时看似不一致的行为

时间:2018-04-05 17:44:35

标签: python numpy

以下代码行看似不一致的行为背后的理由是什么?

import numpy as np

# standard list
print(bool([]))                # False - expected
print(bool([0]))               # True  - expected
print(bool([1]))               # True  - expected
print(bool([0,0]))             # True  - expected

# numpy arrays
print(bool(np.array([])))      # False - expected, deprecation warning: The
                               # truth value of an empty array is ambiguous...
print(bool(np.array([0])))     # False - unexpected, no warning
print(bool(np.array([1])))     # True  - unexpected, no warning 
print(bool(np.array([0,0])))   # ValueError: The truth value of an array 
                               # with more than one element is ambiguous...

我的观点中至少有两个不一致的地方:

  • 可以测试标准python容器的空虚bool(container)。为什么numpy数组不遵循这种模式? (bool(np.array([0]))收益False
  • 为什么在转换空的numpy数组或长度为>的数组时会出现异常/弃用警告。 1,但是当numpy数组只包含一个元素时可以这样做吗?

请注意,空numpy数组的弃用添加在numpy 1.11之间。和1.14。

1 个答案:

答案 0 :(得分:3)

对于第一个问题,原因是你对if np.array([1, 2]):想要做什么一点都不清楚。

这对if [1, 2]:来说不是问题,因为Python列表不做任何元素。你可以问的唯一问题是列表本身是否真实(非空)。

但是Numpy数组在元素方面做了所有事情,可能是元素方面的。请注意,这不是唯一的地方,甚至是最常见的地方,元素方式语义意味着数组与普通Python序列的工作方式不同。例如:

>>> [1, 2] * 3
[1, 2, 1, 2, 1, 2]
>>> np.array([1, 2]) * 3
array([3, 6])

而且,特别是对于这种情况,布尔数组是一个非常有用的东西,特别是因为你可以使用它们进行索引:

>>> arr = np.array([1, 2, 3, 4])
>>> arr > 2 # all values > 2
array([False, False,  True,  True])
>>> arr[arr > 2] = 2 # clamp the values at <= 2
>>> arr
array([1, 2, 2, 2])

一旦你有了这个特性,一个数组在布尔上下文中应该意味着什么变得模棱两可。通常,您需要bool数组。但是当你写if arr:时,你可能意味着任何一件事:

  • 为每个真正的元素做if的正文。 (将主体重写为由bool数组索引的arr上的表达式。)
  • if的身体是否有任何元素是真的。 (使用any。)
  • 如果所有元素都是真实的,请if的正文。 (使用any。)
  • 某个轴上的混合物 - 例如,对于任何元素都是真实的每行都要做主体。
  • 如果数组是非空的,则执行if的主体,就像普通的Python序列一样,但违反了数组通常的元素方式语义。 (明确检查空虚。)

所以,不是猜测,而是经常出错,numpy会给你一个错误并强迫你明白。

对于第二个问题,警告文本是否不适合您?单个元素的真值显然不是模棱两可的。

单元素数组 - 特别是0D数组 - 通常用作伪标量,因此能够做到这一点不仅无害,它有时也很有用。

相比之下,问“这个数组是空的”很少有用。列表是一个可变大小的东西,你通常通过一次添加一个元素,零次或多次(可能隐含在理解中)来构建,所以通常值得问你是否添加了零元素。但是数组是一个固定大小的东西,你通常会在代码中的某处明确指定大小。

这就是允许它的原因。为什么它在单个值上运行,而不是在阵列的大小上运行。

对于空阵列(你没有问过,但确实提出来了):在这里,你可能没有多少合理的东西,很难想到你可能意味着什么。这可能是为什么这是最近唯一更改的情况(参见issue 9583),而不是自Python添加__nonzero__以来的相同情况。