Beberapa agregasi dari kolom yang sama menggunakan panda GroupBy.agg ()

128

Apakah ada cara bawaan panda untuk menerapkan dua fungsi penggabungan yang berbeda f1, f2ke kolom yang sama df["returns"], tanpa harus memanggil agg()beberapa kali?

Contoh kerangka data:

import pandas as pd
import datetime as dt

pd.np.random.seed(0)
df = pd.DataFrame({
         "date"    :  [dt.date(2012, x, 1) for x in range(1, 11)], 
         "returns" :  0.05 * np.random.randn(10), 
         "dummy"   :  np.repeat(1, 10)
}) 

Salah secara sintaksis, tetapi secara intuitif benar, cara untuk melakukannya adalah:

# Assume `f1` and `f2` are defined for aggregating.
df.groupby("dummy").agg({"returns": f1, "returns": f2})

Jelas, Python tidak mengizinkan kunci duplikat. Apakah ada cara lain untuk mengungkapkan masukan agg()? Mungkin daftar tupel [(column, function)]akan bekerja lebih baik, untuk memungkinkan beberapa fungsi diterapkan ke kolom yang sama? Tapi agg()sepertinya itu hanya menerima kamus.

Apakah ada solusi untuk ini selain mendefinisikan fungsi bantu yang hanya menerapkan kedua fungsi di dalamnya? (Bagaimana cara kerjanya dengan agregasi?)

ely
sumber
Terkait - Agregasi di panda
jezrael
2
Mulai 0,25 dan seterusnya, panda menyediakan sintaks yang lebih intuitif untuk beberapa agregasi, serta mengganti nama kolom keluaran. Lihat dokumentasi tentang Agregasi Bernama .
cs95
FYI pertanyaan ini diminta jalan kembali pada panda 0.8.x di 9/2012
smci
1
FYI jawaban yang diterima juga tidak berlaku lagi - jangan berikan agg () diktik.
cs95
@ cs95: Saya tahu ini tidak berlaku lagi, saya katakan SO dikotori dengan solusi lama dari versi lama. SO tidak memiliki cara untuk menandai itu - selain komentar.
smci

Jawaban:

159

Anda cukup meneruskan fungsi sebagai daftar:

In [20]: df.groupby("dummy").agg({"returns": [np.mean, np.sum]})
Out[20]:         
           mean       sum
dummy                    
1      0.036901  0.369012

atau sebagai kamus:

In [21]: df.groupby('dummy').agg({'returns':
                                  {'Mean': np.mean, 'Sum': np.sum}})
Out[21]: 
        returns          
           Mean       Sum
dummy                    
1      0.036901  0.369012
bmu
sumber
5
Apakah ada cara untuk menentukan nama kolom hasil?
Ben
3
@Ben saya pikir Anda harus menggunakan ganti nama setelah itu. contoh oleh Tom Augspurger (lihat sel 25)
Stewbaca
1
@ Ben: Saya menambahkan contoh
bmu
10
@sparc_spread Meneruskan beberapa fungsi sebagai daftar dijelaskan dengan baik dalam dokumentasi pandas . Mengganti nama dan meneruskan beberapa fungsi sebagai kamus tidak akan digunakan lagi di versi panda mendatang. Detail ada di log perubahan 0,20 , yang juga saya rangkum di tempat lain di SO .
joelostblom
3
Itu sudah dikatakan, tetapi menggunakan kamus untuk mengganti nama kolom keluaran dari usia sudah tidak digunakan lagi. Anda malah dapat menentukan daftar tupel. Lihat jawaban ini.
cs95
101

TLDR; Pandas groupby.aggmemiliki sintaks baru yang lebih mudah untuk menentukan (1) agregasi di beberapa kolom, dan (2) beberapa agregasi di kolom. Jadi, untuk melakukan ini pada panda> = 0,25 , gunakan

df.groupby('dummy').agg(Mean=('returns', 'mean'), Sum=('returns', 'sum'))

           Mean       Sum
dummy                    
1      0.036901  0.369012

ATAU

df.groupby('dummy')['returns'].agg(Mean='mean', Sum='sum')

           Mean       Sum
dummy                    
1      0.036901  0.369012

Panda> = 0.25: Agregasi Bernama

Panda telah mengubah perilaku yang GroupBy.aggmendukung sintaks yang lebih intuitif untuk menentukan agregasi bernama. Lihat bagian dokumen 0.25 tentang Penyempurnaan serta masalah GitHub yang relevan GH18366 dan GH26512 .

Dari dokumentasi,

Untuk mendukung agregasi khusus kolom dengan kontrol atas nama kolom keluaran, panda menerima sintaks khusus dalam GroupBy.agg(), yang dikenal sebagai "agregasi bernama", di mana

  • Kata kunci adalah nama kolom keluaran
  • Nilainya adalah tupel yang elemen pertamanya adalah kolom untuk dipilih dan elemen kedua adalah agregasi untuk diterapkan ke kolom tersebut. Pandas menyediakan pandas.NamedAgg bernamatuple dengan kolom ['column', 'aggfunc'] untuk memperjelas argumennya. Seperti biasa, agregasi dapat berupa callable atau alias string.

Anda sekarang dapat mengirimkan tupel melalui argumen kata kunci. Tupel mengikuti format (<colName>, <aggFunc>).

import pandas as pd

pd.__version__                                                                                                                            
# '0.25.0.dev0+840.g989f912ee'

# Setup
df = 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]
})

df.groupby('kind').agg(
    max_height=('height', 'max'), min_weight=('weight', 'min'),)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

Sebagai alternatif, Anda dapat menggunakan pd.NamedAgg(pada dasarnya bernamauple) yang membuat segalanya lebih eksplisit.

df.groupby('kind').agg(
    max_height=pd.NamedAgg(column='height', aggfunc='max'), 
    min_weight=pd.NamedAgg(column='weight', aggfunc='min')
)

      max_height  min_weight
kind                        
cat          9.5         7.9
dog         34.0         7.5

Bahkan lebih sederhana untuk Seri, cukup teruskan aggfunc ke argumen kata kunci.

df.groupby('kind')['height'].agg(max_height='max', min_height='min')    

      max_height  min_height
kind                        
cat          9.5         9.1
dog         34.0         6.0       

Terakhir, jika nama kolom Anda bukan pengenal python yang valid, gunakan kamus dengan membongkar:

df.groupby('kind')['height'].agg(**{'max height': 'max', ...})

Panda <0,25

Dalam versi panda yang lebih baru yang mengarah ke 0,24, jika menggunakan kamus untuk menentukan nama kolom untuk keluaran agregasi, Anda akan mendapatkan FutureWarning:

df.groupby('dummy').agg({'returns': {'Mean': 'mean', 'Sum': 'sum'}})
# FutureWarning: using a dict with renaming is deprecated and will be removed 
# in a future version

Menggunakan kamus untuk mengganti nama kolom sudah tidak digunakan lagi di v0.20. Pada versi panda yang lebih baru, ini dapat ditentukan secara lebih sederhana dengan meneruskan daftar tupel. Jika menentukan fungsi dengan cara ini, semua fungsi untuk kolom itu perlu ditetapkan sebagai tupel dari pasangan (nama, fungsi).

df.groupby("dummy").agg({'returns': [('op1', 'sum'), ('op2', 'mean')]})

        returns          
            op1       op2
dummy                    
1      0.328953  0.032895

Atau,

df.groupby("dummy")['returns'].agg([('op1', 'sum'), ('op2', 'mean')])

            op1       op2
dummy                    
1      0.328953  0.032895
cs95
sumber
4
Ini harus menjadi jawaban teratas karena menggunakan solusi yang lebih jelas dan bersih menggunakan versi antarmuka yang lebih baru.
NKSHELL
Contoh yang digunakan untuk agregasi bernama tidak menyelesaikan masalah asli menggunakan beberapa agregasi pada kolom yang sama. Misalnya, dapatkah Anda menggabungkan dengan min dan maks untuk tinggi tanpa subset pertama untuk df.groupby('kind')['height']?
pemenang
1
@victor Saya menambahkan TLDR di bagian atas jawaban yang langsung menjawab pertanyaan tersebut. Dan jawaban dari pertanyaan kedua Anda adalah ya, silahkan lihat edit di jawaban saya.
cs95
Kode yang lebih umum untuk contoh terakhir dari jawaban Anda> = 0,25 untuk menangani penggabungan beberapa kolom seperti ini akan sangat bagus. df.groupby("kind").agg(**{ 'max height': pd.NamedAgg(column='height', aggfunc=max), 'min weight': pd.NamedAgg(column='weight', aggfunc=min) })
Onur Ece
6

Apakah sesuatu seperti ini berhasil:

In [7]: df.groupby('dummy').returns.agg({'func1' : lambda x: x.sum(), 'func2' : lambda x: x.prod()})
Out[7]: 
              func2     func1
dummy                        
1     -4.263768e-16 -0.188565
Chang She
sumber
2
Tidak, ini tidak berhasil. Jika Anda melihat string dokumen untuk aggregateitu secara eksplisit mengatakan bahwa ketika a dictdilewatkan, kuncinya haruslah nama kolom. Jadi, contoh Anda adalah sesuatu yang Anda ketik tanpa memeriksa kesalahan ini, atau Pandas merusak dokumennya sendiri di sini.
ely
N / MI tidak melihat panggilan ekstra returnsdi sana. Jadi ini adalah agregat versi Seri? Saya ingin melakukan versi agregat DataFrame, dan saya ingin menerapkan beberapa agregasi yang berbeda ke setiap kolom sekaligus.
ely
1
Coba ini: df.groupby ('dummy'). Agg ({'return': {'func1': lambda x: x.sum (), 'func2': lambda x: x.mean ()}})
Chang Dia
Ini memberikan kesalahan pernyataan tanpa pesan. Dari tampilan kode (pandas.core.internals.py, baris 406-408, versi 0.7.3) sepertinya itu melakukan pemeriksaan di bagian akhir untuk memastikan itu tidak mengembalikan lebih banyak kolom daripada ada kunci dalam yang pertama lapisan kamus agregasi.
ely
Bekerja dengan baik pada master. Anda ingin mencoba memperbarui?
Chang She