import numpy as np
a = np.array([0])
b = np.array([None])
c = np.array([''])
d = np.array([' '])
为什么我们应该有这种不一致的地方:
>>> bool(a)
False
>>> bool(b)
False
>>> bool(c)
True
>>> bool(d)
False
答案 0 :(得分:8)
我很确定答案是Scalars中所解释的:
数组标量与ndarrays具有相同的属性和方法。 [1]这允许人们将阵列中的项目部分地放在与阵列相同的基础上,平滑混合标量和数组操作时产生的粗糙边缘。
因此,如果在标量上调用bool
是可以接受的,那么在形状bool
的数组上调用(1,)
必须是可接受的,因为它们尽可能地在同样的事情。
而且,虽然在我所知道的文档中并没有直接说出来,但从设计中可以明显看出NumPy的标量应该像原生Python对象一样。
所以,这解释了为什么np.array([0])
是假的而不是真实的,这是你最初感到惊讶的。
所以,这解释了基础知识。但是案例c
的细节呢?
首先,请注意您的数组np.array([''])
不是一个Python object
的数组,而是一个长度为1的NumPy <U1
以空字符结尾的字符串的数组。固定长度-string值与Python字符串没有相同的真实性规则 - 它们实际上不能;对于固定长度字符串类型,“false if empty”没有任何意义,因为它们永远不会为空。你可以争论NumPy是否应该以这种方式设计,但它显然确实遵循该规则,我认为相反的规则在这里不会混淆,只是不同。
但是字符串似乎还有其他奇怪的东西。考虑一下:
>>> np.array(['a', 'b']) != 0
True
那不是将<U2
字符串的元素比较与0和返回array([True, True])
(从np.array(['a', 'b'], dtype=object)
获得),它正在进行数组范围的比较并决定没有字符串数组等于0,这看起来很奇怪...我不确定这是否值得在这里单独回答,甚至是一个完全独立的问题,但我很确定我不会成为那个写答案的人,因为我不知道这里发生了什么。 :)
除了形状(1,)
的数组之外,形状()
的数组的处理方式相同,但其他任何内容都是ValueError
,否则很容易误用数组and
{1}}和NumPy无法自动转换为元素操作的其他Python运算符。
我个人认为与其他数组保持一致比在这里与标量符合更有用 - 换句话说,只需提升ValueError
。我还认为,如果与标量一致在这里很重要,那么最好与未装箱的Python值保持一致。换句话说,如果允许bool(array([v]))
和bool(array(v))
,则它们应始终返回与bool(v)
完全相同的内容,即使这与np.nonzero
不一致。但我可以用另一种方式看到这个论点。
答案 1 :(得分:7)
对于具有一个元素的数组,数组的真值由该元素的真值确定。
要点的是np.array([''])
不包含一个空Python字符串的数组。创建此数组以保存每个字符串恰好一个字节的字符串,并且NumPy填充的字符串与空字符相比太短。这意味着该数组等于np.array(['\0'])
。
在这方面,NumPy与Python一致,将bool('\0')
评估为True
。
实际上,NumPy数组中唯一的False
字符串是不包含任何非空白字符的字符串('\0'
不是空格字符)。
此布尔评估的详细信息如下所示。
导航NumPy的迷宫源代码并不总是很容易,但是我们可以找到管理不同数据类型中的值如何映射到arraytypes.c.src文件中的布尔值的代码。这将解释如何确定bool(a)
,bool(b)
,bool(c)
和bool(d)
。
在我们找到该文件中的代码之前,我们可以看到在NumPy数组上调用bool()
会调用内部_array_nonzero()
函数。如果数组为空,我们得到False
。如果有两个或更多元素,我们会收到错误。但是如果数组完全一个元素,我们就会遇到这样的一行:
return PyArray_DESCR(mp)->f->nonzero(PyArray_DATA(mp), mp);
现在,PyArray_DESCR
是一个包含数组各种属性的结构。 f
是指向另一个包含数组nonzero
函数的结构PyArray_ArrFuncs
的指针。换句话说,NumPy将调用数组自己的特殊nonzero
函数来检查该元素的布尔值。
确定元素是否为非零显然取决于元素的数据类型。实现特定于类型的非零函数的代码可以在&#34;非零&#34;中找到。 arraytypes.c.src文件的一部分。
正如我们所期望的那样,浮点数,整数和复数是False
如果它们是equal with zero。这解释了bool(a)
。对于对象数组,None
同样会被评估为False
,因为NumPy只是calls the PyObject_IsTrue
function。这解释了bool(b)
。
要了解bool(c)
和bool(d)
的结果,我们会看到字符串类型数组的nonzero
函数已映射到STRING_nonzero
函数:
static npy_bool
STRING_nonzero (char *ip, PyArrayObject *ap)
{
int len = PyArray_DESCR(ap)->elsize; // size of dtype (not string length)
int i;
npy_bool nonz = NPY_FALSE;
for (i = 0; i < len; i++) {
if (!Py_STRING_ISSPACE(*ip)) { // if it isn't whitespace, it's True
nonz = NPY_TRUE;
break;
}
ip++;
}
return nonz;
}
(unicode案例或多或少是相同的想法。)
因此,在具有字符串或unicode数据类型的数组中,如果字符串仅包含空格字符,则该字符串仅为False
:
>>> bool(np.array([' ']))
False
对于问题中的数组c
,有一个真正的空字符\0
填充看似空的字符串:
>>> np.array(['']) == np.array(['\0'])
array([ True], dtype=bool)
STRING_nonzero
函数会看到此非空格字符,因此bool(c)
为True
。
正如本答案开头所述,这与Python对包含单个空字符的字符串的评估一致:bool('\0')
也是True
。
更新:Wim has fixed the behaviour在NumPy主分支中详述,评估为{{ 1}}。这意味着NumPy 1.10 +将看到False
为bool(np.array(['']))
,这更符合Python对&#34;空&#34;的处理。字符串。
答案 2 :(得分:3)
现在是fixed in master。
我认为这是一个错误,numpy
开发者同意,所以今天早些时候patch was merged。我们应该在即将发布的1.10版本中看到新的行为。
答案 3 :(得分:2)
Numpy似乎遵循与builtin python相同的演员 **,在此上下文中it seems to be because of which return true for calls to nonzero
。显然也可以使用len
,但是在这里,这些数组都不是空的(长度为0
) - 因此不直接相关。请注意,根据这些规则调用bool([False])
也会返回True
。
a = np.array([0])
b = np.array([None])
c = np.array([''])
>>> nonzero(a)
(array([], dtype=int64),)
>>> nonzero(b)
(array([], dtype=int64),)
>>> nonzero(c)
(array([0]),)
This also seems consistent with the more enumerative description of bool
casting ---您的示例都已明确讨论过。
有趣的是,字符串数组似乎存在系统不同的行为,例如
>>> a.astype(bool)
array([False], dtype=bool)
>>> b.astype(bool)
array([False], dtype=bool)
>>> c.astype(bool)
ERROR: ValueError: invalid literal for int() with base 10: ''
I think, when numpy converts something into a bool it uses the PyArray_BoolConverter
function反过来只调用PyObject_IsTrue
函数---即内置python使用的完全相同的函数,这就是为什么numpy
的结果是如此一致。