web-dev-qa-db-fra.com

Pandas remodelage long à large, par deux variables

J'ai des données au format long et j'essaie de remodeler à large, mais il ne semble pas y avoir un moyen simple de le faire en utilisant fondre/empiler/désempiler:

Salesman  Height   product      price
  Knut      6        bat          5
  Knut      6        ball         1
  Knut      6        wand         3
  Steve     5        pen          2

Devient:

Salesman  Height    product_1  price_1  product_2 price_2 product_3 price_3  
  Knut      6        bat          5       ball      1        wand      3
  Steve     5        pen          2        NA       NA        NA       NA

Je pense que Stata peut faire quelque chose comme ça avec la commande reshape.

29
Luke

Un simple pivot peut suffire à vos besoins mais c'est ce que j'ai fait pour reproduire la sortie souhaitée:

df['idx'] = df.groupby('Salesman').cumcount()

Le simple fait d'ajouter un compteur/index au sein du groupe vous permettra d'obtenir la plupart du chemin, mais les libellés des colonnes ne seront pas comme vous le souhaitez:

print df.pivot(index='Salesman',columns='idx')[['product','price']]

        product              price        
idx            0     1     2      0   1   2
Salesman                                   
Knut         bat  ball  wand      5   1   3
Steve        pen   NaN   NaN      2 NaN NaN

Pour se rapprocher de la sortie souhaitée, j'ai ajouté ce qui suit:

df['prod_idx'] = 'product_' + df.idx.astype(str)
df['prc_idx'] = 'price_' + df.idx.astype(str)

product = df.pivot(index='Salesman',columns='prod_idx',values='product')
prc = df.pivot(index='Salesman',columns='prc_idx',values='price')

reshape = pd.concat([product,prc],axis=1)
reshape['Height'] = df.set_index('Salesman')['Height'].drop_duplicates()
print reshape

         product_0 product_1 product_2  price_0  price_1  price_2  Height
Salesman                                                                 
Knut           bat      ball      wand        5        1        3       6
Steve          pen       NaN       NaN        2      NaN      NaN       5

Edit: si vous voulez généraliser la procédure à plus de variables, je pense que vous pourriez faire quelque chose comme ceci (bien que cela ne soit pas assez efficace):

df['idx'] = df.groupby('Salesman').cumcount()

tmp = []
for var in ['product','price']:
    df['tmp_idx'] = var + '_' + df.idx.astype(str)
    tmp.append(df.pivot(index='Salesman',columns='tmp_idx',values=var))

reshape = pd.concat(tmp,axis=1)

@Luke a déclaré:

Je pense que Stata peut faire quelque chose comme ça avec la commande reshape.

Vous pouvez, mais je pense que vous avez également besoin d'un compteur dans le groupe pour obtenir la refonte en stata pour obtenir la sortie souhaitée:

     +-------------------------------------------+
     | salesman   idx   height   product   price |
     |-------------------------------------------|
  1. |     Knut     0        6       bat       5 |
  2. |     Knut     1        6      ball       1 |
  3. |     Knut     2        6      wand       3 |
  4. |    Steve     0        5       pen       2 |
     +-------------------------------------------+

Si vous ajoutez idx, vous pouvez refaçonner dans stata:

reshape wide product price, i(salesman) j(idx)
32
Karl D.

Un peu vieux mais je posterai ceci pour d'autres personnes.

Ce que vous voulez peut être atteint, mais vous ne devriez probablement pas le vouloir;) Pandas prend en charge les index hiérarchiques pour les lignes et les colonnes. Dans Python 2.7.x ...

from StringIO import StringIO

raw = '''Salesman  Height   product      price
  Knut      6        bat          5
  Knut      6        ball         1
  Knut      6        wand         3
  Steve     5        pen          2'''
dff = pd.read_csv(StringIO(raw), sep='\s+')

print dff.set_index(['Salesman', 'Height', 'product']).unstack('product')

Produit une représentation probablement plus pratique que ce que vous recherchiez

                price             
product          ball bat pen wand
Salesman Height                   
Knut     6          1   5 NaN    3
Steve    5        NaN NaN   2  NaN

L'avantage d'utiliser set_index et de désempiler par rapport à une seule fonction comme pivot est que vous pouvez diviser les opérations en petites étapes claires, ce qui simplifie le débogage.

16
Gecko
pivoted = df.pivot('salesman', 'product', 'price')

p. 192 Python pour l'analyse des données

9
chucklukowski

Voici une autre solution plus étoffée, tirée de site de Chris Albon .

Créer une "longue" trame de données

raw_data = {'patient': [1, 1, 1, 2, 2],
                'obs': [1, 2, 3, 1, 2],
          'treatment': [0, 1, 0, 1, 0],
              'score': [6252, 24243, 2345, 2342, 23525]}

df = pd.DataFrame(raw_data, columns = ['patient', 'obs', 'treatment', 'score'])

Créer des données "larges"

df.pivot(index='patient', columns='obs', values='score')

8
Charles Clayton

La solution de Karl D est au cœur du problème. Mais je trouve qu'il est beaucoup plus facile de tout faire pivoter (avec .pivot_table à cause des deux colonnes d'index) puis sort et affectez les colonnes pour réduire le MultiIndex:

df['idx'] = df.groupby('Salesman').cumcount()+1
df = df.pivot_table(index=['Salesman', 'Height'], columns='idx', 
                    values=['product', 'price'], aggfunc='first')

df = df.sort_index(axis=1, level=1)
df.columns = [f'{x}_{y}' for x,y in df.columns]
df = df.reset_index()

Sortie:

  Salesman  Height  price_1 product_1  price_2 product_2  price_3 product_3
0     Knut       6      5.0       bat      1.0      ball      3.0      wand
1    Steve       5      2.0       pen      NaN       NaN      NaN       NaN
3
ALollz