为何Python的max()函数在字典含NaN时换位置输出不同?
问题:使用max()获取含NaN字典的最大值键时出现意外结果
假设有如下字典:
dictionary = {'a':3, 'b':4, 'c':float('NaN')}
用max()函数获取值最大的键:
key_maxvalue = max(dictionary, key=dictionary.get) print(key_maxvalue)
Python输出结果为:
b
但交换键'a'和'c'的值后:
dictionary = {'a':float('NaN'), 'b':4, 'c':3} key_maxvalue = max(dictionary, key=dictionary.get) print(key_maxvalue)
得到意外结果:
a
明明键'b'的值仍是字典中的最大值,为何值的位置变化会改变max()的输出?如何避免这种意外情况?
原因分析
问题出在float('NaN')的比较特性上:在Python中,NaN(非数字)和任何值做比较(包括和自身)都会返回False。而max()函数的工作逻辑是逐个遍历元素,保留当前找到的“最大值”——当遇到无法确定谁更大的情况(比如当前最大值是NaN,和后面的4比较时,4 > NaN为False;或者当前最大值是4,和NaN比较时NaN > 4也为False),max()会保留先出现的那个元素。
另外,Python 3.7及以上版本的字典会保留键的插入顺序,所以两次遍历字典的顺序都是a→b→c:
- 第一个例子:初始最大值是a的3,遇到b的4时,
4>3为True,更新最大值为b;再遇到c的NaN,NaN>4为False,不更新,最终返回b。 - 第二个例子:初始最大值是a的NaN,遇到b的4时,
4>NaN为False,不更新最大值;再遇到c的3,3>NaN也为False,所以最终保留第一个出现的a。
避免方法
方法1:过滤掉字典中的NaN值
直接把值为NaN的键值对从字典中剔除后再求最大值:
import math dictionary = {'a':float('NaN'), 'b':4, 'c':3} # 过滤掉NaN对应的键值对 filtered_dict = {k: v for k, v in dictionary.items() if not (isinstance(v, float) and math.isnan(v))} key_maxvalue = max(filtered_dict, key=filtered_dict.get) print(key_maxvalue) # 输出b
方法2:自定义key函数,将NaN视为极小值
把NaN转换成负无穷,这样它就会被当作最小的元素处理:
import math dictionary = {'a':float('NaN'), 'b':4, 'c':3} # 自定义key函数,把NaN替换成负无穷 key_maxvalue = max(dictionary, key=lambda k: dictionary[k] if not (isinstance(dictionary[k], float) and math.isnan(dictionary[k])) else -float('inf')) print(key_maxvalue) # 输出b
内容的提问来源于stack exchange,提问作者NigelBlainey
相关产品推荐
相关产品推荐

