为什么在numpy中`NaN`被认为比`-np.inf`“更小”?

6
< p >为什么在涉及np.minnp.argmin的任何比较中,NaN被认为小于-np.inf?< /p >
import numpy as np
In [73]: m = np.array([np.nan, 1., 0., -np.inf])
In [74]: n = np.array([-np.inf, 1., 0., np.nan])

# Huh??
In [75]: np.min(m)
Out[75]: nan
In [76]: np.min(n)
Out[76]: nan

# Same for np.argmin
In [77]: np.argmin(m)
Out[77]: 0
In [78]: np.argmin(n)
Out[78]: 3

# Its all false!
In [79]: np.nan < -np.inf
Out[79]: False

In [80]: np.nan > -np.inf
Out[80]: False

# OK, that seems to fix it, but its not necessarily elegant
In [81]: np.nanmin(m)
Out[81]: -inf

In [82]: np.nanargmin(m)
Out[82]: 3

我猜测这可能是与 NaN 值比较时返回 False 的任何副作用有关,然而当你的数组中“偶尔”出现 NaN 值时,这会导致一些相当烦人的影响。使用 np.nanminnp.nanargmin 有些感觉像是一个快速修复,被随意地添加到现有行为之上。
除了在 docs 中的这个注释:“NaN 值被传播,也就是说,如果至少有一个项目是 NaN,则相应的最小值也将是 NaN。要忽略 NaN 值(MATLAB 行为),请使用 nanmin。”,我没有找到任何解释该行为背后原理的东西。这是想要的行为还是特定内部表示 NaN 值的副作用?为什么?

2
你可能会发现这个答案有帮助:https://dev59.com/rnI_5IYBdhLWcg3wAeLl - Till Hoffmann
2
这是一种逻辑行为:如果某物不是一个数字,就不能与任何是数字的东西进行比较,因此任何比较都会返回false。更重要的是 - 人们可以认为在大多数情况下,“NaN”不是某些操作的期望结果,因此它应该像任何其他异常一样传播。 - Dunno
1
请参见最近的回答,https://dev59.com/vVgR5IYBdhLWcg3wbs6X#41324751。在这个和相关函数中,“nan”被明确编码为“最大化”。 - hpaulj
2
你的前提是错误的。np.nan与任何其他数字(包括它本身)相比都是无序的,不会引发错误。虽然它小于某些数,但也大于并且不等于任何其他数字 - 包括它本身。这是IEEE 754的一部分。实现细节是NaN是否被视为信号或静默处理。Numpy是静默处理的。 - dawg
2个回答

6
如@Dunno在评论中提到的那样,将NaN与数字进行比较并没有太多意义,因此这种行为可能是可以接受的。IEEE 754标准对于将NaN与数字进行比较有以下规定:

四种互斥的关系是可能的:小于、等于、大于和无序。最后一种情况出现在至少一个操作数为NaN时。每个NaN都应该与任何东西(包括自身)无序比较

根据标准,这样做是可以接受的:
# Its all false!
In [79]: np.nan < -np.inf
Out[79]: False

会导致“无序”结果,因此它并不属于“小于”关系。

这个解释的问题在于Python中没有“无序”类别,所以np.nan < 5np.nan > 5np.nan == 5不能都评估为False,但它们确实是。如果您开始在常规Python语句中使用np.nan,这会导致各种麻烦。例如:min([5, np.nan]) -> 5,而min([np.nan, 5]) -> np.nan。我想知道为什么np.nan支持比较,而不像None一样引发TypeError。 - Bill

0

所以,你可能已经知道了:

"inf"代表无穷大——一个比任何其他值都大的值。因此,"-inf"比任何其他值都小,请记住这个值是一个数字。

"nan"代表不是一个数字。

因此,如果根据你之前声明的数组"m,n",并且当你对它们中的任何一个执行"np.min()"时,实际上发生的是一旦遇到"nan",其他元素就不会被检查或比较,然后执行以下语句并返回该值:

 if (@isnan@(mp)) { /* nan encountered; it's maximal */ return 0; } 

因此,"nan"作为函数的答案返回!

检查此代码,它遇到第一个"nan"时立即返回,并在相应的函数中返回其位置。

    In [1]: import numpy as np

    In [2]: m = np.array([1., 0., -np.inf, np.nan])

    In [3]: n = np.array([np.nan, 1., np.nan, 0.])

    In [4]: np.argmin(m)
    Out[4]: 3

    In [5]: np.argmin(n)
    Out[5]: 0

像“np.nan < -np.inf”和“np.nan > -np.inf”这样的操作返回“False”,因为“nan”无法与任何数字“-inf”进行比较,上述操作的每种情况中的“False”不是比较的答案,而是由于一种异常或上述代码的执行导致的逻辑错误,因为尽管是无穷大但它不能与关于数字的事物“无”的东西进行比较!

因此,如果您从数组中删除所有“nan”,然后使用“np.nanmin()”计算最小值,则输出将按预期为“-inf”,这里不会出现问题!

因此,“NaN”不小于“inf”或“-inf”,因为实际上它与任何这些或任何数字都无法进行比较,它将在与任何数字进行比较时返回“False”!!

    In [1]: np.nan < 1
    Out[1]: False

    In [2]: np.nan > 1
    Out[2]: False

等等……

希望能有所帮助!!


1
从技术上讲,numpy c代码不会引发异常,它只是返回。 if (@isnan@(mp)) { /* nan encountered; it's maximal */ return 0; }. https://dev59.com/vVgR5IYBdhLWcg3wbs6X#41324751 - hpaulj
是的,我所说的引发异常只是指一旦遇到“ nan”,正常执行就会停止。我之前不知道这点,谢谢 @hpaulj - Rahul Singh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接