Pandy: upuścić poziom z wielopoziomowego indeksu kolumny?

242

Jeśli mam indeks kolumn wielopoziomowych:

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

Jak mogę upuścić poziom „a” tego indeksu, aby uzyskać:

    b | do
- + --- + -
0 | 1 | 2)
1 | 3 | 4
David Wolever
źródło
3
Byłoby miło mieć metodę DataFrame, która robi to zarówno dla indeksu, jak i kolumn. Opuszczanie lub wybieranie poziomów indeksu.
Sören
@ Sören Sprawdź stackoverflow.com/a/56080234/3198568 . droplevelprace mogą działać na indeksach wielopoziomowych lub kolumnach poprzez parametr axis.
Irene

Odpowiedzi:

306

Możesz użyć 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
źródło
55
Prawdopodobnie najlepiej jest wyraźnie powiedzieć, który poziom jest opuszczany. Poziomy są indeksowane od 0, zaczynając od góry. >>> df.columns = df.columns.droplevel(0)
Ted Petrou,
6
Jeśli indeks, który próbujesz usunąć, znajduje się po lewej (wiersz) stronie, a nie po stronie górnej (kolumny), możesz zmienić „kolumny” na „indeks” i użyć tej samej metody:>>> df.index = df.index.droplevel(1)
Idodo
7
W wersji Panda 0.23.4 df.columns.droplevel()nie jest już dostępny.
yoonghm,
8
@yoonghm Jest tam, prawdopodobnie po prostu nazywasz to kolumnami, które nie mają wielu indeksów
Matt Harrison
1
Miałem trzy poziomy głębokości i chciałem zejść tylko na środkowy poziom. Odkryłem, że upuszczanie najniższego (poziom [2]), a następnie najwyższego (poziom [0]) działało najlepiej. >>>df.columns = df.columns.droplevel(2) >>>df.columns = df.columns.droplevel(0)
Kyle C
65

Innym sposobem na usunięcie indeksu jest użycie zrozumienia listy:

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

   b  c
0  1  2
1  3  4

Ta strategia jest również przydatna, jeśli chcesz połączyć nazwy z obu poziomów, jak w poniższym przykładzie, w którym dolny poziom zawiera dwa „y”:

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

Upuszczenie najwyższego poziomu spowoduje pozostawienie dwóch kolumn z indeksem „y”. Można tego uniknąć, łącząc nazwy ze zrozumieniem listy.

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

    A_x A_y B_y
0   1   2   8
1   3   4   9

To był problem, który miałem po zrobieniu grupy i zajęło mi trochę czasu znalezienie innego pytania, które go rozwiązało. Dostosowałem to rozwiązanie do konkretnego przypadku.

Mennica
źródło
2
[col[1] for col in df.columns]jest bardziej bezpośredni df.columns.get_level_values(1).
Eric O Lebigot,
2
Miał podobną potrzebę, w której niektóre kolumny miały puste wartości poziomu. Zastosowano następujące:[col[0] if col[1] == '' else col[1] for col in df.columns]
Logan
43

Innym sposobem na to jest ponowne przypisanie dfna podstawie przekroju dfprzy użyciu metody .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
źródło
1
Działa to tylko wtedy, gdy istnieje jedna etykieta dla całego poziomu kolumny.
Ted Petrou,
1
Nie działa, gdy chcesz upuścić drugi poziom.
Sören,
To dobre rozwiązanie, jeśli chcesz kroić i upuszczać na tym samym poziomie. Jeśli chcesz pokroić na drugi poziom (powiedzmy b), a następnie upuść ten poziom i pozostać z pierwszym poziomem ( a), następujące działania będą działać:df = df.xs('b', axis=1, level=1, drop_level=True)
Tiffany G. Wilson
27

Począwszy od Pandas 0.24.0 , możemy teraz używać 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

Jest to bardzo przydatne, jeśli chcesz zachować ciągłość łańcucha metod DataFrame.

jxc
źródło
Jest to „najczystsze” rozwiązanie, ponieważ zwracana jest nowa ramka danych zamiast modyfikować ją „na miejscu”.
EliadL
16

Możesz to również osiągnąć, zmieniając nazwy kolumn:

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

Wymaga to ręcznego kroku, ale może być opcją, szczególnie jeśli ostatecznie zmienisz nazwę swojej ramki danych.

sedeh
źródło
Zasadniczo taka jest pierwsza odpowiedź Mint. Teraz nie ma również potrzeby określania listy nazwisk (co jest na ogół uciążliwe), ponieważ jest ona nadawana przez użytkownika df.columns.get_level_values(1).
Eric O Lebigot,
13

Mała sztuczka sum z użyciem poziomu = 1 (działa, gdy poziom = 1 jest unikalny)

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

Bardziej powszechne rozwiązanie get_level_values

df.columns=df.columns.get_level_values(1)
df
Out[206]: 
   b  c
0  1  2
1  3  4
YOBEN_S
źródło
4

Walczyłem z tym problemem, ponieważ nie wiem, dlaczego moja funkcja droplevel () nie działa. Przeanalizuj kilka i dowiedz się, że „a” w tabeli to nazwa kolumn, a „b”, „c” to indeks. Zrób tak, to pomoże

df.columns.name = None
df.reset_index() #make index become label
dhFrank
źródło
1
To wcale nie odtwarza pożądanej mocy wyjściowej.
Eric O Lebigot,
W zależności od daty opublikowania poziom upuszczenia mógł nie zostać uwzględniony w twojej wersji Pandas (został dodany do stabilnej wersji, 24.0, w styczniu 2019 r.)
LinkBerest