从包含NAN的多索引数据帧获取值

2024-04-29 14:21:14 发布

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

我无法访问包含nan的索引位置的值,我想知道如何解决这个问题。(在我的项目中,这个索引有一个非常特殊的意义,我真的需要保留它,否则我需要进行一些肮脏的手动修改:“总是有一个解决方案”,即使它是一个非常糟糕的解决方案)

df
Out
temp_playlist  objId
0              o1           [0, 6]
               o2           [1, 4]
               o3           [2, 5]
               o4       [8, 9, 12]
               o5         [10, 13]
               o6         [11, 14]
               NaN          [3, 7]
Name: x, dtype: object

df.index
Out
MultiIndex([(0, 'o1'),
            (0, 'o2'),
            (0, 'o3'),
            (0, 'o4'),
            (0, 'o5'),
            (0, 'o6'),
            (0,  nan)],
           names=['temp_playlist', 'objId'])

现在我想以df.loc[(0, np.nan)]的形式访问[3, 7]值,并获得KeyError: (0, nan)错误

仅从角度来看:[df.loc[idx] for idx in df.index if not pd.isna(idx[1])]工作正常,因为我跳过了有问题的索引

我遗漏了什么?我如何解决这个问题

(Windows 10、python 3.8.5、pandas 1.3.1、numpy 1.20.3,报告给pandashere


Tags: dfindexnan解决方案outplaylisttempo2
3条回答

Idea将NaN替换为NA

i = pd.MultiIndex.from_tuples([(0, 'o1'),
            (0, 'o2'),
            (0, 'o3'),
            (0, 'o4'),
            (0, 'o5'),
            (0, 'o6'),
            (0,  np.nan)])

df = pd.DataFrame({'a':0}, index=i)

df = df.rename(lambda x: 'NA' if pd.isna(x) else x, level=1)
print (df)
      a
0 o1  0
  o2  0
  o3  0
  o4  0
  o5  0
  o6  0
  NA  0

df.loc[(0, 'NA')]

更新

我能够在分组和聚合数据帧后重现您的错误

>>> import pandas as pd
>>> data = pd.DataFrame({
...     "temp_playlist": [0] * 15,
...     "objId": ['o1'] * 2 + ['o2'] * 2 + ['o3'] * 2 + ['o4'] * 3 + ['o5'] * 2 + ['o6'] * 2 + [pd.NA] * 2,
...     "vals": [0, 6, 1, 4, 2, 5, 8, 9, 12, 10, 13, 11, 14, 3, 7]
... })
>>> df = data.groupby(["temp_playlist", "objId"], dropna=False).agg(list)
>>> df.loc[(0, pd.NA)]
Traceback (most recent call last):
  File "/home/ec2-user/miniconda3/envs/so-pandas-nan-index/lib/python3.8/site-packages/pandas/core/indexes/base.py", line 3361, in get_loc
    return self._engine.get_loc(casted_key)
  File "pandas/_libs/index.pyx", line 76, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/index.pyx", line 108, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/hashtable_class_helper.pxi", line 5198, in pandas._libs.hashtable.PyObjectHashTable.get_item
  File "pandas/_libs/hashtable_class_helper.pxi", line 5206, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: <NA>

不过,传递一个explit多索引是可行的

>>> df.loc[pd.MultiIndex.from_tuples([(0, pd.NA)], names=["temp_playlist", "objId"])]
                       vals
temp_playlist objId
0             NaN    [3, 7]

>>> df.loc[pd.MultiIndex.from_tuples([(0, pd.NA)])]
         vals
0 NaN  [3, 7]

使用单个元组返回数据帧也是如此。注意使用[[]]返回一个数据帧

>>> df.loc[[(0, pd.NA)]]
                       vals
temp_playlist objId
0             NaN    [3, 7]

^{}一样(另见user guide on reindexing

>>> df.reindex([(0, pd.NA)])
                       vals
temp_playlist objId
0             NaN    [3, 7]

再现错误的最初尝试

我无法重现你的错误。您可以在下面看到,使用df.loc[(0, np.nan)]是有效的

Python 3.8.5 (default, Sep  4 2020, 07:30:14)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> import pandas as pd
>>> nan_index = pd.MultiIndex.from_tuples([(0, 'o1'),
            (0, 'o2'),
            (0, 'o3'),
            (0, 'o4'),
            (0, 'o5'),
            (0, 'o6'),
            (0,  np.nan)])
>>> print(nan_index)
MultiIndex([(0, 'o1'),
            (0, 'o2'),
            (0, 'o3'),
            (0, 'o4'),
            (0, 'o5'),
            (0, 'o6'),
            (0,  nan)],
           )
>>> rng = np.random.default_rng(42)
>>> vals = [rng.choice(20, 2) for i in range(nan_index.shape[0])]
>>> print(vals)
[array([ 1, 15]), array([13,  8]), array([ 8, 17]), array([ 1, 13]), array([4, 1]), array([10, 19]), array([14, 15])]
>>> df = pd.DataFrame({"vals": vals}, index=nan_index)
>>> print(df)
           vals
0 o1    [1, 15]
  o2    [13, 8]
  o3    [8, 17]
  o4    [1, 13]
  o5     [4, 1]
  o6   [10, 19]
  NaN  [14, 15]
>>> print(df.loc[(0, 'o1')])
vals    [1, 15]
Name: (0, o1), dtype: object
>>> print(df.loc[(0, np.nan)])
vals    [14, 15]
Name: (0, nan), dtype: object
>>> print(pd.__version__)
1.3.1

然后我注意到你的索引被打印为(0, nan),而我的是(0, np.nan)。区别在于我使用了np.nan,我怀疑你的是pd.NA

>>> nan_index = pd.MultiIndex.from_tuples([(0, 'o1'),
            (0, 'o2'),
            (0, 'o3'),
            (0, 'o4'),
            (0, 'o5'),
            (0, 'o6'),
            (0,  pd.NA)])
>>> nan_index
MultiIndex([(0, 'o1'),
            (0, 'o2'),
            (0, 'o3'),
            (0, 'o4'),
            (0, 'o5'),
            (0, 'o6'),
            (0,  nan)],
           )
>>> df = pd.DataFrame({"vals": vals}, index=nan_index)
>>> df
           vals
0 o1    [1, 15]
  o2    [13, 8]
  o3    [8, 17]
  o4    [1, 13]
  o5     [4, 1]
  o6   [10, 19]
  NaN  [14, 15]

然而,这并没有解决分歧。我仍然能够使用df.loc[(0, np.nan)]

>>> df.loc[(0, pd.NA)]
vals    [14, 15]
Name: (0, nan), dtype: object

>>> df.loc[(0, np.nan)]
vals    [14, 15]
Name: (0, nan), dtype: object

此外,我还能够使用df.loc[(0, None)]

>>> df.loc[(0, None)]
vals    [14, 15]
Name: (0, nan), dtype: object

只是确认一下,np.nanpd.NANone都是不同的对象。熊猫与DataFrame.loc一起使用时,必须以同样的方式对待它们

>>> pd.NA is np.nan
False

>>> pd.NA is None
False

>>> np.nan is None
False

>>> type(pd.NA)
<class 'pandas._libs.missing.NAType'>

>>> type(np.nan)
<class 'float'>

一个“糟糕的解决方案”不是真正解决根本问题,而是提供了一个有效的解决方案,它是通过将索引转换为字符串(这里str构造函数能够产生惊人的结果)

df.index = [str(idx) for idx in df.index]
df
Out 
(0, 'o1')        [0, 6]
(0, 'o2')        [1, 4]
(0, 'o3')        [2, 5]
(0, 'o4')    [8, 9, 12]
(0, 'o5')      [10, 13]
(0, 'o6')      [11, 14]
(0, nan)         [3, 7]
Name: x, dtype: object

df.index
Out
Index(['(0, 'o1')', '(0, 'o2')', '(0, 'o3')', '(0, 'o4')', '(0, 'o5')',
       '(0, 'o6')', '(0, nan)'],
      dtype='object')

xy_data[0].loc['(0, nan)']  # or
xy_data[0].loc[str((0, nan))]

相关问题 更多 >