Выбор столбцов из панд 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
.
# 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
Вызов 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