dataframes - python pandas merge data frame



Pandas DataFrame aggrega la funzione utilizzando più colonne (4)

È possibile restituire qualsiasi numero di valori aggregati da un oggetto groupby con apply . Semplicemente, restituisci una serie ei valori dell'indice diventeranno i nuovi nomi di colonna.

Vediamo un esempio veloce:

df = pd.DataFrame({'group':['a','a','b','b'],
                   'd1':[5,10,100,30],
                   'd2':[7,1,3,20],
                   'weights':[.2,.8, .4, .6]},
                 columns=['group', 'd1', 'd2', 'weights'])
df

  group   d1  d2  weights
0     a    5   7      0.2
1     a   10   1      0.8
2     b  100   3      0.4
3     b   30  20      0.6

Definire una funzione personalizzata che verrà passata per apply . Accetta implicitamente un DataFrame, ovvero il parametro data è un DataFrame. Si noti come utilizza più colonne, il che non è possibile con il metodo agg groupby:

def weighted_average(data):
    d = {}
    d['d1_wa'] = np.average(data['d1'], weights=data['weights'])
    d['d2_wa'] = np.average(data['d2'], weights=data['weights'])
    return pd.Series(d)

Chiama il metodo groupby apply con la nostra funzione personalizzata:

df.groupby('group').apply(weighted_average)

       d1_wa  d2_wa
group              
a        9.0    2.2
b       58.0   13.2

È possibile ottenere prestazioni migliori eseguendo il calcolo preliminare dei totali ponderati in nuove colonne DataFrame come spiegato in altre risposte ed evitando di utilizzare l' apply tutto.

https://src-bin.com

C'è un modo per scrivere una funzione di aggregazione come è usata nel metodo DataFrame.agg , che avrebbe accesso a più di una colonna dei dati che vengono aggregati? I casi d'uso tipici sarebbero la media ponderata, le funzioni di deviazione standard ponderate.

Mi piacerebbe poter scrivere qualcosa del genere

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 ...

Answer #1

Il seguente (basato sulla risposta di Wes McKinney) realizza esattamente ciò che stavo cercando. Sarei felice di sapere se c'è un modo più semplice per farlo nei 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 funzione df_wavg() restituisce un dataframe raggruppato dalla colonna "groupby" e che restituisce la somma dei pesi per la colonna dei pesi. Altre colonne sono le medie ponderate o, se non numeriche, la funzione min() viene utilizzata per l'aggregazione.


Answer #2

Lo faccio molto e ho trovato il seguente abbastanza utile:

def weighed_average(grp):
    return grp._get_numeric_data().multiply(grp['COUNT'], axis=0).sum()/grp['COUNT'].sum()
df.groupby('SOME_COL').apply(weighed_average)

Questo calcolerà la media ponderata di tutte le colonne numeriche nel df e non quelle non numeriche.


Answer #3

Realizzare questo tramite groupby(...).apply(...) è non performante. Ecco una soluzione che uso sempre (essenzialmente usando la logica di kalu).

def grouped_weighted_average(self, values, weights, *groupby_args, **groupby_kwargs):
   """
    :param values: column(s) to take the average of
    :param weights_col: column to weight on
    :param group_args: args to pass into groupby (e.g. the level you want to group on)
    :param group_kwargs: kwargs to pass into groupby
    :return: pandas.Series or pandas.DataFrame
    """

    if isinstance(values, str):
        values = [values]

    ss = []
    for value_col in values:
        df = self.copy()
        prod_name = 'prod_{v}_{w}'.format(v=value_col, w=weights)
        weights_name = 'weights_{w}'.format(w=weights)

        df[prod_name] = df[value_col] * df[weights]
        df[weights_name] = df[weights].where(~df[prod_name].isnull())
        df = df.groupby(*groupby_args, **groupby_kwargs).sum()
        s = df[prod_name] / df[weights_name]
        s.name = value_col
        ss.append(s)
    df = pd.concat(ss, axis=1) if len(ss) > 1 else ss[0]
    return df

pandas.DataFrame.grouped_weighted_average = grouped_weighted_average




pandas