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:
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?
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 .
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'})