2012-06-08 17 views
49

क्या DataFrame.agg विधि में उपयोग किए गए एकत्रीकरण फ़ंक्शन को लिखने का कोई तरीका है, जिसके पास समेकित डेटा के एक से अधिक कॉलम तक पहुंच होगी? विशिष्ट उपयोग के मामलों को औसत, भारित मानक विचलन funcs भारित किया जाएगा।एकाधिक कॉलम का उपयोग करके पांडस डेटाफ्रेम कुल फ़ंक्शन

मैं

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 की गणना करना चाहते हैं, उदा। डीएफ ['भार'] को छोड़कर सबकुछ? – CPBL

+2

@Wes, क्या कोई भी तरीका 'agg() 'और' lpda' 'np.average (... weights = ...)' के आसपास बनाया गया था, या भारित के लिए पांडा में कोई भी नया मूल समर्थन इसका मतलब है कि यह पोस्ट पहली बार दिखाई दिया था? –

3

निम्नलिखित (वेस मैककिनी के उत्तर पर आधारित) जो मैं ढूंढ रहा था वह पूरा करता है। मुझे यह जानने में खुशी होगी कि 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() एक dataframe कि "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

@ सैंटन, उत्तर के लिए धन्यवाद। क्या आप अपने समाधान का उदाहरण दे सकते हैं? आपके समाधान का उपयोग करने का प्रयास करते समय मुझे '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 (...) पर प्रदर्शन लाभ। लागू (...) है के बारे में 100x (!)

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 वस्तु से एकत्रित मूल्यों के किसी भी संख्या वापस जाने के लिए संभव है। बस, एक श्रृंखला वापस करें और सूचकांक मान नए कॉलम नाम बन जाएंगे।

की एक त्वरित उदाहरण देखते हैं:

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 को दे दिया जाएगा एक कस्टम फ़ंक्शन निर्धारित करें। यह स्पष्ट रूप से डेटाफ्रेम स्वीकार करता है - जिसका अर्थ है data पैरामीटर डेटाफ्रेम है। सूचना है कि यह कैसे एकाधिक स्तंभों का उपयोग करता है, जो 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 

आप भारित precalculating करके बेहतर प्रदर्शन प्राप्त कर सकते हैं अन्य उत्तरों में समझाए गए नए डेटाफ्रेम कॉलम में कुल मिलाकर apply पूरी तरह से उपयोग करने से बचें।

संबंधित मुद्दे