为什么 NaN 的 pandas 数据帧值不能用作字典键?

Why can't a pandas dataframe value of NaN be used as a dictionary key?

我正在尝试使用以下数据框中 values 列的元素作为字典中的键。

In [1]: import numpy as np
   ...: import pandas as pd
   ...: rng = pd.date_range('2021-06-01', periods=4)
   ...: values = [1, -1, 0, np.nan]
   ...: df = pd.DataFrame(values, index=rng, columns=['values'])

In [2]: df
Out[2]:
            values
2021-06-01     1.0
2021-06-02    -1.0
2021-06-03     0.0
2021-06-04     NaN

目标是将 values 列的元素映射到单独列中的一组新值以生成以下数据框:

            values new_values
2021-06-01     1.0    A
2021-06-02    -1.0    B
2021-06-03     0.0    C
2021-06-04     NaN    D 

所以我创建了一个字典,其中键作为 values 列中的元素。

In [3]: repl = {1: 'A', 0: 'B', -1: 'C',np.nan: 'D'}
In [4]: df['rule'] = df['Val'].apply(lambda x: repl[x])
然而,

'NaN' 正在创建一个密钥错误(尽管它是可哈希的)。

KeyError                                  Traceback (most recent call last)
<ipython-input-143-2e9d3caa7f9c> in <module>
----> 1 df['rule'] = df['Val'].apply(lambda x: repl[x])

~/opt/miniconda3/envs/PyAlgo/lib/python3.7/site-packages/pandas/core/series.py in apply(self, func, convert_dtype, args, **kwds)
   4136             else:
   4137                 values = self.astype(object)._values
-> 4138                 mapped = lib.map_infer(values, f, convert=convert_dtype)
   4139
   4140         if len(mapped) and isinstance(mapped[0], Series):

pandas/_libs/lib.pyx in pandas._libs.lib.map_infer()

<ipython-input-143-2e9d3caa7f9c> in <lambda>(x)
----> 1 df['rule'] = df['Val'].apply(lambda x: repl[x])

KeyError: nan

显然,我可以为这个简单的示例手动创建列。但是,这只是一个最低限度的可重现示例。实际上,我有一个更大的数据框,其中包含更多潜在的键。

两个问题:

  1. 为什么 'NaN' 尽管它是可散列的但仍会生成密钥错误?
  2. 解决这个问题的最佳方法是什么?一种可能性是将 'NaN' 值设置为另一个值,例如原始数据框中的 -999?

您可以使用df["column"].map(dict)

>>> df["new_values"] = df["values"].map(repl)
>>> df
            values new_values
2021-06-01     1.0          A
2021-06-02    -1.0          C
2021-06-03     0.0          B
2021-06-04     NaN          D

我认为解释与以下事实有关:python 确定键是否在字典中的方式是 1. 散列键并对照字典检查它,然后 2. 检查使确保它正在寻找的键 is 它在字典中找到的键。

问题是虽然np.nan is np.nanreturnsTrue,np.float64(np.nan) is np.float(np.nan)returnsFalse。同样,np.float64(np.nan) is np.nan returns False.

我猜你的 apply 函数不起作用的原因是你创建的 lambda 函数试图在字典 repl 并没有找到它。即使您的原始数据只包含 np.nan,似乎 pandas 将其转换为 numpy.float64 类型。

例如

a = pd.DataFrame([[np.nan, 0], [1,1]])
a[0][0], type(a[0][0]), type(np.nan)
>> nan, numpy.float64, float

另一方面,

map 将字典作为参数,专门用于处理某些值缺失或等于 np.nan 的情况(参见:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.Series.map.html

有关在字典中使用 nan 作为键的更多信息,请参阅此问题:NaNs as key in dictionaries