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.