2012-06-08 85 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

是;使用.apply(...)函數,該函數將在每個子DataFrame上調用。例如:

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 ['權重']之外的所有內容? – CPBL

+2

@Wes,有沒有什麼辦法可以一次用'AGG()'和'周圍建np.average一個'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 

df_wavg()返回年代由「GROUPBY」列編組一個數據幀的功能,並返回該權重的權重列的總和。其他列或者是加權平均值,或者如果是非數字值,則使用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

@santon,謝謝你的回答。你能舉一個你的解決方案的例子嗎?在嘗試使用您的解決方案時出現'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

它可以從一個GROUPBY物體apply返回任何數目的聚合值。簡單地說,返回一個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

相關問題