web-dev-qa-db-fra.com

Quel est l'équivalent pandas de dplyr résumer / agréger par plusieurs fonctions?

Je rencontre des problèmes lors de la transition vers pandas de R où le package dplyr peut facilement se regrouper et effectuer plusieurs résumés.

Aidez-moi à améliorer mon code Python pandas pour plusieurs agrégations:

import pandas as pd
data = pd.DataFrame(
    {'col1':[1,1,1,1,1,2,2,2,2,2],
    'col2':[1,2,3,4,5,6,7,8,9,0],
     'col3':[-1,-2,-3,-4,-5,-6,-7,-8,-9,0]
    }
)
result = []
for k,v in data.groupby('col1'):
    result.append([k, max(v['col2']), min(v['col3'])])
print pd.DataFrame(result, columns=['col1', 'col2_agg', 'col3_agg'])

Problèmes:

  • trop verbeux
  • peut probablement être optimisé et efficace. (J'ai réécrit une implémentation de for-loop groupby Dans groupby.agg Et l'amélioration des performances était énorme).

Dans R, le code équivalent serait:

data %>% groupby(col1) %>% summarize(col2_agg=max(col2), col3_agg=min(col3))

MISE À JOUR: @ayhan a résolu ma question, voici une question de suivi que je posterai ici au lieu de commenter:

Q2) Quel est l'équivalent de groupby().summarize(newcolumn=max(col2 * col3)) c'est-à-dire une agrégation/récapitulation où la fonction est une fonction composée de 2+ colonnes?

31
B.Mr.W.

L'équivalent de

df %>% groupby(col1) %>% summarize(col2_agg=max(col2), col3_agg=min(col3))

est

df.groupby('col1').agg({'col2': 'max', 'col3': 'min'})

qui revient

      col2  col3
col1            
1        5    -5
2        9    -9

L'objet renvoyé est un pandas.DataFrame avec un index appelé col1 Et des colonnes nommées col2 Et col3. Par défaut, lorsque vous regroupez vos données pandas définit la ou les colonnes de regroupement comme index pour un accès et une modification efficaces. Cependant, si vous ne le souhaitez pas, il existe deux alternatives pour définir col1 Sous forme de colonne.

  • Passe as_index=False:

    df.groupby('col1', as_index=False).agg({'col2': 'max', 'col3': 'min'})
    
  • Appelez reset_index:

    df.groupby('col1').agg({'col2': 'max', 'col3': 'min'}).reset_index()
    

les deux donnent

col1  col2  col3           
   1     5    -5
   2     9    -9

Vous pouvez également transmettre plusieurs fonctions à groupby.agg.

agg_df = df.groupby('col1').agg({'col2': ['max', 'min', 'std'], 
                                 'col3': ['size', 'std', 'mean', 'max']})

Cela retourne également un DataFrame mais maintenant il a un MultiIndex pour les colonnes.

     col2               col3                   
      max min       std size       std mean max
col1                                           
1       5   1  1.581139    5  1.581139   -3  -1
2       9   0  3.535534    5  3.535534   -6   0

MultiIndex est très pratique pour la sélection et le regroupement. Voici quelques exemples:

agg_df['col2']  # select the second column
      max  min       std
col1                    
1       5    1  1.581139
2       9    0  3.535534

agg_df[('col2', 'max')]  # select the maximum of the second column
Out: 
col1
1    5
2    9
Name: (col2, max), dtype: int64

agg_df.xs('max', axis=1, level=1)  # select the maximum of all columns
Out: 
      col2  col3
col1            
1        5    -1
2        9     0

Plus tôt (avant version 0.20. ), il était possible d'utiliser des dictionnaires pour renommer les colonnes dans l'appel agg. Par exemple

df.groupby('col1')['col2'].agg({'max_col2': 'max'})

retournerait le maximum de la deuxième colonne comme max_col2:

      max_col2
col1          
1            5
2            9

Cependant, il a été déconseillé au profit de la méthode de renommage:

df.groupby('col1')['col2'].agg(['max']).rename(columns={'max': 'col2_max'})

      col2_max
col1          
1            5
2            9

Il peut être détaillé pour un DataFrame comme agg_df Défini ci-dessus. Vous pouvez utiliser une fonction de changement de nom pour aplatir ces niveaux dans ce cas:

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

      col2_max  col2_min  col2_std  col3_size  col3_std  col3_mean  col3_max
col1                                                                        
1            5         1  1.581139          5  1.581139         -3        -1
2            9         0  3.535534          5  3.535534         -6         0

Pour les opérations comme groupby().summarize(newcolumn=max(col2 * col3)), vous pouvez toujours utiliser agg en ajoutant d'abord une nouvelle colonne avec assign.

df.assign(new_col=df.eval('col2 * col3')).groupby('col1').agg('max') 

      col2  col3  new_col
col1                     
1        5    -1       -1
2        9     0        0

Cela renvoie le maximum pour les anciennes et les nouvelles colonnes, mais comme toujours, vous pouvez le découper.

df.assign(new_col=df.eval('col2 * col3')).groupby('col1')['new_col'].agg('max')

col1
1   -1
2    0
Name: new_col, dtype: int64

Avec groupby.apply, Ce serait plus court:

df.groupby('col1').apply(lambda x: (x.col2 * x.col3).max())

col1
1   -1
2    0
dtype: int64

Cependant, groupby.apply Traite cela comme une fonction personnalisée et n'est donc pas vectorisé. Jusqu'à présent, les fonctions que nous avons passées à agg ('min', 'max', 'min', 'size' etc.) sont vectorisées et ce sont des alias pour ces fonctions optimisées. Vous pouvez remplacer df.groupby('col1').agg('min') par df.groupby('col1').agg(min), df.groupby('col1').agg(np.min) ou df.groupby('col1').min() et ils exécuteront tous la même fonction. Vous ne verrez pas la même efficacité lorsque vous utilisez des fonctions personnalisées.

Enfin, à partir de la version 0.20, agg peut être utilisé directement sur les DataFrames, sans avoir à grouper au préalable. Voir les exemples ici .

48
ayhan

Vérifiez la comparaison côte à côte donnée par la documentation Pandas ici: http://pandas.pydata.org/pandas-docs/stable/comparison_with_r.html#grouping-and -sommaire

R's dplyr

gdf <- group_by(df, col1)
summarise(gdf, avg=mean(col1, na.rm=TRUE))  

Pandas

gdf = df.groupby('col1')
df.groupby('col1').agg({'col1': 'mean'})
1
Puriney