索引Pandas MultiIndex时如何避免排序



当按第二级对具有两个级别的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

最新更新