当按第二级对具有两个级别的MultiIndexed Panda数据帧进行索引时,结果会自动按索引排序。有没有一种不用排序就能得到结果的优雅方法?
这里有一个玩具示例:
>>> 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)
重新索引结果。有没有什么方法可以以更简洁/优雅的方式做到这一点,理想情况下不会产生数据帧的副本?为什么结果排在第一位?在这种情况下,这似乎是不明智的。
这可能是为了性能而做出的决定。您可以在Sorting a MultiIndex中了解到这一点,其要点是您希望索引进行lexsorted,该索引由.loc
的当前输出维护。如果它为您提供了所需的输出,那么索引就不会进行lexsort,这可能会导致几个问题。您应该使用.reindex
,因为它将导致lexsorted的MultiIndex
。
您的原始DataFrame
是lexsort:
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
尽管重新索引确实需要更长的时间,但它会导致lexsorted索引。
subset2 = df.reindex(index=values, level=1)
subset2.index.is_lexsorted()
#True
当您的MultiIndex
未进行lexsorted时,会出现意外的后果。因此,即使subset
看起来是排序的,并且应该可以对范围进行切片,但你不能。在.reindex
之后,切片是可行的,因为它是lexsort:
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