Pengelompokan dengan groupby

Agregasi per kelompok dengan groupby dan pembacaan hasilnya.

Daftar isi

Pertanyaan analisis hampir selalu berbicara per kelompok, misalnya rata-rata per stasiun atau maksimum per jam, dan groupby adalah jawaban pandas untuk semuanya. Topik ini membedah pola split-apply-combine yang melandasinya, menerapkannya dengan beberapa agregat sekaligus, serta memperkenalkan transform yang mengembalikan hasil ke bentuk baris aslinya. Setelah membacanya, ringkasan per kelompok pada seluruh modul EDA berikutnya sudah dikuasai pola kerjanya.

Split, apply, combine#

groupby memecah tabel menjadi kelompok menurut nilai satu atau beberapa kolom, menerapkan satu fungsi pada setiap kelompok, lalu menggabungkan hasilnya menjadi tabel baru. Tiga langkah itu dijuluki split-apply-combine, dan memahaminya membuat seluruh variasi groupby dapat ditebak daripada dihafalkan.

Skrip berikut menerapkan pola itu pada data pengukuran dua stasiun, dengan tiga agregat sekaligus, penentuan jam dengan nilai tertinggi, dan transform yang menempelkan rata-rata kelompok ke setiap baris.

python pengelompokan_groupby.py
"""Agregasi per kelompok dengan groupby."""
import pandas as pd

data = pd.DataFrame(
    {
        "stasiun": ["A", "A", "A", "B", "B", "B"],
        "jam": [8, 9, 10, 8, 9, 10],
        "pm25": [34.0, 41.5, 28.0, 52.3, 47.0, 61.2],
    }
)

ringkas = data.groupby("stasiun")["pm25"].agg(["count", "mean", "max"])
print(ringkas.round(1).to_string())
per_jam = data.groupby("jam")["pm25"].mean()
print(per_jam.idxmax(), per_jam.max().round(1))
print(data.groupby("stasiun")["pm25"].transform("mean").round(1).tolist())
text Keluaran
         count  mean   max
stasiun                   
A            3  34.5  41.5
B            3  53.5  61.2
10 44.6
[34.5, 34.5, 34.5, 53.5, 53.5, 53.5]

Baris pertama keluaran memperlihatkan hasil combine, yaitu tabel baru yang indeksnya nilai kelompok dan kolomnya agregat yang diminta. Stasiun B konsisten lebih tinggi daripada A pada data kecil ini, dan count yang tampil bersamanya memberi bobot jumlah data, yaitu kebiasaan yang penting karena rata-rata tanpa jumlah data mudah meleset maknanya.

Banyak agregat dan banyak kunci#

.agg() menerima satu daftar fungsi, kamus yang memetakan kolom ke agregatnya masing-masing, atau fungsi buatan sendiri. Bentuk kamusnya yang paling sering berguna pada praktik, misalnya data.groupby("stasiun").agg(pm25_rata=("pm25", "mean"), suhu_max=("suhu_c", "max")), yang sekaligus menamai kolom hasilnya dengan jelas.

Kunci kelompok dapat lebih dari satu dengan menyebutkan daftar kolom, dan hasilnya bertingkat, misalnya groupby(["stasiun", "jam"]) memberi satu baris untuk setiap pasangan stasiun dan jam. Tabel bertingkat ini diperdalam pada modul segmentasi karena menjadi alat utamanya, dan reset_index() mengembalikannya menjadi tabel datar yang lebih mudah digabungkan dengan data lain.

Agregat yang dipilih harus dibaca ulang sebelum dilaporkan. sum pada data pengukuran per jam bermakna akumulasi, sedangkan mean menyatakan tingkat rata-rata, dan keduanya sah tetapi menjawab pertanyaan yang berbeda. Kebiasaan menyebut satuannya pada nama kolom hasil, misalnya pm25_rata, menjaga pembacaan tetap jujur.

Transform untuk kembali ke baris#

Agregasi menyusutkan setiap kelompok menjadi satu baris, sedangkan transform menerapkan perhitungan kelompok yang sama tetapi mengembalikan hasil dengan panjang yang sama seperti tabel aslinya. Baris ketiga keluaran di atas memperlihatkannya, yaitu setiap baris stasiun A membawa 34.5 dan setiap baris B membawa 53.5.

Bentuk inilah yang dibutuhkan ketika nilai kelompok perlu dibandingkan dengan barisnya sendiri, misalnya selisih setiap pengukuran terhadap rata-rata stasiunnya atau penyusutan dari terbesar kelompoknya. Pola umumnya adalah data["pm25"] - data.groupby("stasiun")["pm25"].transform("mean"), dan hasilnya adalah penyimpangan yang terstandardisasi per kelompok tanpa perulangan apa pun.

Pasangan lain yang sering muncul adalah filter, yang mengembalikan seluruh baris kelompok yang lolos syarat tingkat kelompok, misalnya kelompok dengan jumlah data lebih dari dua. Ketiganya membentuk satu keluarga, yaitu apply untuk fleksibilitas penuh, transform untuk membentuk ulang ke baris, dan filter untuk memilih kelompok.

Kesalahan umum#

Kesalahan pertama adalah membaca hasil groupby sebagai tabel biasa tanpa menyadari indeksnya adalah kunci kelompok. Operasi yang mengandalkan nomor posisi kemudian meleset, sedangkan reset_index() atau argumen as_index=False mengembalikan bentuk tabel yang akrab.

Kesalahan kedua adalah rata-rata rata-rata, yaitu mengagregasi hasil agregasi tanpa menimbang ukuran kelompok. Rata-rata dari 34.5 dan 53.5 memang 44.0, dan kebetulan rata-rata keseluruhan enam baris data ini juga 44.0, tetapi kebetulan itu menyesatkan: pada ukuran kelompok yang timpang, rata-rata rata-rata berbeda nyata dari rata-rata keseluruhan. Bobot dihitung dari count, bukan diabaikan.

Kesalahan ketiga adalah lupa nilai hilang. Secara bawaan groupby melewati kunci yang NaN sehingga baris tersebut keluar dari semua kelompok, dan jumlah baris hasil agregasi yang kurang dari tabel aslinya adalah sinyalnya. Perlakuan atas data hilang diambil sebelumnya, sebagaimana dibahas pada modul pembersihan.

Ringkasan#

  • groupby bekerja dengan pola split-apply-combine, dan indeks hasilnya adalah kunci kelompok.
  • .agg() menerima banyak agregat sekaligus dan menamai kolom hasilnya, sedangkan kunci kelompok ganda menghasilkan tabel bertingkat.
  • transform mengembalikan perhitungan kelompok ke panjang baris asli, sehingga setiap baris dapat dibandingkan dengan kelompoknya.
  • Rata-rata antar kelompok harus ditimbang ukuran kelompoknya, dan kunci NaN dilewati secara bawaan.

Referensi#