Pada chapter-chapter sebelumnya, hasil groupby().agg() selalu menyusut menjadi satu baris per kelompok. Format ringkas itu bagus untuk laporan rekap, tetapi sering kali kita justru membutuhkan angka agregat yang menempel pada setiap baris asli, misalnya menampilkan total kota di samping setiap transaksi. Method transform() pada GroupBy dirancang untuk kebutuhan tersebut: hasil agregasi dikembalikan dengan jumlah baris dan urutan yang sama seperti data asli. Chapter ini membahas cara kerja transform(), penggunaannya untuk menambahkan kolom agregat, dan kapan kita sebaiknya memilih transform() dibanding agg().
Masalah: Angka Agregat yang Tetap Menempel pada Baris #
Bayangkan laporan penjualan berisi tujuh transaksi dari empat kota. Manajer ingin membaca tabel transaksi yang utuh, tetapi setiap baris juga menampilkan total kotanya serta selisih transaksi dari rata-rata kota. Method agg() tidak cocok untuk kebutuhan ini karena hasilnya hanya beberapa baris ringkasan tanpa detail transaksi. Kita membutuhkan operasi yang menghitung per kelompok, namun mengembalikan hasil ke setiap baris asli.
Mekanisme transform() pada GroupBy #
transform() menerima argumen yang sama dengan agg(): string nama fungsi seperti "sum", "mean", atau "max", maupun fungsi Python. GroupBy membagi data per kelompok, menghitung fungsi tersebut pada setiap kelompok, lalu menempatkan hasilnya ke baris-baris anggota kelompok yang bersangkutan. Proses penempatan kembali ini sering disebut broadcast: satu nilai per kelompok disebarkan ke semua baris kelompoknya. Karena itu, Series hasil transform() selalu punya jumlah baris dan index yang sama dengan DataFrame asli, sehingga aman ditempel sebagai kolom baru.
Contoh: Total Kota dan Selisih dari Rata-rata #
Skrip berikut memakai data penjualan empat kota. Kita bandingkan dulu hasil sum() yang menyusut dengan hasil transform("sum") yang tidak menyusut, lalu kita tambahkan dua kolom baru: total_kota dan selisih_rata.
import pandas as pd
penjualan = pd.DataFrame({
"kota": ["Jakarta", "Jakarta", "Bandung", "Bandung", "Surabaya", "Surabaya", "Medan"],
"kategori": ["Elektronik", "Pakaian", "Elektronik", "Pakaian", "Elektronik", "Pakaian", "Elektronik"],
"nilai": [120, 80, 90, 70, 110, 60, 50],
})
print(penjualan, "\n")
print("sum() menyusut menjadi satu baris per kelompok:")
print(penjualan.groupby("kota")["nilai"].sum(), "\n")
print('transform("sum") mempertahankan jumlah baris asli:')
print(penjualan.groupby("kota")["nilai"].transform("sum"), "\n")
penjualan["total_kota"] = penjualan.groupby("kota")["nilai"].transform("sum")
penjualan["selisih_rata"] = (
penjualan["nilai"] - penjualan.groupby("kota")["nilai"].transform("mean")
)
print(penjualan)Output yang Dihasilkan:
kota kategori nilai
0 Jakarta Elektronik 120
1 Jakarta Pakaian 80
2 Bandung Elektronik 90
3 Bandung Pakaian 70
4 Surabaya Elektronik 110
5 Surabaya Pakaian 60
6 Medan Elektronik 50
sum() menyusut menjadi satu baris per kelompok:
kota
Bandung 160
Jakarta 200
Medan 50
Surabaya 170
Name: nilai, dtype: int64
transform("sum") mempertahankan jumlah baris asli:
0 200
1 200
2 160
3 160
4 170
5 170
6 50
Name: nilai, dtype: int64
kota kategori nilai total_kota selisih_rata
0 Jakarta Elektronik 120 200 20.0
1 Jakarta Pakaian 80 200 -20.0
2 Bandung Elektronik 90 160 10.0
3 Bandung Pakaian 70 160 -10.0
4 Surabaya Elektronik 110 170 25.0
5 Surabaya Pakaian 60 170 -25.0
6 Medan Elektronik 50 50 0.0Mari kita telaah baris-baris penting pada skrip di atas:
- Baris 3-7: DataFrame
penjualandibangun dari tujuh transaksi; kolomkotamenjadi kunci kelompok dan kolomnilaimenjadi data yang diagregasi. - Baris 10-11:
groupby("kota")["nilai"].sum()menghasilkan empat baris, satu per kota. Hasil ini ringkas, tetapi tidak bisa ditempel langsung ke tabel asli karena ukurannya berbeda. - Baris 13-14:
transform("sum")menghasilkan Series dengan tujuh baris. Baris 0 dan 1 sama-sama bernilai 200 karena keduanya milik Jakarta, dan seterusnya mengikuti kelompoknya. - Baris 16-19: hasil
transform("sum")menjadi kolomtotal_kota, sedangkanselisih_ratamengurangi nilai tiap transaksi dengan rata-rata kotanya daritransform("mean").
Perhatikan kolom selisih_rata pada output: transaksi Medan bernilai 0.0 karena Medan hanya memiliki satu transaksi, sehingga nilainya sama dengan rata-rata kotanya sendiri. Sementara itu, Surabaya memiliki selisih 25.0 dan -25.0, tanda dua transaksinya sama jauh di atas dan di bawah rata-rata. Pola selisih dari rata-rata seperti ini sering dipakai untuk menandai transaksi yang tidak lazim.
Memilih antara agg() dan transform() #
Kedua method mengerjakan agregasi yang sama, tetapi bentuk hasilnya berbeda tujuan. Tabel berikut merangkum kapan masing-masing lebih pas:
| Aspek | agg() | transform() |
|---|---|---|
| Ukuran hasil | satu baris per kelompok | sama dengan data asli |
| Cocok untuk | laporan ringkasan per kelompok | kolom bantu pada tabel asli |
| Contoh pertanyaan | “berapa total per kota?” | “berapa total kota untuk transaksi ini?” |
| Posisi hasil | tabel ringkasan terpisah | sejajar dengan index asli |
Aturan praktisnya: jika hasil akhir yang diinginkan adalah tabel ringkasan, pakai agg(); jika tabel asli harus tetap utuh dengan tambahan angka per kelompok, pakai transform(). Kedua method ini saling melengkapi dan akan muncul bergantian pada chapter-chapter berikutnya. Referensi lengkapnya ada pada panduan GroupBy
, DataFrameGroupBy.transform
, dan DataFrame.transform
.
Dengan transform(), angka agregat dan data mentah kini hidup berdampingan dalam satu tabel. Kemampuan ini menjadi fondasi teknik lanjutan seperti menghitung persentase per kelompok yang dibahas beberapa chapter lagi. Sebelum sampai ke sana, Chapter berikutnya membahas filter(): menyaring kelompok utuh berdasarkan kondisi agregat, misalnya mempertahankan hanya kota dengan total penjualan tertentu.