numpy NaN不总是被识别

rqcrx0a6  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(133)

这使我感到困惑:

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应该被允许吗?毕竟,它的工作时,比较南本身?

stszievb

stszievb1#

这不是关于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对象,其值为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

这里is是True -因为b包含指向内存中已经存在的对象的指针,包括np.nan对象。对于这样构造的列表也是如此。

fcy6dtqo

fcy6dtqo2#

首先,至少在NumPy 1.15中,np.nan恰好是一个特殊的单例,这意味着每当NumPy必须给予一个类型为float的NaN值时,它都会尝试给你给予相同的np.nan值。
但这并没有在任何地方被记录下来,也不能保证在不同版本中都是如此。
作为实现细节,这适合更大的值类,这些值可能是也可能不是单例。
作为一般规则,如果您的代码依赖于不可变类型的两个相等值相同或不相同,则代码是错误的。
以下是CPython 3.7默认版本的一些示例:

>>> 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

你可以学习所有这些规则,但是它们都可以在不同的Python实现中改变,或者在3.8版本中,甚至在3.7版本中构建自定义配置选项。因此,永远不要将1math.nannp.nan''is一起使用;仅将其用于专门记录为单例的对象(例如None-当然,或者您自己类型的示例)。
其次,当你索引一个numpy数组时,它必须通过构造一个标量来“取消装箱”值,该标量的类型适合数组的dtype。对于dtype=float64数组,它构造的标量值是np.float64
因此,a[2]保证是np.float64
np.nan不是np.float64,而是float
所以,当你要求a[2]时,NumPy不可能给予np.nan。相反,它给你一个值为NaN的np.float64
这就是为什么a[2] is np.nan总是False。但是为什么a[2] is a[2]通常也是false?
正如我上面提到的,NumPy试图在需要给你一个float NaN时给予你一个np.nan。但是,至少在1.15中,当它需要给予np.float64 NaN时,它没有提供任何特殊的单例值。没有理由它不能,但是没有人费心去写这样的代码,因为对于任何正确编写的应用程序来说,这两种方式都不重要。
因此,每次将a[2]中的值拆箱为标量np.float64时,它都会给您一个新的NaN值np.float64
但为什么这与301-1 is 300不同呢?好吧,这样做的原因是允许编译器将已知的不可变类型的常量与相等的值合并,CPython在每个编译单元中对简单的情况做了同样的事情。但两个NaN值并不相等; NaN值甚至不等于它本身。所以,它不可能是恒定折叠的。
(If你想知道如果你创建一个int dtype数组,并在其中存储小值,并检查它们是否合并到small-int singletons中,会发生什么-试试看。
当然,这就是为什么isnan首先存在。你不能用相等性测试NaN(因为NaN的值不等于任何东西,甚至不等于它们自己),你不能用恒等性测试NaN(出于上面描述的所有原因),所以你需要一个函数来测试它们。

e3bfsja2

e3bfsja23#

看看这个:

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

他们不一样

相关问题