为Pandas MultiIndex编制索引时如何避免排序?

时间:2019-01-18 21:13:49

标签: python pandas multi-index

在第二级对具有两个级别的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)重新索引结果。有什么方法可以更简洁/优雅地完成此操作,理想情况下不会导致数据框的副本?为什么将结果放在首位?在这种情况下,这似乎并不直观。

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