Python-NaN在集合中的唯一性

2024-10-06 07:01:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我刚刚偶然发现了Python的这个有趣的行为,它涉及NaN中的set

# Test 1
nan = float('nan')
things = [0, 1, 2, nan, 'a', 1, nan, 'a', 2, nan, nan]
unique = set(things)
print(unique)  # {0, 1, 2, nan, 'a'}

# Test 2
things = [0, 1, 2, float('nan'), 'a', 1, float('nan'), 'a', 2, float('nan'), float('nan')]
unique = set(things)
print(unique)  # {0, 1, 2, nan, nan, nan, nan, 'a'}

当然,同一个键nan在最后一个set中多次出现似乎很奇怪

我认为这是由于nan与自身不相等(由IEEE 754定义),再加上在向set添加对象时,对象是基于值相等之前的内存位置(id())进行比较的。然后,似乎每个float('nan')都会产生一个新的对象,而不是返回一些全局“单例”float(例如小整数)

  • 事实上,我刚刚发现this SO question描述了相同的行为,似乎证实了上述观点

问题:

  1. 这真的是你想要的行为吗
  2. 假设我从上面得到了第二个things。我如何计算实际唯一元素的数量?通常的len(set(things))显然不起作用。事实上,我可以使用import numpy as np; len(np.unique(things)),但我想知道这是否可以在不使用第三方库的情况下实现

附录

作为一个小附录,让我补充一点,类似的故事适用于dicts:

d = {float('nan'): 0, float('nan'): 1}
print(d)  # {nan: 0, nan: 1}

我的印象是NaNdict中作为键是完全不可能的,但只要存储对用作键的确切对象的引用,它实际上就可以工作:

nan0 = float('nan')
nan1 = float('nan')
d = {nan0: 0, nan1: 1}
d[float('nan')]  # KeyError
d[nan0]  # 0
d[nan1]  # 1

当然,这很有意思,但如果需要在现有的dict中存储额外的值,并且不关心要使用哪些键,当然每个新键不必已经在dict中,我可以看到这个技巧很有用。也就是说,可以使用float('nan')作为工厂来生成新的dict密钥的无止境供应,保证不会相互碰撞、现有或未来的密钥


Tags: 对象testlennp密钥nanfloatdict
1条回答
网友
1楼 · 发布于 2024-10-06 07:01:50

float()所需的行为是返回float(类)的实例。你说得对,“南”并不等于它本身。因此,float(1) == float(1)float('nan') != float('nan')

为了得到一个唯一的集合,我建议像在测试1中那样建立一个nan常量。如果这不适合你,你可以选择import math; math.isnan(float('nan'))。迭代列表(或集合)并删除元素。 newlist = [ x for x in things if not math.isnan(x) ]

你可能会想:不,我删除所有的南。如果以前有一个呢

import math

things = [0, 1, 2, float('nan'), 'a', 1, float('nan'), 'a', 2, float('nan'), float('nan')]
nan = float('nan')
length = len(things)
newlist = [ x for x in things if not isinstance(x, str) and not math.isnan(x) ]
if len(newlist) != length:
    newlist.append(nan)  # or however you'd like to handle it
unique = set(newlist)
print(unique)

{0, 1, 2, nan}

相关问题 更多 >