J'essaie d'utiliser une fonction personnalisée avec groupby
dans les pandas. Je trouve que l'utilisation de apply
me permet de le faire de la manière suivante:
(Un exemple qui calcule une nouvelle moyenne à partir de deux groupes)
df.groupby(['pool']).agg({'count': sum, ['count', 'mean']: apply(newAvg)})
Est-il possible d'intégrer cela dans une fonction agg
? Dans ce sens:
import pandas as pd def newAvg(x): x['cm'] = x['count']*x['mean'] sCount = x['count'].sum() sMean = x['cm'].sum() return sMean/sCount data = [['A', 4, 2.5], ['A', 3, 6], ['B', 4, 9.5], ['B', 3, 13]] df = pd.DataFrame(data, columns=['pool', 'count', 'mean']) df_gb = df.groupby(['pool']).apply(newAvg)
5 Réponses :
Fonction agg
travailler avec chaque colonne séparément, donc la solution possible est de créer d'abord la colonne cm
avec assign
puis agréger somme
, diviser en dernier chaque colonne:
df_gb = df.assign(cm=df['count']*df['mean']).groupby('pool')['cm','count'].sum() print (df_gb) cm count pool A 28.0 7 B 77.0 7 out = df_gb.pop('cm') / df_gb.pop('count') print (out) pool A 4.0 B 11.0 dtype: float64
Un dictionnaire avec agg
est utilisé pour effectuer des calculs séparés pour chaque série. Pour votre problème, je suggère pd.concat
:
g = df.groupby('pool') res = pd.concat([g['count'].sum(), g.apply(newAvg).rename('newAvg')], axis=1) print(res) # count newAvg # pool # A 7 4.0 # B 7 11.0
Ce n'est pas la solution la plus efficace car votre fonction newAvg
effectue des calculs qui peut être effectué sur l'ensemble de la trame de données au départ, mais il prend en charge les calculs prédéfinis arbitraires.
IIUC
df.groupby(['pool']).apply(lambda x : pd.Series({'count':sum(x['count']),'newavg':newAvg(x)})) Out[58]: count newavg pool A 7.0 4.0 B 7.0 11.0
J'aime beaucoup ça. Merci à tout le monde
@Christian heureux codage
Utilisez assign
avec eval
:
pool cm count AggCol 0 A 28.0 7 4.0 1 B 77.0 7 11.0
Output:
df.assign(cm=df['count']*df['mean'])\ .groupby('pool', as_index=False)['cm','count'].sum()\ .eval('AggCol = cm / count')
p >
Si vous calculez une moyenne pondérée, vous pouvez le faire facilement en utilisant les fonctions agg
et NumPy np.average
. Lisez simplement la série pour la colonne «moyenne»:
df_gb2 = df.groupby(['pool']).agg(newAvg)['mean'] print(df_gb2) # pool # A 4.0 # B 11.0 # Name: mean, dtype: float64
Vous pouvez également le faire en utilisant votre fonction newAvg
, bien que cela produise des avertissements:
XXX
Si vous souhaitez utiliser la fonction newAvg
, vous pouvez la redéfinir pour éviter de travailler sur des copies:
def newAvg(x): cm = x['count']*x['mean'] sCount = x['count'].sum() sMean = cm.sum() return sMean/sCount
Avec cette modification, vous obtenez votre résultat attendu:
df_gb2 = df.groupby(['pool']).agg(newAvg)['mean']