Panda: rilasciare un livello da un indice di colonna multi-livello?


242

Se ho un indice di colonna multi-livello:

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

Come posso eliminare il livello "a" di quell'indice, quindi finisco con:

    b | c
- + --- + -
0 | 1 | 2
1 | 3 | 4

3
Sarebbe bello avere un metodo DataFrame che lo fa sia per indice che per colonne. Rilascio o selezione dei livelli dell'indice.
Sören,

@ Sören Guarda stackoverflow.com/a/56080234/3198568 . droplevelfunziona può funzionare su indici multilivello o colonne attraverso il parametro axis.
Irene,

Risposte:


306

Puoi usare 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]

55
Probabilmente è meglio dire esplicitamente quale livello viene abbandonato. I livelli sono indicizzati 0 a partire dall'alto. >>> df.columns = df.columns.droplevel(0)
Ted Petrou,

6
Se l'indice che si sta tentando di eliminare si trova sul lato sinistro (riga) e non sul lato superiore (colonna), è possibile modificare le "colonne" in "indice" e utilizzare lo stesso metodo:>>> df.index = df.index.droplevel(1)
Idodo

7
Nella versione Panda 0.23.4, df.columns.droplevel()non è più disponibile.
Yoonghm,

8
@yoonghm È lì, probabilmente lo stai semplicemente chiamando su colonne che non hanno un multiindice
matt harrison,

1
Avevo tre livelli di profondità e volevo scendere solo al livello medio. Ho scoperto che l'abbassamento del livello più basso (livello [2]) e quindi del livello più alto (livello [0]) ha funzionato meglio. >>>df.columns = df.columns.droplevel(2) >>>df.columns = df.columns.droplevel(0)
Kyle C,

65

Un altro modo per eliminare l'indice è utilizzare una comprensione dell'elenco:

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

   b  c
0  1  2
1  3  4

Questa strategia è utile anche se si desidera combinare i nomi di entrambi i livelli come nell'esempio di seguito in cui il livello inferiore contiene due "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

La caduta del livello superiore lascerebbe due colonne con l'indice 'y'. Ciò può essere evitato unendo i nomi con la comprensione dell'elenco.

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

    A_x A_y B_y
0   1   2   8
1   3   4   9

Questo è un problema che ho avuto dopo aver fatto un groupby e ci è voluto un po 'di tempo per trovare quest'altra domanda che lo ha risolto. Ho adattato quella soluzione al caso specifico qui.


2
[col[1] for col in df.columns]è più direttamente df.columns.get_level_values(1).
Eric O Lebigot,

2
Aveva un'esigenza simile in cui alcune colonne avevano valori di livello vuoti. Usato quanto segue:[col[0] if col[1] == '' else col[1] for col in df.columns]
Logan

43

Un altro modo per farlo è riassegnare in dfbase a una sezione trasversale di df, usando il metodo .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

1
Funziona solo ogni volta che esiste una singola etichetta per un intero livello di colonna.
Ted Petrou,

1
Non funziona quando si desidera eliminare il secondo livello.
Sören,

Questa è una buona soluzione se si desidera tagliare e rilasciare per lo stesso livello. Se si desidera tagliare il secondo livello (ad esempio b), quindi abbandonare quel livello ed essere lasciato al primo livello ( a), funzionerebbe quanto segue:df = df.xs('b', axis=1, level=1, drop_level=True)
Tiffany G. Wilson,

27

A partire da Pandas 0.24.0 , ora possiamo usare 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

Ciò è molto utile se si desidera mantenere attiva la catena di metodi DataFrame.


Questa è la soluzione "più pura" in quanto viene restituito un nuovo DataFrame anziché averlo modificato "sul posto".
EliadL

16

Puoi anche farlo rinominando le colonne:

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

Ciò comporta un passaggio manuale, ma potrebbe essere un'opzione soprattutto se alla fine rinominassi il tuo frame di dati.


Questa è essenzialmente la prima risposta di Mint. Ora, non è nemmeno necessario specificare l'elenco dei nomi (che è generalmente noioso), come viene fornito da df.columns.get_level_values(1).
Eric O Lebigot,

13

Un piccolo trucco usando sum con level = 1 (funziona quando level = 1 è tutto unico)

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

Soluzione più comune get_level_values

df.columns=df.columns.get_level_values(1)
df
Out[206]: 
   b  c
0  1  2
1  3  4

4

Ho lottato con questo problema poiché non so perché la mia funzione droplevel () non funziona. Lavora attraverso diversi e impara che 'a' nella tua tabella è il nome delle colonne e 'b', 'c' sono indice. Fare come questo aiuterà

df.columns.name = None
df.reset_index() #make index become label

1
Questo non riproduce affatto l'output desiderato.
Eric O Lebigot,

In base alla data di pubblicazione, il livello di rilascio potrebbe non essere stato incluso nella tua versione di Panda (è stato aggiunto alla versione stabile, 24.0, gennaio 2019)
LinkBerest
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.