92 votes

Fonction d'agrégation de DataFrame Pandas utilisant plusieurs colonnes

Existe-t-il un moyen d'écrire une fonction d'agrégation telle qu'elle est utilisée dans le document DataFrame.agg qui aurait accès à plus d'une colonne des données qui sont agrégées ? Les cas d'utilisation typiques sont les fonctions de moyenne pondérée et d'écart type pondéré.

Je voudrais pouvoir écrire quelque chose comme

def wAvg(c, w):
    return ((c * w).sum() / w.sum())

df = DataFrame(....) # df has columns c and w, i want weighted average
                     # of c using w as weight.
df.aggregate ({"c": wAvg}) # and somehow tell it to use w column as weights ...

4voto

dslack Points 560

Ce qui suit (basé sur la réponse de Wes McKinney) accomplit exactement ce que je recherchais. Je serais heureux d'apprendre s'il existe une manière plus simple de faire cela dans le cadre de pandas .

def wavg_func(datacol, weightscol):
    def wavg(group):
        dd = group[datacol]
        ww = group[weightscol] * 1.0
        return (dd * ww).sum() / ww.sum()
    return wavg

def df_wavg(df, groupbycol, weightscol):
    grouped = df.groupby(groupbycol)
    df_ret = grouped.agg({weightscol:sum})
    datacols = [cc for cc in df.columns if cc not in [groupbycol, weightscol]]
    for dcol in datacols:
        try:
            wavg_f = wavg_func(dcol, weightscol)
            df_ret[dcol] = grouped.apply(wavg_f)
        except TypeError:  # handle non-numeric columns
            df_ret[dcol] = grouped.agg({dcol:min})
    return df_ret

La fonction df_wavg() renvoie un cadre de données qui est groupé par la colonne "groupby", et qui renvoie la somme des poids pour la colonne weights. Les autres colonnes sont soit les moyennes pondérées, soit, si elles ne sont pas numériques, la colonne min() est utilisée pour l'agrégation.

3voto

Iyar Lin Points 157

Voici une solution qui présente les avantages suivants :

  1. Vous n'avez pas besoin de définir une fonction à l'avance
  2. Vous pouvez l'utiliser à l'intérieur d'un tuyau (puisqu'il utilise un lambda).
  3. Vous pouvez nommer la colonne résultante

:

df.groupby('group')
  .apply(lambda x: pd.Series({
'weighted_average': np.average(x.data, weights = x.weights)})

Vous pouvez également utiliser le même code pour effectuer plusieurs agrégations :

df.groupby('group')
  .apply(lambda x: pd.Series({
'weighted_average': np.average(x.data, weights = x.weights), 
'regular_average': np.average(x.data)}))

0voto

Mykola Zotko Points 1929

Vous pouvez mettre en œuvre cette fonction de la manière suivante :

(df['c'] * df['w']).groupby(df['groups']).sum() / df.groupby('groups')['w'].sum()

Par exemple :

df = pd.DataFrame({'groups': [1, 1, 2, 2], 'c': [3, 3, 4, 4], 'w': [5, 5, 6, 6]})
(df['c'] * df['w']).groupby(df['groups']).sum() / df.groupby('groups')['w'].sum()

Résultat :

groups
1    3.0
2    4.0
dtype: float64

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X