Python:在存在nan的情况下,sort函数会中断

max*_*max 29 python sorting math nan

sorted([2, float('nan'), 1]) 回报 [2, nan, 1]

(至少在Activestate Python 3.1实现上.)

我理解这nan是一个奇怪的对象,所以如果它出现在排序结果中的随机位置,我不会感到惊讶.但它也混淆了容器中非纳米数的排序,这实在是出乎意料.

我问了一个相关的问题有关max,并根据我理解为什么sort是这样工作的.但是,这应该被视为一个错误吗?

文档只是说"返回一个新的排序列表[...]"而没有指定任何细节.

编辑:我现在同意这不违反IEEE标准.但是,我认为这是任何常识观点的错误.即使微软经常承认自己的错误,也已经认识到这个错误,并将其修复为最新版本:http://connect.microsoft.com/VisualStudio/feedback/details/363379/bug- in-list-double-sort-in-list-which-contains-double-nan.

无论如何,我最终关注@ khachik的回答:

sorted(list_, key = lambda x : float('-inf') if math.isnan(x) else x)
Run Code Online (Sandbox Code Playgroud)

我怀疑它与默认情况下的语言相比会导致性能下降,但至少它可行(除非我介绍的任何错误).

小智 13

以前的答案很有用,但可能不清楚问题的根源.

在任何语言中,sort都应用由比较函数或以其他方式定义的给定排序,而不是输入值的域.例如,operator <,当且仅当小于定义输入值的合适排序时,小于,也可以全部使用.

但是对于浮点值并且小于:"NaN是无序的:它不等于,大于或小于任何东西,包括它自己." (来自GNU C手册的清晰散文,但适用于所有IEEE754基于现代的浮点)

所以可能的解决方案是:

  1. 首先删除NaNs,通过<(或正在使用的其他排序函数)很好地定义输入域
  2. 定义定义NaN排序的自定义比较函数(aka谓词),例如小于任何数字或大于任何数字.

这两种方法都可以用于任何语言.

实际上,考虑到python,如果你不关心最快的性能,或者如果在上下文中删除NaN是一种理想的行为,我宁愿删除NaN.

否则你可以在旧的python版本中通过"cmp"使用合适的谓词函数,或者通过这个和 functools.cmp_to_key().后者比首先去除NaN更加尴尬.和关怀将被要求避免糟糕的表现,定义这个谓词功能时.

  • IEEE 754 要求 max(NaN, 1) 返回 1。如果 Python 遵循该标准就好了,但它没有。如果它遵循自己的规则,它至少可以有一些合理的规则,而不是随机的不稳定行为。 (2认同)

小智 7

问题是如果列表包含NAN,则没有正确的顺序,因为如果a1 <= a2 <= a3 <= ... <= an,则对序列a1,a2,a3,...,a进行排序.如果这些值中的任何一个是NAN,则排序的属性会中断,因为对于所有a,<= NAN和NAN <= a都是假的.


x0s*_*x0s 7

假设您想保留 NaN 并将它们排序为最低的“值”,这里是一种适用于非唯一 nan唯一 numpy nan数字非数字对象的解决方法:

def is_nan(x):
    return (x is np.nan or x != x)

list_ = [2, float('nan'), 'z', 1, 'a', np.nan, 4, float('nan')]
sorted(list_, key = lambda x : float('-inf') if is_nan(x) else x)
# [nan, nan, nan, 1, 2, 4, 'a', 'z']
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个答案。我不明白为什么 nan 不能定义为 -inf 或 inf。我从数学上理解如何无法比较 0 和 1/0,但这不应该妨碍合理的语言结构来处理这个问题。 (2认同)

kha*_*hik 5

我不确定该错误,但解决方法可能如下:

sorted(
    (2, 1, float('nan')),
    lambda x,y: x is float('nan') and -1 
                or (y is float('nan') and 1
                or cmp(x,y)))
Run Code Online (Sandbox Code Playgroud)

这导致:

('nan', 1, 2)
Run Code Online (Sandbox Code Playgroud)

或者nan在排序之前删除s或其他任何东西.