如何在索引 Pandas MultiIndex 时避免排序?

4

当使用第二级索引对具有两个级别的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个回答

3
这可能是出于性能考虑做出的决定。您可以在Sorting a MultiIndex中了解相关信息,其中要点是您希望索引按字典顺序排序,这由当前输出的.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

谢谢,这很有道理(尽管我仍然觉得结果不直观)。在我的用例中,实际上是一组滚动协方差矩阵的数据框,因此保持每个协方差矩阵的对称性(其中行和列具有相同的顺序)非常重要。我想我现在会继续使用重新索引。 - David Thielke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接