web-dev-qa-db-fra.com

Pandas: Modifier un nom de colonne spécifique dans la structure de données comportant des colonnes à plusieurs niveaux

Je veux trouver le moyen de changer le nom d'une colonne spécifique dans un cadre de données multiniveau.

Avec ces données:

data = {
    ('A', '1', 'I'): [1, 2, 3, 4, 5], 
    ('B', '2', 'II'): [1, 2, 3, 4, 5], 
    ('C', '3', 'I'): [1, 2, 3, 4, 5], 
    ('D', '4', 'II'): [1, 2, 3, 4, 5], 
    ('E', '5', 'III'): [1, 2, 3, 4, 5], 
}

dataDF = pd.DataFrame(data)

Ce code ne fonctionne pas:

dataDF.rename(columns = {('A', '1', 'I'):('Z', '100', 'Z')}, inplace=True)

Résultat:

    A   B   C   D   E
    1   2   3   4   5
    I   II  I   II  III
0   1   1   1   1   1
1   2   2   2   2   2
2   3   3   3   3   3
3   4   4   4   4   4
4   5   5   5   5   5

Et pas non plus:

dataDF.columns.values[0] = ('Z', '100', 'Z')

Résultat:

    A   B   C   D   E
    1   2   3   4   5
    I   II  I   II  III
0   1   1   1   1   1
1   2   2   2   2   2
2   3   3   3   3   3
3   4   4   4   4   4
4   5   5   5   5   5

Mais avec la combinaison des codes ci-dessus qui fonctionnent !!!

dataDF.columns.values[0] = ('Z', '100', 'Z')
dataDF.rename(columns = {('A', '1', 'I'):('Z', '100', 'Z')}, inplace=True)
dataDF

Résultat:

    Z   B   C   D   E
    100 2   3   4   5
    Z   II  I   II  III
0   1   1   1   1   1
1   2   2   2   2   2
2   3   3   3   3   3
3   4   4   4   4   4
4   5   5   5   5   5

Est-ce que ce bug des pandas?

7
sh.jeon

C'est ma théorie

les pandas ne veulent pas que pd.Indexs soit mutable. Nous pouvons le voir si nous essayons de changer nous-mêmes le premier élément de l'index

dataDF.columns[0] = ('Z', '100', 'Z')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-32-2c0b76762235> in <module>()
----> 1 dataDF.columns[0] = ('Z', '100', 'Z')

//anaconda/envs/3.5/lib/python3.5/site-packages/pandas/indexes/base.py in __setitem__(self, key, value)
   1372 
   1373     def __setitem__(self, key, value):
-> 1374         raise TypeError("Index does not support mutable operations")
   1375 
   1376     def __getitem__(self, key):

TypeError: Index does not support mutable operations

Mais les pandas ne peuvent pas contrôler ce que vous faites avec l'attribut values.

dataDF.columns.values[0] = ('Z', '100', 'Z')

nous voyons que dataDF.columns est identique, mais dataDF.columns.values reflète clairement le changement. Malheureusement, df.columns.values n’est pas ce qui apparaît sur l’affichage du cadre de données.


D'un autre côté, cela semble vraiment fonctionner. Le fait que cela ne me semble pas mauvais.

dataDF.rename(columns={('A', '1', 'I'): ('Z', '100', 'Z')}, inplace=True)

Je crois que si cela ne fonctionne que si les valeurs ont été modifiées, c'est que rename force la reconstruction des colonnes en examinant les valeurs. Depuis que nous changeons les valeurs, cela fonctionne maintenant. C'est exceptionnellement kludgy et je ne recommande pas de construire un processus qui repose sur cela.


ma recommandation

  • identifiez l'emplacement du nom de la colonne que vous souhaitez modifier
  • assigne le nom de la colonne au tableau de valeurs
  • construire de nouvelles colonnes à partir de zéro, explicitement

from_col = ('A', '1', 'I')
to_col = ('Z', '100', 'Z')
colloc = dataDF.columns.get_loc(from_col)
cvals = dataDF.columns.values
cvals[colloc] = to_col

dataDF.columns = pd.MultiIndex.from_tuples(cvals.tolist())

dataDF

[![enter code here][1]][1]
10
piRSquared

Vous pouvez simplement le changer comme DF.columns.levels=[[u'Z', u'B', u'C', u'D', u'E'],[u'5', u'2', u'3', u'4', u'5'],[u'IIIIII', u'II', u'III']]

1
Dark Matter

Je suis tombé sur cette question alors que j'essayais moi-même de trouver la solution pour renommer les noms de colonnes dans un cadre de données à plusieurs niveaux. J'ai essayé la solution fournie par @Dark Matter car elle semblait très simple:

dataDF.columns.levels = [[u'Z', u'B', u'C', u'D', u'E'], [u'100', u'2', u'3', u'4', u'5'], [u'Z', u'II', u'III']]

Mais un message d'erreur était affiché:

C:\anaconda3\lib\site-packages\ipykernel_launcher.py:1: FutureWarning: setting `levels` directly is deprecated. Use set_levels instead
  """Entry point for launching an IPython kernel.

Il semble que cela a fonctionné mais ne fonctionne plus. Alors j'ai utilisé:

dataDF.columns.set_levels([['Z', 'B', 'C', 'D', 'E'],
                           ['100', '2', '3', '4', '5'],
                           ['Z', 'II', 'III']],
                          [0, 1, 2], inplace=True)

Résultat: dataDF

Z   B   C   D   E
100 2   3   4   5
Z   II  Z   II  III
0   1   1   1   1   1
1   2   2   2   2   2
2   3   3   3   3   3
3   4   4   4   4   4
4   5   5   5   5   5
1
novastar