在第二级对具有两个级别的MultiIndexed pandas数据框进行索引时,结果将自动按索引排序。是否有一种优雅的方式来获得结果而不进行排序?
这是一个玩具示例:
>>> df = pd.DataFrame(np.ones((10, 3)), columns=list("ABC"))
>>> df.index = pd.MultiIndex.from_product([range(5), list("AB")])
>>> df
A B C
0 A 1.0 1.0 1.0
B 1.0 1.0 1.0
1 A 1.0 1.0 1.0
B 1.0 1.0 1.0
2 A 1.0 1.0 1.0
B 1.0 1.0 1.0
3 A 1.0 1.0 1.0
B 1.0 1.0 1.0
4 A 1.0 1.0 1.0
B 1.0 1.0 1.0
>>> values = ["B", "A"]
>>> idx = pd.IndexSlice
>>> subset = df.loc[idx[:, values], values]
>>> subset
B A
0 A 1.0 1.0
B 1.0 1.0
1 A 1.0 1.0
B 1.0 1.0
2 A 1.0 1.0
B 1.0 1.0
3 A 1.0 1.0
B 1.0 1.0
4 A 1.0 1.0
B 1.0 1.0
我希望结果索引的第二级按["B", "A"]
的顺序排列-与选定的列相似-但是按排序顺序返回。
到目前为止,我发现一种解决方法是用subset.reindex(index=values, level=1)
重新索引结果。有什么方法可以更简洁/优雅地完成此操作,理想情况下不会导致数据框的副本?为什么将结果放在首位?在这种情况下,这似乎并不直观。
答案 0 :(得分:1)
这可能是性能决定。您可以在Sorting a MultiIndex中阅读有关此内容的信息,即您希望对索引进行lexsorted,这由.loc
的当前输出来维护。如果给定了您想要的输出,则索引将不会按顺序进行排序,这可能会导致多个问题。您应该使用.reindex
,因为它会导致经过分类的MultiIndex
。
您的原始DataFrame
已按字母顺序排序:
df.index.is_lexsorted()
#True
您得到的不需要的输出保持了这种排序:
df.loc[idx[:, values], values].index.is_lexsorted()
#True
如果我们使用.loc
修改了排序,则会丢失此排序,并且根据文档,现在会遇到性能问题。
subset = df.loc[[(0, 'B'), (0, 'A')], ['B', 'A']]
# B A
#0 B 1.0 1.0
# A 1.0 1.0
subset.index.is_lexsorted()
#False
尽管重新索引确实需要更长的时间,但它会导致索引排序错误。
subset2 = df.reindex(index=values, level=1)
subset2.index.is_lexsorted()
#True
如果您的MultiIndex
未按词序排序,则会有意外的后果。因此,即使subset
似乎已经排序,并且应该可以对范围进行切片,但您不能这样做。在.reindex
之后,切片是可行的,因为它是按词法排序的:
subset.loc[(0,'B'): (0, 'A')]
#UnsortedIndexError: 'Key length (2) was greater than MultiIndex lexsort depth (1)'
subset2.loc[(0,'B'): (0, 'A')]
# A B C
#0 B 1.0 1.0 1.0
# A 1.0 1.0 1.0