【问题标题】:Why can't a pandas dataframe value of NaN be used as a dictionary key?为什么不能将 NaN 的 pandas 数据框值用作字典键?
【发布时间】:2021-08-24 19:44:27
【问题描述】:

我正在尝试将以下数据框中的 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?

【问题讨论】:

  • np.nan 当然可以用作字典键。你可以自己尝试来证明这一点。抱怨不是np.nan 不可散列,而是密钥不存在。有没有可能你真的得到了字符串“nan”? Pandas 做了很多自动转换。
  • np.nan 确实是可散列的,如下所示。
  • 在 [1]: 将 numpy 作为 np 导入 [2]: hash(np.nan) Out[2]: 0 在 [3]: d = {-1: 'A', np .nan: 'B'} 输入 [4]: d[np.nan] 输出[4]: 'B'

标签: python pandas hashtable nan


【解决方案1】:

您可以使用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

【讨论】:

  • 很多,谢谢。你知道为什么“NaN”一旦被封装为 Pandas 数据框中的值就不能用作字典键吗?
  • 不客气。我不确定这里发生了什么,可能是熊猫的一些内部结构。
【解决方案2】:

我认为这个解释与以下事实有关正在寻找它在字典中找到的 is 键。

问题是尽管np.nan is np.nan 返回Truenp.float64(np.nan) is np.float(np.nan) 返回False。同样,np.float64(np.nan) is np.nan 返回False

我的猜测是您的 apply 函数不起作用的原因是您创建的 lambda 函数试图在字典 repl 中找到 np.float64(np.nan)(或您的 DataFrame 中的类似内容)但没有找到它。即使您的原始数据只包含np.nan,熊猫似乎也将其转换为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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-10
    • 2021-08-24
    • 1970-01-01
    • 2021-02-22
    • 1970-01-01
    • 2023-03-13
    • 2021-06-01
    • 1970-01-01
    相关资源
    最近更新 更多