web-dev-qa-db-fra.com

pandas: comment gérer un pivot avec un multi-index?

Je voudrais faire un pivot sur un pandas DataFrame, l'index étant constitué de deux colonnes et non d'une. Par exemple, un champ pour l'année, un pour le mois, un champ 'item' qui indique 'item 1' et 'item 2' et un champ 'value' avec des valeurs numériques. Je veux que l'index soit l'année + le mois.

La seule manière pour que cela fonctionne est de combiner les deux champs en un, puis de les séparer à nouveau. Y a-t-il un meilleur moyen?

Code minimal copié ci-dessous. Merci beaucoup!

PS Oui, je suis conscient qu'il y a d'autres questions portant les mots-clés 'pivot' et 'multi-index', mais je ne comprenais pas si/comment elles pouvaient m'aider avec cette question.

import pandas as pd
import numpy as np

df= pd.DataFrame()
month = np.arange(1, 13)
values1 = np.random.randint(0, 100, 12)
values2 = np.random.randint(200, 300, 12)


df['month'] = np.hstack((month, month))
df['year'] = 2004
df['value'] = np.hstack((values1, values2))
df['item'] = np.hstack((np.repeat('item 1', 12), np.repeat('item 2', 12)))

# This doesn't work: 
# ValueError: Wrong number of items passed 24, placement implies 2
# mypiv = df.pivot(['year', 'month'], 'item', 'value')

# This doesn't work, either:
# df.set_index(['year', 'month'], inplace=True)
# ValueError: cannot label index with a null key
# mypiv = df.pivot(columns='item', values='value')

# This below works but is not ideal: 
# I have to first concatenate then separate the fields I need
df['new field'] = df['year'] * 100 + df['month']

mypiv = df.pivot('new field', 'item', 'value').reset_index()
mypiv['year'] = mypiv['new field'].apply( lambda x: int(x) / 100)  
mypiv['month'] = mypiv['new field'] % 100
39
Pythonista anonymous

Vous pouvez grouper puis désempiler.

>>> df.groupby(['year', 'month', 'item'])['value'].sum().unstack('item')
item        item 1  item 2
year month                
2004 1          33     250
     2          44     224
     3          41     268
     4          29     232
     5          57     252
     6          61     255
     7          28     254
     8          15     229
     9          29     258
     10         49     207
     11         36     254
     12         23     209

Ou utiliser pivot_table:

>>> df.pivot_table(
        values='value', 
        index=['year', 'month'], 
        columns='item', 
        aggfunc=np.sum)
item        item 1  item 2
year month                
2004 1          33     250
     2          44     224
     3          41     268
     4          29     232
     5          57     252
     6          61     255
     7          28     254
     8          15     229
     9          29     258
     10         49     207
     11         36     254
     12         23     209
57
Alexander

Je crois que si vous incluez item dans votre MultiIndex, vous pouvez simplement désempiler:

df.set_index(['year', 'month', 'item']).unstack(level=-1)

Cela donne:

                value      
item       item 1 item 2
year month              
2004 1         21    277
     2         43    244
     3         12    262
     4         80    201
     5         22    287
     6         52    284
     7         90    249
     8         14    229
     9         52    205
     10        76    207
     11        88    259
     12        90    200

C'est un peu plus rapide que d'utiliser pivot_table, et à peu près à la même vitesse ou légèrement plus lentement que d'utiliser groupby.

21
Ajean