3
votes

pandas groupby fonction personnalisée d'agrégation avec plusieurs colonnes

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)


0 commentaires

5 Réponses :


3
votes

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


0 commentaires

2
votes

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.


0 commentaires

3
votes

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


2 commentaires

J'aime beaucoup ça. Merci à tout le monde


@Christian heureux codage



3
votes

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 >


0 commentaires

1
votes

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']


0 commentaires