web-dev-qa-db-fra.com

Groupby moyenne pondérée et somme en pandas dataframe

J'ai une trame de données,

    Out[78]: 
   contract month year  buys  adjusted_lots    price
0         W     Z    5  Sell             -5   554.85
1         C     Z    5  Sell             -3   424.50
2         C     Z    5  Sell             -2   424.00
3         C     Z    5  Sell             -2   423.75
4         C     Z    5  Sell             -3   423.50
5         C     Z    5  Sell             -2   425.50
6         C     Z    5  Sell             -3   425.25
7         C     Z    5  Sell             -2   426.00
8         C     Z    5  Sell             -2   426.75
9        CC     U    5   Buy              5  3328.00
10       SB     V    5   Buy              5    11.65
11       SB     V    5   Buy              5    11.64
12       SB     V    5   Buy              2    11.60

J'ai besoin d'une somme de lots_ajustés, prix moyen pondéré, de prix et lots_ajustés, regroupés par toutes les autres colonnes, c'est-à-dire. regroupés par (contrat, mois, année et achats)

Une solution similaire sur R a été obtenue en suivant le code, en utilisant dplyr, mais incapable de faire de même dans les pandas.

> newdf = df %>%
  select ( contract , month , year , buys , adjusted_lots , price ) %>%
  group_by( contract , month , year ,  buys) %>%
  summarise(qty = sum( adjusted_lots) , avgpx = weighted.mean(x = price , w = adjusted_lots) , comdty = "Comdty" )

> newdf
Source: local data frame [4 x 6]

  contract month year comdty qty     avgpx
1        C     Z    5 Comdty -19  424.8289
2       CC     U    5 Comdty   5 3328.0000
3       SB     V    5 Comdty  12   11.6375
4        W     Z    5 Comdty  -5  554.8500

est-ce la même chose possible par groupby ou toute autre solution?

32
samsri

Pour passer plusieurs fonctions à un objet groupby, vous devez passer un dictionnaire avec les fonctions d'agrégation correspondant aux colonnes:

# Define a lambda function to compute the weighted mean:
wm = lambda x: np.average(x, weights=df.loc[x.index, "adjusted_lots"])

# Define a dictionary with the functions to apply for a given column:
f = {'adjusted_lots': ['sum'], 'price': {'weighted_mean' : wm} }

# Groupby and aggregate with your dictionary:
df.groupby(["contract", "month", "year", "buys"]).agg(f)

                         adjusted_lots         price
                                   sum weighted_mean
contract month year buys                            
C        Z     5    Sell           -19    424.828947
CC       U     5    Buy              5   3328.000000
SB       V     5    Buy             12     11.637500
W        Z     5    Sell            -5    554.850000

Vous pouvez en voir plus ici:

et dans une question similaire ici:

J'espère que cela t'aides

71
jrjc

Faire une moyenne pondérée par groupe (...). Appliquer (...) peut être très lent (100 fois ce qui suit). Voir ma réponse (et d'autres) sur ce fil .

def weighted_average(df,data_col,weight_col,by_col):
    df['_data_times_weight'] = df[data_col]*df[weight_col]
    df['_weight_where_notnull'] = df[weight_col]*pd.notnull(df[data_col])
    g = df.groupby(by_col)
    result = g['_data_times_weight'].sum() / g['_weight_where_notnull'].sum()
    del df['_data_times_weight'], df['_weight_where_notnull']
    return result
4
ErnestScribbler

La solution qui utilise un dict de fonctions d'agrégation sera obsolète dans une future version de pandas (version 0.22):

FutureWarning: using a dict with renaming is deprecated and will be removed in a future 
version return super(DataFrameGroupBy, self).aggregate(arg, *args, **kwargs)

Utilisez un groupe pour appliquer et renvoyer une série pour renommer les colonnes comme indiqué dans: Renommer les colonnes de résultat de Pandas agrégation ("FutureWarning: l'utilisation d'un dict avec renommage est déconseillée")

def my_agg(x):
    names = {'weighted_ave_price': (x['adjusted_lots'] * x['price']).sum()/x['adjusted_lots'].sum()}
    return pd.Series(names, index=['weighted_ave_price'])

produit le même résultat:

>df.groupby(["contract", "month", "year", "buys"]).apply(my_agg)

                          weighted_ave_price
contract month year buys                    
C        Z     5    Sell          424.828947
CC       U     5    Buy          3328.000000
SB       V     5    Buy            11.637500
W        Z     5    Sell          554.850000
1
Mark Greenwood