Birden çok gruptaki sütuna birden çok işlev uygulama


221

Docs tuşları olarak çıktı sütun adlarını içeren bir dict kullanarak bir seferde GroupBy nesne üzerinde birden fazla işlevi nasıl uygulanacağını gösterir:

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

Ancak, bu yalnızca bir Series groupby nesnesinde çalışır. Ve bir dikte, bir grup tarafından DataFrame'e benzer şekilde iletildiğinde, anahtarların işlevin uygulanacağı sütun adları olmasını bekler.

Ne yapmak istiyorum birkaç sütun için birden çok işlev uygulamak (ancak belirli sütunları birden çok kez çalıştırılacaktır). Ayrıca, bazı işlevler groupby nesnesindeki diğer sütunlara bağlı olacaktır (sumif işlevleri gibi). Şu anki çözümüm, diğer satırlara bağlı işlevler için lambdas kullanarak, sütun sütun gitmek ve yukarıdaki kod gibi bir şey yapmaktır. Ama bu uzun zaman alıyor, (bence bir grup nesnesinden tekrar etmek uzun zaman alıyor). Tek bir seferde tüm groupby nesnesini yinelemem için değiştirmem gerekecek, ancak pandalarda bunu biraz temiz yapmak için yerleşik bir yol olup olmadığını merak ediyorum.

Örneğin, şöyle bir şey denedim

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(), ...)

ancak beklendiği gibi aggbir KeyError alıyorum (çünkü DataFrame'den anahtarlar bir sütun olması gerektiğinden).

Yapmak istediklerimi yapmak için herhangi bir yerleşik var mı veya bu işlevin eklenebileceği ihtimali var mı, yoksa sadece groupby'yi manuel olarak tekrarlamam gerekecek mi?

Teşekkürler


2
Bu soruya 2017'den itibaren geliyorsanız, birden fazla sütunu bir araya getirmenin deyimsel yolunu görmek için lütfen aşağıdaki cevaba bakın . Şu anda seçili olan yanıtın içinde birden fazla kullanımdan yoksunluk var, yani bir grubun sonucundaki sütunları yeniden adlandırmak için artık sözlük sözlüğü kullanamazsınız.
Ted Petrou

Yanıtlar:


283

Şu anda kabul edilen cevabın ikinci yarısı modası geçmiş ve iki kullanımdan kaldırılıyor. İlk ve en önemlisi, artık sözlük sözlüklerini agggroupby yöntemine geçiremezsiniz. İkincisi, asla kullanmayın .ix.

Aynı anda iki ayrı sütunla çalışmak istiyorsanız apply, uygulanan işleve örtük olarak bir DataFrame geçiren yöntemi kullanmanızı öneririm . Yukarıdakine benzer bir veri çerçevesi kullanalım

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

Sütun adlarından toplama işlevlerine eşlenen bir sözlük, bir toplama gerçekleştirmenin hala mükemmel bir yoludur.

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

Bu çirkin lambda sütun adını beğenmediyseniz, normal bir işlev kullanabilir ve bu özel __name__özelliğe özel bir ad verebilirsiniz :

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

applySeri Kullanma ve Döndürme

Şimdi, birlikte etkileşim için gereken birden çok sütununuz varsa, bunu kullanamazsınız aggve bu da bir Diziyi toplama işlevine dolaylı olarak geçirir. applyTüm grubu DataFrame olarak kullanırken işleve aktarılır.

Tüm toplamaların bir serisini döndüren tek bir özel işlev yapmanızı öneririm. Seri dizinini yeni sütunlar için etiket olarak kullanın:

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'e aşıksanız, yine de bunun gibi bir Diziye geri dönebilirsiniz:

    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
Dizi döndüren bir işlevi kullanma şeklini seviyorum. Çok temiz.
Stephen McAteer

2
Bu, bir veri çerçevesini simulatneosly çoklu sütun girişleri yoluyla toplamak için bulduğum tek yoldur (yukarıdaki c_d örneği)
Blake

2
Sonuçlar karıştı, agrup içinde toplamı 0bu olmamalı 0.418500 + 0.446069 = 0.864569mı? Aynı durum diğer hücreler için de geçerlidir, sayılar toplanmış gibi görünmemektedir. Sonraki örneklerde biraz farklı bir veri çerçevesi kullanılmış olabilir mi?
slackline

Kayıt sayısını görmek için sık sık bir groupby ile .size () kullanın. Agg: dict yöntemini kullanarak bunu yapmanın bir yolu var mı? Belirli bir alanı sayabileceğimi anlıyorum, ama benim tercihim sayımın alandan bağımsız olması.
Chris Decker

1
@slackline evet. Ben sadece test ve iyi çalışıyor. Ted, çerçeveyi birkaç farklı kez oluşturmuş olmalı ve rastgele sayı oluşturma yoluyla oluşturulduğundan, verileri gerçekten oluşturmak için kullanılan df verileri, hesaplamalarda nihai olarak kullanılandan farklıydı
Lucas H

166

İlk bölüm için tuşlar için bir sütun adı ve değerler için bir işlev listesi iletebilirsiniz:

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

GÜNCELLEME 1:

Toplama işlevi Seri üzerinde çalıştığından, diğer sütun adlarına yapılan başvurular kaybolur. Bu sorunu aşmak için, tam veri çerçevesine başvurabilir ve lambda işlevi içindeki grup dizinlerini kullanarak dizine ekleyebilirsiniz.

İşte hileli bir çözüm:

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

Burada, ortaya çıkan 'D' sütunu toplanan 'E' değerlerinden oluşur.

GÜNCELLEME 2:

İşte size sorduğunuz her şeyi yapacağını düşündüğüm bir yöntem. Önce özel bir lambda işlevi yapın. Aşağıda g grubu ifade eder. Toplarken, g bir Seri olacaktır. Geçme g.indexiçin df.ix[]df den seçer akım grup. Daha sonra C sütununun 0.5'ten küçük olup olmadığını test ederim. Döndürülen boole serisi, g[]yalnızca ölçütleri karşılayan satırları seçecek şekilde geçirilir .

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

İlginç, ayrıca {funcname: func}özel isimlerimi tutmak için listeler yerine değerler olarak bir diksiyon iletebilirim . Ancak her iki durumda da lambdadiğer sütunları kullanan bir lambda x: x['D'][x['C'] < 3].sum()iletiyi geçemiyorum ( yukarıdaki gibi: "KeyError: 'D'"). Bunun mümkün olup olmadığı hakkında bir fikrin var mı?
beardc

Tam olarak yapmaya çalışıyorum ve hatayı alıyorumKeyError: 'D'
Zelazny7

Harika, çalışmam lazım df['A'].ix[g.index][df['C'] < 0].sum(). Bu oldukça dağınık olmaya başlıyor - Bence okunabilirlik için manuel döngü tercih edilebilir, artı aggargümanda ( benim yerine <lambda>) tercih ettiğim adı vermenin bir yolu olduğundan emin değilim . Birisinin daha basit bir yol bileceğini umuyorum ...
beardc

3
Sütun değeri için bir dikte iletebilirsiniz {'D': {'my name':lambda function}}ve bu iç dikt anahtarını sütun adı yapar.
Zelazny7

1
Pandaların artık gruplandırılmış bir veri çerçevesine uygulanan birden fazla işlevi desteklediğine inanıyorum: pandas.pydata.org/pandas-docs/stable/…
IanS

22

Ted Petrou'nun cevabına alternatif olarak (çoğunlukla estetik üzerine), biraz daha kompakt bir listeyi tercih ettiğimi buldum. Lütfen kabul etmeyi düşünmeyin, Ted'in cevabı ve kod / veri hakkında çok daha ayrıntılı bir yorum. Python / pandalar benim ilk / en iyi değil, ama bunu iyi okumak için buldum:

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

dplyrBoruları ve data.tablezincirleme komutları daha anımsatan buluyorum . Daha iyi olduklarını söylememek, bana daha tanıdık gelmek. ( defBu tür operasyonlar için gücü ve çoğu kişi için daha resmi fonksiyonları kullanma tercihini kesinlikle kabul ediyorum . Bu sadece bir alternatif, ille de daha iyi değil.)


Ted ile aynı şekilde veri ürettim, tekrarlanabilirlik için bir tohum ekleyeceğim.

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
Bu cevabı en çok seviyorum. Bu, R'deki dplyr borularına benzer
Renhuai

18

Pandas >= 0.25.0, adlandırılmış toplamalar

Pandalar sürümü yana 0.25.0veya üstü, biz Sözlük tabanlı toplama uzaklaşan ve yeniden adlandırma ve doğru hareket ediyor adında toplamalardan bir kabul tuple. Şimdi aynı anda daha bilgilendirici bir sütun adıyla toplayabilir ve yeniden adlandırabiliriz:

Örnek :

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.aggAdlandırılmış toplama ile uygula :

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

Bu adlandırılmış toplamaları beğendim, ancak bunları birden çok sütunla nasıl kullanmamız gerektiğini göremedim?
Simon Woodhead

İyi soru, bunu anlayamadım, şüphesiz bu mümkün (henüz). Bunun için bir bilet açtım . Sorumu koruyacak ve güncellenecek. @SimonWoodhead işaret için teşekkürler
Erfan

4

0.25.0 sürümündeki yenilikler.

Çıktı sütun adları üzerinde denetime sahip sütuna özgü toplamayı desteklemek için pandalar GroupBy.agg () içindeki özel sözdizimini kabul eder "adlandırılmış toplama" olarak bilinen ;

  • Anahtar kelimeler çıktı sütunu adlarıdır
  • Değerler, ilk öğesi seçilecek sütun ve ikinci öğesi de bu sütuna uygulanacak toplama olan tuples'tir. Pandalar, argümanların ne olduğunu daha net hale getirmek için pandas.NamedAgg adlı bir diziyi ['column', 'aggfunc'] alanlarıyla sağlar. Her zamanki gibi, toplama çağrılabilir veya dize takma adı olabilir.
    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 sadece adlandırılmış bir gruptur. Düz kaplara da izin verilir.

    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

Ek anahtar kelime bağımsız değişkenleri toplama işlevlerine aktarılmaz. Yalnızca (sütun, aggfunc) çiftleri ** kwargs olarak geçirilmelidir. Toplama işlevleriniz ek bağımsız değişkenler gerektiriyorsa, bunları functools.partial () ile kısmen uygulayın.

Adlandırılmış toplama, Seri grubu tarafından yapılan toplamalar için de geçerlidir. Bu durumda sütun seçimi yoktur, bu nedenle değerler sadece işlevlerdir.

    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

Ted'in cevabı inanılmaz. Herkes ilgilenirse bunun daha küçük bir sürümünü kullanarak sona erdi. Birden çok sütundaki değerlere bağlı olan bir toplama ararken kullanışlıdır:

veri çerçevesi oluştur

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

uygula ile gruplama ve toplama (birden çok sütun kullanarak)

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

toplama ile gruplama ve toplama (birden çok sütun kullanarak)

Hala toplamayı kullanabildiğim için bu yaklaşımı seviyorum. Belki de insanlar, gruplar üzerinde toplamalar yaparken birden çok sütuna ulaşmak için neden başvurunun gerekli olduğunu bana bildireceklerdir.

Şimdi açık görünüyor, ancak ilgilenen sütunu gruptan hemen sonra seçmediğiniz sürece , toplama işlevinizden veri çerçevesinin tüm sütunlarına erişebileceksiniz.

sadece seçilen sütuna erişim

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

seçim sihirden sonra tüm sütunlara erişim

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

veya benzer şekilde

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

Umarım bu yardımcı olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.