Панды: сбросить уровень из многоуровневого индекса столбца?

243

Если у меня есть многоуровневый индекс столбца:

>>> cols = pd.MultiIndex.from_tuples([("a", "b"), ("a", "c")])
>>> pd.DataFrame([[1,2], [3,4]], columns=cols)
    
   --- + -
    б | с
- + --- + -
0 | 1 | 2
1 | 3 | 4

Как я могу опустить уровень «a» этого индекса, чтобы я в итоге:

    б | с
- + --- + -
0 | 1 | 2
1 | 3 | 4
Дэвид Волевер
источник
3
Было бы неплохо иметь метод DataFrame, который делает это как для индекса, так и для столбцов. Либо из падения или выбора уровней индекса.
Сёрен
@ Sören Проверьте stackoverflow.com/a/56080234/3198568 . droplevelworks может работать с многоуровневыми индексами или столбцами через параметр axis.
Ирэн

Ответы:

307

Вы можете использовать MultiIndex.droplevel:

>>> cols = pd.MultiIndex.from_tuples([("a", "b"), ("a", "c")])
>>> df = pd.DataFrame([[1,2], [3,4]], columns=cols)
>>> df
   a   
   b  c
0  1  2
1  3  4

[2 rows x 2 columns]
>>> df.columns = df.columns.droplevel()
>>> df
   b  c
0  1  2
1  3  4

[2 rows x 2 columns]
DSM
источник
55
Вероятно, лучше прямо сказать, какой уровень падает. Уровни 0 индексируются, начиная сверху. >>> df.columns = df.columns.droplevel(0)
Тед Петру
6
Если индекс, который вы пытаетесь удалить, находится слева (строка), а не сверху (столбец), вы можете изменить «столбцы» на «индекс» и использовать тот же метод:>>> df.index = df.index.droplevel(1)
Идодо
7
В версии Panda 0.23.4 df.columns.droplevel()больше не доступно.
августа
8
@yoonghm Именно там, вы, вероятно , просто вызывая его на столбцы , которые не имеют мульти-индекс
матовое Harrison
1
У меня было три уровня глубины, и я хотел опуститься до среднего уровня. Я обнаружил, что снижение самого низкого (уровень [2]) и затем самого высокого (уровень [0]) работает лучше всего. >>>df.columns = df.columns.droplevel(2) >>>df.columns = df.columns.droplevel(0)
Кайл C
65

Другой способ отбросить индекс - использовать понимание списка:

df.columns = [col[1] for col in df.columns]

   b  c
0  1  2
1  3  4

Эта стратегия также полезна, если вы хотите объединить имена с обоих уровней, как в примере ниже, где нижний уровень содержит два «у»:

cols = pd.MultiIndex.from_tuples([("A", "x"), ("A", "y"), ("B", "y")])
df = pd.DataFrame([[1,2, 8 ], [3,4, 9]], columns=cols)

   A     B
   x  y  y
0  1  2  8
1  3  4  9

Отбрасывание верхнего уровня оставило бы два столбца с индексом «у». Этого можно избежать, соединив имена с пониманием списка.

df.columns = ['_'.join(col) for col in df.columns]

    A_x A_y B_y
0   1   2   8
1   3   4   9

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

мятный
источник
2
[col[1] for col in df.columns]это более прямо df.columns.get_level_values(1).
Эрик О Лебиго
2
Похожая потребность была в том, что некоторые столбцы имели пустые значения уровня. Использовал следующее:[col[0] if col[1] == '' else col[1] for col in df.columns]
Логан
43

Другой способ сделать это - переназначить dfна основе поперечного сечения df, используя метод .xs .

>>> df

    a
    b   c
0   1   2
1   3   4

>>> df = df.xs('a', axis=1, drop_level=True)

    # 'a' : key on which to get cross section
    # axis=1 : get cross section of column
    # drop_level=True : returns cross section without the multilevel index

>>> df

    b   c
0   1   2
1   3   4
spacetyper
источник
1
Это работает только тогда, когда есть одна метка для всего уровня столбца.
Тед Петру
1
Не работает, когда вы хотите сбросить второй уровень.
Сёрен
Это хорошее решение, если вы хотите нарезать и бросить для того же уровня. Если вы хотите нарезать на второй уровень (скажем b), затем опустите этот уровень и оставьте с первым уровнем ( a), сработает следующее:df = df.xs('b', axis=1, level=1, drop_level=True)
Тиффани Дж. Уилсон
27

Начиная с Pandas 0.24.0 , теперь мы можем использовать DataFrame.droplevel () :

cols = pd.MultiIndex.from_tuples([("a", "b"), ("a", "c")])
df = pd.DataFrame([[1,2], [3,4]], columns=cols)

df.droplevel(0, axis=1) 

#   b  c
#0  1  2
#1  3  4

Это очень полезно, если вы хотите сохранить цепочку методов DataFrame.

jxc
источник
Это самое «чистое» решение в том смысле, что возвращается новый DataFrame, а не модифицируется «на месте».
EliadL
16

Вы также можете достичь этого, переименовав столбцы:

df.columns = ['a', 'b']

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

sedeh
источник
По сути, это то, что делает первый ответ Mint. Теперь также нет необходимости указывать список имен (который, как правило, утомителен), так как он предоставляется вам df.columns.get_level_values(1).
Эрик О Лебиго
13

Небольшой трюк, использующий sum уровень = 1 (работает, когда уровень = 1 уникален)

df.sum(level=1,axis=1)
Out[202]: 
   b  c
0  1  2
1  3  4

Более распространенное решение get_level_values

df.columns=df.columns.get_level_values(1)
df
Out[206]: 
   b  c
0  1  2
1  3  4
YOBEN_S
источник
4

Я боролся с этой проблемой, так как не знаю, почему моя функция droplevel () не работает. Проработайте несколько и узнайте, что «a» в вашей таблице - это имя столбца, а «b», «c» - индекс. Делать как это поможет

df.columns.name = None
df.reset_index() #make index become label
dhFrank
источник
1
Это не воспроизводит желаемый результат вообще.
Эрик О Лебиго
На основании даты публикации этого сообщения, возможно, уровень падения не был включен в вашу версию Pandas (он был добавлен в стабильную версию 24.0 в январе 2019 года)
LinkBerest