Как избежать сортировки при индексации Pandas MultiIndex? - PullRequest
0 голосов
/ 19 января 2019

При индексировании фрейма данных 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 голосов
/ 19 января 2019

Вероятно, это решение принято для производительности.Вы можете прочитать об этом в Сортировка 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
...