Pandas的多级索引(MultiIndex)的行为是否会随着层级数量的变化而改变?
我有一个行索引为多级且层级超过两个的Pandas Series。
arrays = [
["bar", "baz", "foo", "qux"],
["one", "two",],
["a", "b","c" ]
]
index = pd.MultiIndex.from_product(arrays, names=["first", "second","third"])
s = pd.Series(np.random.randn(24), index=index)
我能够像下面这样对这个DataFrame进行切片:
df.loc[(slice(None), slice(None), "a")
输出如下
first second
bar one 1.574135
two 1.557756
baz one -0.023177
two -0.537326
foo one -0.183099
two -1.776498
qux one 0.425121
two 0.451778
dtype: float64
为了让某些绘图更方便,我重置了索引,将第三列作为行的一部分嵌入进来。
s = s.reset_index(level = [2])
third 0
first second
bar one a 1.574135
one b -2.450800
one c 0.966331
two a 1.557756
two b -0.800615
two c 0.405982
baz one a -0.023177
one b 1.743043
one c 0.847148
two a -0.537326
two b -1.157622
two c -0.229345
foo one a -0.183099
one b 1.047875
one c -1.980324
two a -1.776498
two b -1.169729
two c -1.509195
qux one a 0.425121
one b 0.903984
one c -0.455681
two a 0.451778
two b 0.215866
two c -0.090196
现在,使用相同的格式进行切片时会得到KeyError
s.loc[(slice(None),"one")]
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File c:filepath\pandas\core\indexes\base.py:3812, in Index.get_loc(self, key)
3811 try:
-> 3812 return self._engine.get_loc(casted_key)
3813 except KeyError as err:
File pandas/_libs/index.pyx:167, in pandas._libs.index.IndexEngine.get_loc()
File pandas/_libs/index.pyx:196, in pandas._libs.index.IndexEngine.get_loc()
File pandas/_libs/hashtable_class_helper.pxi:7088, in pandas._libs.hashtable.PyObjectHashTable.get_item()
File pandas/_libs/hashtable_class_helper.pxi:7096, in pandas._libs.hashtable.PyObjectHashTable.get_item()
KeyError: 'one'
The above exception was the direct cause of the following exception:
KeyError Traceback (most recent call last)
Cell In[65], line 1
----> 1 s.loc[(slice(None),"one")]
File c:filepath\pandas\core\indexing.py:1184, in _LocationIndexer.__getitem__(self, key)
1182 if self._is_scalar_access(key):
...
3822 # InvalidIndexError. Otherwise we fall through and re-raise
3823 # the TypeError.
3824 self._check_indexing_error(key)
KeyError: 'one'
摸了半天头后,我想也许在切片时只传入了两个参数,pandas以为第二个是列切片,尽管它在括号内。
果不其然,在外侧再加一个slice(None),以让pandas明确第二个参数不是用于列,结果是
s.loc[(slice(None),"one"),slice(None)]
third 0
first second
bar one a 1.574135
one b -2.450800
one c 0.966331
baz one a -0.023177
one b 1.743043
one c 0.847148
foo one a -0.183099
one b 1.047875
one c -1.980324
qux one a 0.425121
one b 0.903984
one c -0.455681
为什么pandas会这样工作?这只是一个怪现象吗?将来我应该怎么做来避免这个问题?
解决方案
当你重置索引时,你创建的是DataFrame,而不是Series。 在DataFrame上使用 loc 需要 [row_indexer, column_indexer] _getitem_tuple_same_dim 似乎就是该函数。
def _getitem_tuple_same_dim(self, tup: tuple):
"""
Index with indexers that should return an object of the same dimension
as self.obj.
This is only called after a failed call to _getitem_lowerdim.
"""
retval = self.obj
# Selecting columns before rows is significantly faster
start_val = (self.ndim - len(tup)) + 1
for i, key in enumerate(reversed(tup)):
i = self.ndim - i - start_val
if com.is_null_slice(key):
continue
retval = getattr(retval, self.name)._getitem_axis(key, axis=i)
# We should never have retval.ndim < self.ndim, as that should
# be handled by the _getitem_lowerdim call above.
assert retval.ndim == self.ndim
if retval is self.obj:
# if all axes were a null slice (`df.loc[:, :]`), ensure we still
# return a new object (https://github.com/pandas-dev/pandas/pull/49469)
retval = retval.copy(deep=False)
return retval
self.ndim 对于Series为 1,对于DataFrame为 2
于是发生的情况是 start_val = (2 - 2) + 1,接着它对元组 for i, key in enumerate(reversed(tup)): 进行逆序迭代,因此对你包含 (slice(None), "one") 的元组的第一次迭代变成 i=0; key='one'。它之所以进行反转,是因为显然“先选择列再选择行要快得多”。这意味着你原始元组的最后一个值 (slice(None), "one") 正在寻找名为 one 的列,而该列并不存在,于是你就得到KeyError。
如果把它完整推导下去
start_val = (2 - 2) + 1 = 1
i = 0, key = 'one' # from enumerate(reversed(tup))
i = 2 - 0 - 1 = 1 # i gets reassigned
_getitem_axis('one', axis=1) # axis=1 means columns which results in a KeyError
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。