Pandas: supprimer un niveau d'un index de colonne à plusieurs niveaux?

243

Si j'ai un index de colonne à plusieurs niveaux:

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

Comment puis-je supprimer le niveau "a" de cet index, donc je me retrouve avec:

    b | c
- + --- + -
0 | 1 | 2
1 | 3 | 4
David Wolever
la source
3
Ce serait bien d'avoir une méthode DataFrame qui fait cela pour l'index et les colonnes. Soit de supprimer ou de sélectionner des niveaux d'index.
Sören
@ Sören Consultez stackoverflow.com/a/56080234/3198568 . droplevelles travaux peuvent fonctionner sur des index ou des colonnes à plusieurs niveaux via le paramètre axis.
irene

Réponses:

307

Vous pouvez utiliser 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
la source
55
Il est probablement préférable de dire explicitement quel niveau est supprimé. Les niveaux sont indexés 0 en commençant par le haut. >>> df.columns = df.columns.droplevel(0)
Ted Petrou
6
Si l'index que vous essayez de supprimer se trouve sur le côté gauche (ligne) et non sur le côté supérieur (colonne), vous pouvez remplacer "colonnes" par "index" et utiliser la même méthode:>>> df.index = df.index.droplevel(1)
Idodo
7
Dans la version Panda 0.23.4, df.columns.droplevel()n'est plus disponible.
yoonghm
8
@yoonghm Il est là, vous l'appelez probablement sur des colonnes qui n'ont pas de multi-index
matt harrison
1
J'avais trois niveaux de profondeur et je voulais descendre jusqu'au niveau intermédiaire. J'ai trouvé que laisser tomber le plus bas (niveau [2]) puis le plus haut (niveau [0]) fonctionnait le mieux. >>>df.columns = df.columns.droplevel(2) >>>df.columns = df.columns.droplevel(0)
Kyle C
65

Une autre façon de supprimer l'index est d'utiliser une compréhension de liste:

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

   b  c
0  1  2
1  3  4

Cette stratégie est également utile si vous souhaitez combiner les noms des deux niveaux comme dans l'exemple ci-dessous où le niveau inférieur contient deux «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 suppression du niveau supérieur laisserait deux colonnes avec l'index «y». Cela peut être évité en joignant les noms à la compréhension de la liste.

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

    A_x A_y B_y
0   1   2   8
1   3   4   9

C'est un problème que j'ai eu après avoir fait un groupe et il a fallu un certain temps pour trouver cette autre question qui l'a résolu. J'ai adapté cette solution au cas spécifique ici.

menthe
la source
2
[col[1] for col in df.columns]est plus directement df.columns.get_level_values(1).
Eric O Lebigot
2
Avait un besoin similaire dans lequel certaines colonnes avaient des valeurs de niveau vides. Utilisé ce qui suit:[col[0] if col[1] == '' else col[1] for col in df.columns]
Logan
43

Une autre façon de procéder consiste à réaffecter en dffonction d'une section transversale de df, en utilisant la méthode .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
la source
1
Cela ne fonctionne que lorsqu'il existe une seule étiquette pour un niveau de colonne entier.
Ted Petrou
1
Ne fonctionne pas lorsque vous souhaitez supprimer le deuxième niveau.
Sören
C'est une bonne solution si vous souhaitez couper et déposer pour le même niveau. Si vous vouliez couper au deuxième niveau (disons b), puis laisser tomber ce niveau et se retrouver avec le premier niveau ( a), ce qui suit fonctionnerait:df = df.xs('b', axis=1, level=1, drop_level=True)
Tiffany G. Wilson
27

Depuis Pandas 0.24.0 , nous pouvons maintenant utiliser 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

Ceci est très utile si vous souhaitez maintenir le roulement de votre chaîne de méthodes DataFrame.

jxc
la source
Il s'agit de la solution "la plus pure" dans la mesure où un nouveau DataFrame est renvoyé plutôt que modifié "en place".
EliadL
16

Vous pouvez également y parvenir en renommant les colonnes:

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

Cela implique une étape manuelle, mais pourrait être une option, surtout si vous renommez éventuellement votre bloc de données.

sedeh
la source
C'est essentiellement ce que fait la première réponse de Mint. Maintenant, il n'est pas non plus nécessaire de spécifier la liste des noms (ce qui est généralement fastidieux), telle qu'elle vous est donnée par df.columns.get_level_values(1).
Eric O Lebigot
13

Un petit truc en utilisant sum avec level = 1 (fonctionne quand level = 1 est unique)

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

Solution plus courante get_level_values

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

J'ai eu du mal avec ce problème car je ne sais pas pourquoi ma fonction droplevel () ne fonctionne pas. Parcourez plusieurs et découvrez que «a» dans votre table est le nom des colonnes et «b», «c» sont index. Faire comme ça vous aidera

df.columns.name = None
df.reset_index() #make index become label
dhFrank
la source
1
Cela ne reproduit pas du tout la sortie souhaitée.
Eric O Lebigot
En fonction de la date de publication, le niveau de baisse peut ne pas avoir été inclus dans votre version de Pandas (il a été ajouté à la version stable, 24.0, en janvier 2019)
LinkBerest