2012-06-08 61 views
49

DataFrame.aggメソッドで使用されている集計関数を作成する方法があります。集計されるデータの複数の列にアクセスできますか?典型的な使用事例は、加重平均、加重標準偏差関数である。複数の列を使用するPandas DataFrame集計関数

私ははい

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

答えて

68

ような何かを書くことができるようにしたいと思います。それぞれDataFrameで呼び出される.apply(...)関数を使用します。例:

grouped = df.groupby(keys) 

def wavg(group): 
    d = group['data'] 
    w = group['weights'] 
    return (d * w).sum()/w.sum() 

grouped.apply(wavg) 
+0

(1)重みの列を作成する、(2)観測値を重みで正規化する、(3)重み付き観測値とグループ化した観測値の合計を計算する(4)観測の加重和を加重和で正規化する。 – kalu

+3

wavgの多くの変数(列)を計算する場合はどうでしょうか? df ['weights']以外はすべて? – CPBL

+2

@Wesは、 'agg()'と 'np.average(... weights = ...)'の周りに構築された 'lambda'でこれを行う方法はありますか、この投稿が最初に登場したので意味しますか? –

3

以下は(Wes McKinneyの回答に基づいて)私が探していたものを正確に達成しています。 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 

「GROUPBY」カラムによってグループ化されだデータフレームを返すdf_wavg()機能、及びその重み列の重みの合計を返します。他の列は加重平均か、非数値の場合はmin()関数が集計に使用されます。

3

私はこれをたくさん行うと、次は非常に便利が見つかりました:

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) 

これはdf内のすべての数値列の加重平均を計算し、数値以外のものをドロップします。

+0

これは速いです!よくやった! –

+0

複数の列がある場合、これは本当に甘いです。ニース! – Chris

+0

@サンタン、答えに感謝します。あなたのソリューションの例を教えてください。あなたのソリューションを使用しようとしているときに、「KeyError: 'COUNT」というエラーが表示されました。 – Allen

1

これを達成するのはgroupby(...).apply(...)です。ここで私が常に使用する解決策があります(基本的にカルルのロジックを使用しています)。

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 
+0

あなたは非実行者と言うとき。違いはいくらですか?それを測定しましたか? – Bouncner

1

私のソリューションは、ナサニエルのソリューションに似ているだけで、それは単一の列のためだと私は法外遅くなる可能性がありたびに、データフレーム全体をディープコピーしないでください。ソリューションGROUPBY(...)を超える性能向上。適用(...)は約100倍(!)

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 
0

applyでGROUPBYオブジェクトから集約された任意の数の値を返すことが可能です。単純にSeriesを返し、インデックス値が新しい列名になります。

はのは、簡単な例を見てみましょう:

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 

applyに渡されるカスタム関数を定義します。それは暗黙的にDataFrameを受け入れます - つまり、dataパラメータがDataFrameであることを意味します。 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) 

当社のカスタム関数でGROUPBY applyメソッドを呼び出します。

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

     d1_wa d2_wa 
group    
a  9.0 2.2 
b  58.0 13.2 

あなたは、重み付けをあらかじめ計算しておくことによって、より良いパフォーマンスを得ることができます他の回答で説明したように新しいDataFrame列に合計し、applyを完全に使用しないでください。

関連する問題