एक से अधिक समूहों के लिए कई कार्य लागू करें


221

डॉक्स कैसे कुंजी के रूप में उत्पादन स्तंभ नाम के साथ एक dict का उपयोग कर एक समय में एक GroupBy वस्तु पर कई कार्यों को लागू करने के दिखाने:

In [563]: grouped['D'].agg({'result1' : np.sum,
   .....:                   'result2' : np.mean})
   .....:
Out[563]: 
      result2   result1
A                      
bar -0.579846 -1.739537
foo -0.280588 -1.402938

हालांकि, यह केवल एक श्रृंखला समूह वस्तु पर काम करता है। और जब एक तानाशाह को समान रूप से एक Groupby DataFrame में पारित किया जाता है, तो यह अपेक्षा करता है कि कॉलम उन नामों के लिए होगा जिन्हें फ़ंक्शन पर लागू किया जाएगा।

मैं जो करना चाहता हूं वह कई कॉलमों के लिए कई फ़ंक्शन लागू करता है (लेकिन कुछ कॉलम कई बार संचालित होंगे)। साथ ही, कुछ फ़ंक्शन ग्रुपबी ऑब्जेक्ट में अन्य कॉलम पर निर्भर होंगे (जैसे कि समिफ़ फ़ंक्शंस)। मेरा वर्तमान समाधान स्तंभ द्वारा स्तंभ पर जाना है, और ऊपर दिए गए कोड की तरह कुछ करना, अन्य पंक्तियों पर निर्भर करने वाले कार्यों के लिए लंबोदा का उपयोग करना। लेकिन इसमें एक लंबा समय लग रहा है, (मुझे लगता है कि एक समूह वस्तु के माध्यम से पुनरावृत्त होने में लंबा समय लगता है)। मुझे इसे बदलना होगा ताकि मैं एक ही रन में पूरे ग्रुपबी ऑब्जेक्ट के माध्यम से पुनरावृत्त कर दूं, लेकिन मैं सोच रहा हूं कि क्या यह कुछ साफ तरीके से करने के लिए पांडा में एक तरह से बनाया गया है।

उदाहरण के लिए, मैंने कुछ ऐसा करने की कोशिश की है

grouped.agg({'C_sum' : lambda x: x['C'].sum(),
             'C_std': lambda x: x['C'].std(),
             'D_sum' : lambda x: x['D'].sum()},
             'D_sumifC3': lambda x: x['D'][x['C'] == 3].sum(), ...)

लेकिन जैसा कि मुझे उम्मीद थी कि मुझे KeyError मिलती है (चूँकि aggDataFrame से चाबियों को एक कॉलम कहा जाता है)।

क्या कोई ऐसा तरीका है जो मैं करना चाहता हूं, या एक संभावना है कि इस कार्यक्षमता को जोड़ा जा सकता है, या क्या मुझे केवल ग्रुपबी के माध्यम से मैन्युअल रूप से पुनरावृति करने की आवश्यकता होगी?

धन्यवाद


2
यदि आप 2017+ में इस प्रश्न पर आ रहे हैं, तो कृपया नीचे दिए गए उत्तर को एक साथ कई कॉलमों को एकत्र करने के लिए मुहावरेदार तरीके से देखें। वर्तमान में चयनित उत्तर में इसमें कई पदावनतियां हैं, अर्थात आप किसी समूह के परिणाम में कॉलम का नाम बदलने के लिए शब्दकोशों का उपयोग नहीं कर सकते हैं।
टेड पेट्रोउ

जवाबों:


282

वर्तमान में स्वीकृत उत्तर का दूसरा भाग पुराना है और इसमें दो पदावनतियां हैं। सबसे पहले और सबसे महत्वपूर्ण, आप अब शब्दकोश के शब्दकोश को aggग्रुपबी विधि से पारित नहीं कर सकते । दूसरा, कभी उपयोग न करें .ix

यदि आप एक ही समय में दो अलग-अलग स्तंभों के साथ काम करने की इच्छा रखते हैं, तो मैं उस applyपद्धति का उपयोग करने का सुझाव दूंगा, जो लागू फ़ंक्शन के लिए एक DataFrame गुजरता है। चलो ऊपर से एक के रूप में एक समान डेटाफ्रेम का उपयोग करें

df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]
df

          a         b         c         d  group
0  0.418500  0.030955  0.874869  0.145641      0
1  0.446069  0.901153  0.095052  0.487040      0
2  0.843026  0.936169  0.926090  0.041722      1
3  0.635846  0.439175  0.828787  0.714123      1

कॉलम नामों से एकत्रीकरण कार्यों के लिए मैप किया गया एक शब्दकोश अभी भी एक एकत्रीकरण प्रदर्शन करने का एक अच्छा तरीका है।

df.groupby('group').agg({'a':['sum', 'max'], 
                         'b':'mean', 
                         'c':'sum', 
                         'd': lambda x: x.max() - x.min()})

              a                   b         c         d
            sum       max      mean       sum  <lambda>
group                                                  
0      0.864569  0.446069  0.466054  0.969921  0.341399
1      1.478872  0.843026  0.687672  1.754877  0.672401

यदि आप उस बदसूरत लैम्ब्डा कॉलम नाम को पसंद नहीं करते हैं, तो आप एक सामान्य फ़ंक्शन का उपयोग कर सकते हैं और __name__इस तरह विशेष विशेषता के लिए एक कस्टम नाम की आपूर्ति कर सकते हैं :

def max_min(x):
    return x.max() - x.min()

max_min.__name__ = 'Max minus Min'

df.groupby('group').agg({'a':['sum', 'max'], 
                         'b':'mean', 
                         'c':'sum', 
                         'd': max_min})

              a                   b         c             d
            sum       max      mean       sum Max minus Min
group                                                      
0      0.864569  0.446069  0.466054  0.969921      0.341399
1      1.478872  0.843026  0.687672  1.754877      0.672401

applyएक श्रृंखला का उपयोग करना और वापस करना

अब, यदि आपके पास कई कॉलम थे, जिन्हें आपस में बातचीत करने की आवश्यकता थी, तो आप उपयोग नहीं कर सकते हैं agg, जो कुल मिलाकर एग्रीगेटिंग फ़ंक्शन के लिए एक श्रृंखला है। जब applyएक DataFrame फ़ंक्शन के रूप में पूरे समूह का उपयोग कर रहा है।

मैं एक एकल कस्टम फ़ंक्शन बनाने की सलाह देता हूं जो सभी एकत्रीकरणों की एक श्रृंखला देता है। नए कॉलम के लेबल के रूप में श्रृंखला सूचकांक का उपयोग करें:

def f(x):
    d = {}
    d['a_sum'] = x['a'].sum()
    d['a_max'] = x['a'].max()
    d['b_mean'] = x['b'].mean()
    d['c_d_prodsum'] = (x['c'] * x['d']).sum()
    return pd.Series(d, index=['a_sum', 'a_max', 'b_mean', 'c_d_prodsum'])

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

         a_sum     a_max    b_mean  c_d_prodsum
group                                           
0      0.864569  0.446069  0.466054     0.173711
1      1.478872  0.843026  0.687672     0.630494

यदि आप MultiIndexes के साथ प्यार में हैं, तो आप अभी भी इस तरह से एक श्रृंखला वापस कर सकते हैं:

    def f_mi(x):
        d = []
        d.append(x['a'].sum())
        d.append(x['a'].max())
        d.append(x['b'].mean())
        d.append((x['c'] * x['d']).sum())
        return pd.Series(d, index=[['a', 'a', 'b', 'c_d'], 
                                   ['sum', 'max', 'mean', 'prodsum']])

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

              a                   b       c_d
            sum       max      mean   prodsum
group                                        
0      0.864569  0.446069  0.466054  0.173711
1      1.478872  0.843026  0.687672  0.630494

3
मुझे एक फ़ंक्शन का उपयोग करने का पैटर्न पसंद है जो एक श्रृंखला लौटाता है। बहुत साफ़।
स्टीफन मैकएयर

2
यह एकमात्र तरीका है जो मैंने एक डेटाफ्रेम को कई कॉलम इनपुटों के माध्यम से सिमुलटेनोसली (ऊपर c_d उदाहरण) को एकत्र करने के लिए पाया है
ब्लेक

2
मैं परिणामों से उलझन में हूँ, aसमूह के भीतर का योग 0लेना यह नहीं होना चाहिए 0.418500 + 0.446069 = 0.864569? वही अन्य कोशिकाओं के लिए सही है, संख्याओं को जोड़ने के लिए प्रकट नहीं होता है। क्या यह थोड़ा अलग अंतर्निहित डेटाफ़्रेम बाद के उदाहरणों में इस्तेमाल किया जा सकता है?
स्लैकलाइन

मैं अक्सर रिकॉर्ड की संख्या देखने के लिए एक समूह के साथ .size () का उपयोग करता हूं। क्या एग का उपयोग करने का एक तरीका है: तानाशाही विधि? मैं समझता हूं कि मैं एक विशेष क्षेत्र की गणना कर सकता हूं, लेकिन मेरी प्राथमिकता क्षेत्र के स्वतंत्र होने के लिए होगी।
क्रिस डेकर

1
@ स्लैकलाइन हाँ। मैं सिर्फ यह परीक्षण किया है और यह ठीक काम करता है। टेड ने फ्रेम को कुछ अलग समय में बनाया होगा और चूंकि यह यादृच्छिक संख्या पीढ़ी के माध्यम से बनाया गया था, डीएफ डेटा वास्तव में डेटा उत्पन्न करने के लिए गणना में प्रयुक्त एक से भिन्न था
लुकास एच

166

पहले भाग के लिए आप कुंजियों के लिए स्तंभ नामों का मानदंड और मूल्यों के लिए कार्यों की एक सूची पारित कर सकते हैं:

In [28]: df
Out[28]:
          A         B         C         D         E  GRP
0  0.395670  0.219560  0.600644  0.613445  0.242893    0
1  0.323911  0.464584  0.107215  0.204072  0.927325    0
2  0.321358  0.076037  0.166946  0.439661  0.914612    1
3  0.133466  0.447946  0.014815  0.130781  0.268290    1

In [26]: f = {'A':['sum','mean'], 'B':['prod']}

In [27]: df.groupby('GRP').agg(f)
Out[27]:
            A                   B
          sum      mean      prod
GRP
0    0.719580  0.359790  0.102004
1    0.454824  0.227412  0.034060

अद्यतन 1:

क्योंकि कुल फ़ंक्शन श्रृंखला पर काम करता है, अन्य स्तंभ नामों के संदर्भ खो जाते हैं। इसके आस-पास जाने के लिए, आप लैम्बडा फ़ंक्शन के भीतर समूह सूचकांकों का उपयोग करके पूर्ण डेटाफ़्रेम और इंडेक्स का संदर्भ दे सकते हैं।

यहाँ एक हैकर वर्कअराउंड:

In [67]: f = {'A':['sum','mean'], 'B':['prod'], 'D': lambda g: df.loc[g.index].E.sum()}

In [69]: df.groupby('GRP').agg(f)
Out[69]:
            A                   B         D
          sum      mean      prod  <lambda>
GRP
0    0.719580  0.359790  0.102004  1.170219
1    0.454824  0.227412  0.034060  1.182901

यहाँ, परिणामी 'D' कॉलम 'ई' मानों से बना है।

अद्यतन 2:

यहाँ एक तरीका है जो मुझे लगता है कि आप जो भी पूछेंगे वह सब कुछ करेंगे। पहले एक कस्टम लंबो फ़ंक्शन करें। नीचे, जी समूह का संदर्भ देता है। जब एकत्रीकरण, जी एक श्रृंखला होगी। Df से वर्तमान समूह g.indexका df.ix[]चयन करने के लिए पासिंग । मैं तब परीक्षण करता हूं यदि स्तंभ C 0.5 से कम है। लौटी बूलियन श्रृंखला पारित की जाती है g[]जो मानदंडों को पूरा करने वाली केवल उन पंक्तियों का चयन करती है।

In [95]: cust = lambda g: g[df.loc[g.index]['C'] < 0.5].sum()

In [96]: f = {'A':['sum','mean'], 'B':['prod'], 'D': {'my name': cust}}

In [97]: df.groupby('GRP').agg(f)
Out[97]:
            A                   B         D
          sum      mean      prod   my name
GRP
0    0.719580  0.359790  0.102004  0.204072
1    0.454824  0.227412  0.034060  0.570441

दिलचस्प है, मैं {funcname: func}अपने कस्टम नामों को रखने के लिए सूचियों के बजाय मूल्यों के रूप में एक तानाशाही पारित कर सकता हूं । लेकिन किसी भी मामले में मैं lambdaअन्य कॉलम (जैसे lambda x: x['D'][x['C'] < 3].sum()ऊपर: "KeyError: 'D'") का उपयोग नहीं कर सकता । किसी भी विचार अगर यह संभव है?
दाढ़ी

मैं ठीक वैसा ही करने की कोशिश कर रहा हूं, और मुझे त्रुटि मिलती हैKeyError: 'D'
ज़ेलज़नी 7

कूल, मुझे इसके साथ काम करने के लिए मिला df['A'].ix[g.index][df['C'] < 0].sum()। यह बहुत गड़बड़ हो रहा है, हालांकि - मुझे लगता है कि पठनीयता के लिए मैनुअल लूपिंग बेहतर हो सकता है, साथ ही मुझे यकीन नहीं है कि यह aggतर्क में मेरा पसंदीदा नाम देने के लिए एक तरीका है (इसके बजाय <lambda>)। मैं आशा करता हूँ कि कोई और अधिक सरल तरीका जान सकता है ...
दाढ़ी

3
आप स्तंभ मान के लिए एक तानाशाही पास कर सकते हैं {'D': {'my name':lambda function}}और यह आंतरिक ताना कुंजी को स्तंभ नाम बना देगा।
ज़ेलज़नी 7

1
मेरा मानना ​​है कि पांडा अब एक ग्रुप
बाय

22

टेड पेट्रो के जवाब के विकल्प के रूप में (ज्यादातर सौंदर्यशास्त्र पर), मैंने पाया कि मैंने थोड़ी अधिक कॉम्पैक्ट लिस्टिंग को प्राथमिकता दी है। कृपया इसे स्वीकार करने पर विचार न करें, यह टेड के उत्तर, प्लस कोड / डेटा पर एक बहुत अधिक विस्तृत टिप्पणी है। पायथन / पांडा मेरा पहला / सर्वश्रेष्ठ नहीं है, लेकिन मैंने इसे अच्छी तरह से पढ़ने के लिए पाया:

df.groupby('group') \
  .apply(lambda x: pd.Series({
      'a_sum'       : x['a'].sum(),
      'a_max'       : x['a'].max(),
      'b_mean'      : x['b'].mean(),
      'c_d_prodsum' : (x['c'] * x['d']).sum()
  })
)

          a_sum     a_max    b_mean  c_d_prodsum
group                                           
0      0.530559  0.374540  0.553354     0.488525
1      1.433558  0.832443  0.460206     0.053313

मुझे यह dplyrपाइप और data.tableजंजीर कमांड की अधिक याद दिलाता है । यह कहने के लिए नहीं कि वे बेहतर हैं, बस मेरे लिए अधिक परिचित हैं। (मैं निश्चित रूप से शक्ति को पहचानता हूं और कई के लिए, इस defप्रकार के कार्यों के लिए अधिक औपचारिक कार्यों का उपयोग करने की प्राथमिकता है । यह सिर्फ एक विकल्प है, जरूरी नहीं कि बेहतर हो।)


मैंने टेड के समान डेटा उत्पन्न किया, मैं प्रजनन के लिए एक बीज जोड़ूंगा।

import numpy as np
np.random.seed(42)
df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]
df

          a         b         c         d  group
0  0.374540  0.950714  0.731994  0.598658      0
1  0.156019  0.155995  0.058084  0.866176      0
2  0.601115  0.708073  0.020584  0.969910      1
3  0.832443  0.212339  0.181825  0.183405      1

2
मुझे यह जवाब सबसे ज्यादा पसंद है। यह R.
रेन्हुई

18

Pandas >= 0.25.0, नाम एकत्रीकरण

पांडा संस्करण 0.25.0या उच्चतर के बाद से , हम शब्दकोश आधारित एकत्रीकरण और नामकरण से दूर जा रहे हैं, और नामित एकत्रीकरण की ओर बढ़ रहे हैं जो एक को स्वीकार करता है tuple। अब हम एक साथ अधिक जानकारीपूर्ण स्तंभ नाम के साथ + नाम जोड़ सकते हैं:

उदाहरण :

df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]

          a         b         c         d  group
0  0.521279  0.914988  0.054057  0.125668      0
1  0.426058  0.828890  0.784093  0.446211      0
2  0.363136  0.843751  0.184967  0.467351      1
3  0.241012  0.470053  0.358018  0.525032      1

GroupBy.aggनामित एकत्रीकरण के साथ आवेदन करें :

df.groupby('group').agg(
             a_sum=('a', 'sum'),
             a_mean=('a', 'mean'),
             b_mean=('b', 'mean'),
             c_sum=('c', 'sum'),
             d_range=('d', lambda x: x.max() - x.min())
)

          a_sum    a_mean    b_mean     c_sum   d_range
group                                                  
0      0.947337  0.473668  0.871939  0.838150  0.320543
1      0.604149  0.302074  0.656902  0.542985  0.057681

मुझे इन नामित एकत्रीकरण पसंद हैं लेकिन मैं यह नहीं देख सकता कि हम उन्हें कई स्तंभों के साथ कैसे उपयोग करने वाले हैं?
साइमन वुडहेड

अच्छा सवाल, यह पता नहीं लगा सका, यह संभव है (अभी तक)। मैंने इसके लिए एक टिकट खोला । मेरा सवाल रखेंगे और आपने अपडेट किया। @SimonWoodhead को इंगित करने के लिए धन्यवाद
Erfan

4

संस्करण में नया 0.25.0।

आउटपुट कॉलम के नामों पर नियंत्रण के साथ कॉलम-विशिष्ट एकत्रीकरण का समर्थन करने के लिए, पांडा समूहबाग () में विशेष वाक्यविन्यास को स्वीकार करता है , जिसे "नामित एकत्रीकरण" के रूप में जाना जाता है , जहां

  • कीवर्ड आउटपुट कॉलम नाम हैं
  • मान tuples हैं जिसका पहला तत्व चयन करने के लिए कॉलम है और दूसरा तत्व उस कॉलम पर लागू करने के लिए एकत्रीकरण है। पंडों ने पंडों को प्रदान किया। नेमएड नाम का नाम खेतों के साथ ['कॉलम', 'एग्गफंक'] यह स्पष्ट करने के लिए कि तर्क क्या हैं। हमेशा की तरह, एकत्रीकरण एक कॉल करने योग्य या एक स्ट्रिंग उपनाम हो सकता है।
    In [79]: animals = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
       ....:                         'height': [9.1, 6.0, 9.5, 34.0],
       ....:                         'weight': [7.9, 7.5, 9.9, 198.0]})
       ....: 

    In [80]: animals
    Out[80]: 
      kind  height  weight
    0  cat     9.1     7.9
    1  dog     6.0     7.5
    2  cat     9.5     9.9
    3  dog    34.0   198.0

    In [81]: animals.groupby("kind").agg(
       ....:     min_height=pd.NamedAgg(column='height', aggfunc='min'),
       ....:     max_height=pd.NamedAgg(column='height', aggfunc='max'),
       ....:     average_weight=pd.NamedAgg(column='weight', aggfunc=np.mean),
       ....: )
       ....: 
    Out[81]: 
          min_height  max_height  average_weight
    kind                                        
    cat          9.1         9.5            8.90
    dog          6.0        34.0          102.75

pandas.NamedAgg सिर्फ एक नामांकित व्यक्ति है। सादा टुपल्स की भी अनुमति है।

    In [82]: animals.groupby("kind").agg(
       ....:     min_height=('height', 'min'),
       ....:     max_height=('height', 'max'),
       ....:     average_weight=('weight', np.mean),
       ....: )
       ....: 
    Out[82]: 
          min_height  max_height  average_weight
    kind                                        
    cat          9.1         9.5            8.90
    dog          6.0        34.0          102.75

अतिरिक्त कीवर्ड तर्क एकत्रीकरण फ़ंक्शन के माध्यम से पारित नहीं होते हैं। केवल (स्तंभ, एग्गफंक) के जोड़े को ** कंवर के रूप में पारित किया जाना चाहिए। यदि आपके एकत्रीकरण कार्यों को अतिरिक्त तर्कों की आवश्यकता होती है, तो उन्हें फंक्शनल टूलपार्टियल () के साथ आंशिक रूप से लागू करें।

नामांकित एकत्रीकरण श्रृंखला समूहन एकत्रीकरण के लिए भी मान्य है। इस मामले में कोई कॉलम चयन नहीं है, इसलिए मान केवल कार्य हैं।

    In [84]: animals.groupby("kind").height.agg(
       ....:     min_height='min',
       ....:     max_height='max',
       ....: )
       ....: 
    Out[84]: 
          min_height  max_height
    kind                        
    cat          9.1         9.5
    dog          6.0        34.0

3

टेड का जवाब आश्चर्यजनक है। अगर किसी को दिलचस्पी है, तो मैंने उस छोटे संस्करण का उपयोग किया। उपयोगी है जब आप एक एकत्रीकरण की तलाश कर रहे हैं जो कई स्तंभों के मूल्यों पर निर्भर करता है:

एक डेटाफ़्रेम बनाएँ

df=pd.DataFrame({'a': [1,2,3,4,5,6], 'b': [1,1,0,1,1,0], 'c': ['x','x','y','y','z','z']})


   a  b  c
0  1  1  x
1  2  1  x
2  3  0  y
3  4  1  y
4  5  1  z
5  6  0  z

(कई कॉलम का उपयोग करके) के साथ समूहीकरण और समुच्चय

df.groupby('c').apply(lambda x: x['a'][(x['a']>1) & (x['b']==1)].mean())

c
x    2.0
y    4.0
z    5.0

समूहीकरण (कई स्तंभों का उपयोग करके) के साथ समूहीकरण और संयोजन करना

मुझे यह दृष्टिकोण पसंद है क्योंकि मैं अभी भी कुल का उपयोग कर सकता हूं। शायद लोग मुझे बताएंगे कि समूहों पर एकत्रीकरण करते समय कई कॉलमों पर आवेदन करने की आवश्यकता क्यों होती है।

यह अब स्पष्ट लगता है, लेकिन जब तक आप समूह के बाद सीधे ब्याज के कॉलम का चयन नहीं करते हैं , तब तक आपके एकत्रीकरण फ़ंक्शन के भीतर से डेटाफ्रेम के सभी स्तंभों तक पहुंच होगी।

केवल चयनित कॉलम तक पहुंच

df.groupby('c')['a'].aggregate(lambda x: x[x>1].mean())

चयन के बाद से सभी स्तंभों तक पहुंच सभी जादू के बाद है

df.groupby('c').aggregate(lambda x: x[(x['a']>1) & (x['b']==1)].mean())['a']

या इसी तरह

df.groupby('c').aggregate(lambda x: x['a'][(x['a']>1) & (x['b']==1)].mean())

आशा है कि ये आपकी मदद करेगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.