从具有包含 NaN 的 MultiIndex 索引的数据帧中获取值
Posted
技术标签:
【中文标题】从具有包含 NaN 的 MultiIndex 索引的数据帧中获取值【英文标题】:Get values from dataframe with MultiIndex index containg NaNs 【发布时间】:2021-11-21 04:30:33 【问题描述】:我无法访问其中包含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,报告给pandas here)
【问题讨论】:
一个想法 - 如果使用None
可以进行测试?
我尝试手动创建索引为pd.MultiIndex.from_arrays([[0, 0, 0, 0, 0, 0, 0], ['o1', 'o2', 'o3', 'o4', 'o5', 'o6', None]], names=('temp_playlist', 'objId'))
,而None
被转换为np.nan
。结果与问题中发布的完全相同的索引。
很遗憾。
同样失败df.loc[(0, 'nan')]
?
是的,还有KeyError
,但是这个很清楚,但这给了我一个想法。现在这个想法是一个“糟糕的解决方案”,但它会是:df.index = [str(idx) for idx in df.index]; df.loc['(0, nan)']
。我不会发布这个“解决方案”作为我不会接受的答案;)
【参考方案1】:
更新
在对数据框进行分组和聚合后,我能够重现您的错误。
>>> 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>
不过,传入一个显式 MultiIndex 是可行的。
>>> 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]
使用单个元组返回数据帧也是如此。注意使用[[]]
返回一个DataFrame。
>>> df.loc[[(0, pd.NA)]]
vals
temp_playlist objId
0 NaN [3, 7]
DataFrame.reindex
也是如此(另请参阅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.nan
、pd.NA
和 None
都是不同的对象。与DataFrame.loc
一起使用时,Pandas 必须同样对待它们。
>>> 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'>
【讨论】:
[...] Passing in an explit MultiIndex works, though [...]
非常有趣。 Tks,这似乎比我的和@jezrael 的“更好的解决方案”。
仍然看起来像一个错误。我的意思是,为什么 df.loc[[(0, <any pd compatible NaN variation>]]
会工作并实际返回该索引处的数据列表(例如 [[3, 7]])?【参考方案2】:
将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')]
【讨论】:
感谢答案,但这与我的答案有些相似。我会等几天,如果没有人有其他解决方案,我会把它作为一个 bug 放到 pandas git 中。【参考方案3】:一个“糟糕的解决方案”,不是真正解决根本问题,而是提供一个可行的解决方案,将索引转换为字符串(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))]
【讨论】:
以上是关于从具有包含 NaN 的 MultiIndex 索引的数据帧中获取值的主要内容,如果未能解决你的问题,请参考以下文章
对包含 str 和元组的 Pandas MultiIndex 进行排序
为啥在具有一级索引的 MultiIndex 列的 pandas DataFrame 中表现不同?
按特定索引值过滤具有 MultiIndex 的数据帧 [重复]