numpy NaN并不总是可以识别

时间:2018-09-01 00:17:04

标签: python numpy nan

这使我感到困惑:

```
a=np.array([1,2,np.nan,3])    # an array with a nan
print(np.isnan(a)[2])         # it truly is a nan
print(a[2])                   # it quacks like a nan
print(np.nan is np.nan)       # nan's can be compared
print(a[2] is np.nan)         # But then, this isn't a nan after all!!??

>>> True
>>> nan
>>> True
>>> False
```

我知道我们不允许将nan与==进行比较,但是应该允许与is进行比较?毕竟,将nan与自身进行比较时能起作用吗?

感谢您对这里发生的情况的提示。

3 个答案:

答案 0 :(得分:5)

与其说是Python is运算符,还不如说是对数组元素进行索引或拆箱的问题。

In [363]: a=np.array([1,2,np.nan,3])
In [364]: a[2]
Out[364]: nan
In [365]: type(a[2])
Out[365]: numpy.float64
In [366]: a[2] is a[2]
Out[366]: False

a[2]不会简单地返回nan。它返回一个值为np.float64的{​​{1}}对象。另一个np.nan将产生另一个a[2]对象。在np.float64的意义上,两个这样的对象不匹配。对于任何数组元素,不仅是is值,都是如此。

由于nan==不起作用,因此我们不得不使用nan函数。

np.isnan是唯一的np.nan对象(在此会话中),但是float未设置为该对象。

如果将数组定义为对象类型:

a[2]

此处In [376]: b=np.array([1,2,np.nan,3], object) In [377]: b[2] is np.nan Out[377]: True 为True-因为is包含指向内存中已经存在的对象的指针,包括b对象。像这样构造的列表也是如此。

答案 1 :(得分:3)

首先,至少在NumPy 1.15中,np.nan恰好是一个特殊的单例,这意味着每当NumPy必须为您提供类型为float的NaN值时,它都会尝试为您提供相同的{值{1}}。

但这在任何地方都没有记录,或者保证在各个版本中都是正确的。

作为实现细节,它适合于可能不是单例的较大值类别。

通常,如果您的代码依赖于一个不变类型相同或不相同的两个相等值,那么您的代码是错误的。

以下是默认版本的CPython 3.7的一些示例:

np.nan

可以学习使所有这些内容都以这种方式出现的所有规则,但是它们都可以在不同的Python实现,版本3.8甚至使用自定义配置选项。因此,永远不要使用>>> a, b = 200, 201 >>> a is b-1 True >>> a, b = 300, 301 >>> a is b-1 False >>> 301-1 is 300 True >>> math.nan is math.nan True >>> float('nan') is math.nan False >>> float('nan') is float('nan') False 1math.nannp.nan'';仅将其用于专门记录为单例的对象(例如is或您自己类型的实例)。


第二,当您索引一个numpy数组时,它必须通过构造一个标量来“拆箱”该值,该标量的类型适合于该数组的None。对于dtype数组,其构造的标量值为dtype=float64

因此,保证np.float64a[2]

但是np.float64不是np.nan,而是np.float64

因此,当您要求float时,NumPy不可能给您np.nan。相反,它为您提供了一个NaN值的a[2]


好的,所以这就是np.float64始终为False的原因。但是为什么a[2] is np.nan通常也为假?

如上所述,NumPy会在需要给您a[2] is a[2] NaN时尝试给您np.nan。但是,至少在1.15中,它没有任何特殊的单例值可以在需要给您float NaN时提供。完全没有理由,但是没有人愿意编写这样的代码,因为对任何正确编写的应用程序来说,这都不重要。

因此,每次将np.float64中的值拆箱为标量a[2]时,它都会为您提供一个新的NaN值np.float64

但是为什么这与np.float64不同?嗯,行之有效的原因是允许编译器以相等的值折叠已知的不可变类型的常量,对于简单的情况,CPython会在每个编译单元中做到这一点。但是两个NaN值不相等。 NaN值甚至不等于其自身。因此,它不能恒定折叠。

(如果您想创建一个具有int dtype的数组并将小值存储在其中,并检查它们是否合并到small-int单例中,请尝试一下。)


当然,这就是301-1 is 300首先存在的原因。您无法使用相等性测试NaN(因为NaN值甚至不等于任何值,甚至它们本身),也无法使用身份测试NaN(出于上述所有原因),因此您需要一个函数来测试他们。

答案 2 :(得分:0)

检查一下:

In [1]: type(a[2])
Out[1]: numpy.float64
In [2]: type(numpy.nan)
Out[2]: float

还有

In [3]: id(a[2])
Out[3]: 4419858888
In [4]: id(np.nan)
Out[4]: 4326468200

他们不一样