如何使用字典将 numpy 中的 NaN 映射到值?

How to map NaN in numpy to values using dictionary?

我有一本将数值映射到标签的字典。我用它为给定的 numpy 数组创建标签。该数组最初包含所有 NaN 值,并且一些元素被填充为非 NaN 值。我想将 NaN 值映射到标签。但是,这失败了:

import numpy as np
# make array with all NaNs
a = np.ones(5) * np.nan
# populate some of it with non-NaN values
a[0] = 1
a[1] = 2
l = {"1": "one", 2: "two", np.nan: "NA"}
for k in l:
  if k == np.nan:
    print l[k]
# this returns false
print (np.nan in a)

这是因为数组的初始化吗?为什么 np.nan 不等于 a 中的 NaN 值?

我正在尝试获取以下的工作版本:

print l[a[3]]  # should print "NA", not raise keyerror

关于 NaN 的一件有趣的事情是 IEEE 指定 NaN 不等于 任何东西(包括它自己)。 Numpy 和 python 通常遵循此规则。

>>> NaN = float('nan')
>>> NaN == NaN
False
>>> import numpy as np
>>> np.nan == np.nan
False

这应该可以解释为什么您的 print l['k'] 语句永远不会打印以及为什么 np.nan in a 不会 return True.

一种解决方法可能是:

numpy.isnan(a).any()  # Check if any element in `a` is `nan`.

如果我对您的评论理解正确,则以下代码片段可以更恰当地说明问题:

>>> import numpy as np
>>> d = {np.nan: 'foo'}
>>> d[np.nan]
'foo'
>>> a = np.array([np.nan])
>>> d[a[0]]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: nan

不幸的是,由于 NaN 的疯狂属性,您在这里无能为力。 numpy 数组本质上是 C-Arrays 保存浮点数。当你有一个 free-floating np.nan 时,它有一个永远不会改变的 ID(内存地址),所以 python 可以通过指针比较来锁定它。这就是为什么第一位与上面的字典一起工作的原因。

不幸的是,当您将 NaN 放入数组时,它会用 NaN 填充数组中的值。在这种情况下,该元素的 ID 是相对于数组中第一个元素的位置的 - 因此 python 无法判断此 NaN 是否与您用来构造的相同数组(因为它不是)。由于 NaN 的属性,现在 ID 比较失败并且相等比较失败,你有点倒霉。

至于你的值 -> 标签转换,你可能可以使用 numpy 内置功能:

label_array = np.empty(a.shape, dtype='|S3')
label_array[np.isnan(a)] = 'NA'
label_array[a == 1] = 'one'
label_array[a == 2] = 'two'

对于中等大小的数组,应该足够快...

请注意,只有当您直接将个和两个放入 a 时,这才有效——如果您已经完成了一些浮点数学运算来计算它们,则无效。例如a[n] = 5. / 2.5 因为精度错误可能会使您得到的数字非常接近 2 而并不完全等于 2...

NaN 未通过 any 比较检查,包括针对自身。即

NaN == NaN

为假。

因此,您的声明

if k == np.nan:

must return False 对于所有 k 值。相反,试试这个:

if not k == k:
  print l[k]

这会产生所需的 "NA" 输出。

请注意,您不能

来欺骗它
if k != k:

因为这也是 returns False.


这对你有用吗?

import numpy as np
# make array with all NaNs
a = np.ones(5) * np.nan
# populate some of it with non-NaN values
a[0] = 1
a[1] = 2
a[3] = 1
l = {1: "one", 2: "two", "NaN": "NA"}
for k in l:
  if not k == k:
    print l[k]
# this returns false
print (np.nan in a)

a_label = [l[a[n]] if a[n] in l else l["NaN"] for n in range(len(a))]
print a_label

输出:

False
['one', 'two', 'NA', 'one', 'NA']

您可以创建自己的字典来按照您想要的方式处理 NaN:

class MyDict(dict):

    def __getitem__(self, key):
        try:
            if np.isnan(key):
                return 'NA'
        except TypeError:
            pass
        return super(MyDict, self).__getitem__(key)

    def __contains__(self, key):
        try:
            self.__getitem__(key)
            return True
        except KeyError:
            return False

测试一下:

>>> l = MyDict({1: "one", 2: "two"})
>>> l[a[3]]
'NA'
>>> l[a[0]]
'one'
>>> np.nan in l
True