具有一个falsey元素的numpy数组的真值似乎取决于dtype

时间:2015-05-05 03:44:33

标签: python arrays numpy types truthiness

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

4 个答案:

答案 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 +将看到Falsebool(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的结果是如此一致。