Выбор столбцов из панд MultiIndex

У меня есть DataFrame с столбцами MultiIndex, которые выглядят следующим образом:

# sample data
col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'],
                                ['a', 'b', 'c', 'a', 'b', 'c']])
data = pd.DataFrame(np.random.randn(4, 6), columns=col)
data

sample data

Каков правильный и простой способ выбора только определенных столбцов (например, ['a', 'c'], а не диапазона) со второго уровня?

В настоящее время я делаю это так:

import itertools
tuples = [i for i in itertools.product(['one', 'two'], ['a', 'c'])]
new_index = pd.MultiIndex.from_tuples(tuples)
print(new_index)
data.reindex_axis(new_index, axis=1)

expected result

Не похоже, что это хорошее решение, потому что мне нужно выкинуть itertools, создать другой MultiIndex вручную, а затем повторно проиндексировать (и мой фактический код еще более грязный, поскольку списки столбцов не так просто для извлечения). Я уверен, что это должно быть несколько ix или xs способ сделать это, но все, что я пытался, привело к ошибкам.

Ответ 1

Это не здорово, но возможно:

>>> data
        one                           two                    
          a         b         c         a         b         c
0 -0.927134 -1.204302  0.711426  0.854065 -0.608661  1.140052
1 -0.690745  0.517359 -0.631856  0.178464 -0.312543 -0.418541
2  1.086432  0.194193  0.808235 -0.418109  1.055057  1.886883
3 -0.373822 -0.012812  1.329105  1.774723 -2.229428 -0.617690
>>> data.loc[:,data.columns.get_level_values(1).isin({"a", "c"})]
        one                 two          
          a         c         a         c
0 -0.927134  0.711426  0.854065  1.140052
1 -0.690745 -0.631856  0.178464 -0.418541
2  1.086432  0.808235 -0.418109  1.886883
3 -0.373822  1.329105  1.774723 -0.617690

должно сработать?

Ответ 2

Я думаю, что есть намного лучший способ (сейчас), поэтому я беспокоюсь о том, чтобы вытащить этот вопрос (который был лучшим результатом Google) из тени:

data.select(lambda x: x[1] in ['a', 'b'], axis=1)

дает ожидаемый результат в быстром и чистом однострочном слое:

        one                 two          
          a         b         a         b
0 -0.341326  0.374504  0.534559  0.429019
1  0.272518  0.116542 -0.085850 -0.330562
2  1.982431 -0.420668 -0.444052  1.049747
3  0.162984 -0.898307  1.762208 -0.101360

В основном это объясняет себя, [1] относится к уровню.

Ответ 3

Вы можете использовать либо loc, либо ix. Я покажу пример с loc:

data.loc[:, [('one', 'a'), ('one', 'c'), ('two', 'a'), ('two', 'c')]]

Когда у вас есть MultiIndexed DataFrame, и вы хотите отфильтровать только некоторые из столбцов, вам нужно передать список кортежей, соответствующих этим столбцам. Таким образом, подход itertools был в порядке, но вам не нужно создавать новый MultiIndex:

data.loc[:, list(itertools.product(['one', 'two'], ['a', 'c']))]

Ответ 4

Чтобы выбрать все столбцы с именами 'a' и 'c' на втором уровне индексатора столбцов, вы можете использовать слайсеры:

>>> data.loc[:, (slice(None), ('a', 'c'))]

        one                 two          
          a         c         a         c
0 -0.983172 -2.495022 -0.967064  0.124740
1  0.282661 -0.729463 -0.864767  1.716009
2  0.942445  1.276769 -0.595756 -0.973924
3  2.182908 -0.267660  0.281916 -0.587835

Здесь вы можете прочитать больше о слайсерах.

Ответ 5

ix и select не рекомендуются!

Использование pd.IndexSlice делает loc более предпочтительным вариантом для ix и select.


DataFrame.loc с pd.IndexSlice

# Setup
col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'],
                                ['a', 'b', 'c', 'a', 'b', 'c']])
data = pd.DataFrame('x', index=range(4), columns=col)
data

  one       two      
    a  b  c   a  b  c
0   x  x  x   x  x  x
1   x  x  x   x  x  x
2   x  x  x   x  x  x
3   x  x  x   x  x  x

data.loc[:, pd.IndexSlice[:, ['a', 'c']]]

  one    two   
    a  c   a  c
0   x  x   x  x
1   x  x   x  x
2   x  x   x  x
3   x  x   x  x

Вы можете в качестве альтернативы, axis параметр loc, чтобы сделать его явным какую ось вы индексировать от:

data.loc(axis=1)[pd.IndexSlice[:, ['a', 'c']]]

  one    two   
    a  c   a  c
0   x  x   x  x
1   x  x   x  x
2   x  x   x  x
3   x  x   x  x

MultiIndex.get_level_values

Вызов data.columns.get_level_values для фильтрации с помощью loc - еще один вариант:

data.loc[:, data.columns.get_level_values(1).isin(['a', 'c'])]

  one    two   
    a  c   a  c
0   x  x   x  x
1   x  x   x  x
2   x  x   x  x
3   x  x   x  x

Это, естественно, позволяет фильтровать любое условное выражение на одном уровне. Вот случайный пример с лексикографической фильтрацией:

data.loc[:, data.columns.get_level_values(1) > 'b']

  one two
    c   c
0   x   x
1   x   x
2   x   x
3   x   x

Более подробную информацию о нарезке и фильтрации мультииндексов можно найти в разделе Выбор строк в пандах MultiIndex DataFrame.

Ответ 6

Немного проще, на мой взгляд, рифф на Марка П. ответить с помощью ломтика:

import pandas as pd
col = pd.MultiIndex.from_arrays([['one', 'one', 'one', 'two', 'two', 'two'], ['a', 'b', 'c', 'a', 'b', 'c']])
data = pd.DataFrame(np.random.randn(4, 6), columns=col)

data.loc[:, pd.IndexSlice[:, ['a', 'c']]]

        one                 two          
          a         c         a         c
0 -1.731008  0.718260 -1.088025 -1.489936
1 -0.681189  1.055909  1.825839  0.149438
2 -1.674623  0.769062  1.857317  0.756074
3  0.408313  1.291998  0.833145 -0.471879

Начиная с панды 0,21 или около того, .select не рекомендуется в пользу .loc.

Ответ 7

Самый простой способ с .loc:

data.loc[:, (['one', 'two'], ['a', 'b'])]


   one       two     
     a    c    a    c
0  0.4 -0.6 -0.7  0.9
1  0.1  0.4  0.5 -0.3
2  0.7 -1.6  0.7 -0.8
3 -0.9  2.6  1.9  0.6

Помните, что [] и () имеют особое значение при работе с объектом MultiIndex:

(...) кортеж интерпретируется как один многоуровневый ключ

(...) список используется для указания нескольких клавиш [на одном уровне ]

(...) кортеж списков ссылается на несколько значений в пределах уровня

Когда мы пишем (['one', 'two'], ['a', 'b']), первый список внутри кортежа определяет все значения, которые мы хотим получить с 1-го уровня MultiIndex. Второй список внутри кортежа определяет все значения, которые мы хотим от 2-го уровня MultiIndex.

Источник: MultiIndex/Advanced Indexing