Pertanyaan analisis yang sangat sering muncul di dunia kerja berbunyi seperti ini: berapa persen kontribusi setiap transaksi terhadap total kotanya, atau berapa persen porsi setiap kategori terhadap total penjualan. Perhitungan ini menggabungkan dua teknik yang sudah kita pelajari sebelumnya, yaitu transform() untuk membawa angka agregat ke setiap baris dan pembagian element-wise untuk mengubah perbandingan menjadi persentase. Chapter ini membangun pola tersebut langkah demi langkah, lalu menutup dengan rata-rata berbobot yang memakai div() antar kolom hasil agg().

Rumus Persentase per Kelompok #

Rumusnya sederhana: persentase sebuah baris sama dengan nilai baris dibagi total kelompoknya, lalu dikalikan 100. Bagian yang menentukan kebenaran hasil bukan rumusnya, melainkan cara mendapatkan total kelompok dalam ukuran yang sejajar dengan baris. Jika kita memakai hasil groupby().sum() yang hanya berisi satu baris per kelompok, pembagian akan salah karena ukurannya tidak cocok. Di sinilah transform("sum") berperan: menghasilkan total per kelompok dengan jumlah baris dan index yang sama seperti data asli.

Langkah 1 dan 2: transform(“sum”) lalu div #

Langkah pertama adalah menghitung total_kota = df.groupby("kota")["nilai"].transform("sum"). Langkah kedua adalah membagi kolom nilai dengan total tersebut menggunakan method div(), lalu mengalikan 100 dengan mul() dan membulatkan dengan round(). Rantai method seperti .div(total).mul(100).round(1) bekerja element-wise pada posisi index yang sejajar, sehingga setiap baris dibagi dengan total kelompoknya masing-masing. Operator / juga bisa dipakai, tetapi rantai div() dan mul() membuat urutan operasi terbaca sebagai satu alur perhitungan.

Contoh Lengkap: Kontribusi Tiap Transaksi dan Tiap Kategori #

Skrip berikut menghitung dua jenis persentase. Pertama, kontribusi setiap transaksi terhadap total kotanya. Kedua, kontribusi setiap kategori terhadap total keseluruhan penjualan.

python
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],
})

total_kota = penjualan.groupby("kota")["nilai"].transform("sum")
penjualan["persen"] = penjualan["nilai"].div(total_kota).mul(100).round(1)
print(penjualan, "\n")

total_semua = penjualan["nilai"].sum()
kontribusi = (
    penjualan.groupby("kategori")["nilai"].sum()
    .div(total_semua)
    .mul(100)
    .round(1)
)
print("kontribusi tiap kategori terhadap total keseluruhan:")
print(kontribusi)

Output yang Dihasilkan:

text
       kota    kategori  nilai  persen
0   Jakarta  Elektronik    120    60.0
1   Jakarta     Pakaian     80    40.0
2   Bandung  Elektronik     90    56.2
3   Bandung     Pakaian     70    43.8
4  Surabaya  Elektronik    110    64.7
5  Surabaya     Pakaian     60    35.3
6     Medan  Elektronik     50   100.0 

kontribusi tiap kategori terhadap total keseluruhan:
kategori
Elektronik    63.8
Pakaian       36.2
Name: nilai, dtype: float64

Mari kita bedah perhitungannya:

  1. Baris 9: transform("sum") menghasilkan total per kota pada setiap baris; Jakarta 200, Bandung 160, Surabaya 170, dan Medan 50.
  2. Baris 10: div(total_kota) membagi setiap nilai dengan total kotanya, mul(100) mengubah skala menjadi persen, dan round(1) membulatkan ke satu desimal. Hasilnya misalnya 120 dibagi 200 dikali 100 sama dengan 60.0.
  3. Baris 13-19: untuk perspektif kedua, groupby("kategori")["nilai"].sum() dijumlahkan lalu dibagi total_semua yang merupakan total seluruh penjualan, bukan total per kelompok.
  4. Baris 21: hasilnya Elektronik 63.8 persen dan Pakaian 36.2 persen dari total keseluruhan.

Persen per Kelompok vs Persen terhadap Total Keseluruhan #

Dua perhitungan di atas memakai penyebut yang berbeda, dan pembaca laporan perlu diberi tahu mana yang sedang ditampilkan. Kolom persen pada tabel membandingkan setiap transaksi dengan total kotanya, sehingga setiap kota berjumlah 100 persen; transaksi Medan bernilai 100.0 karena hanya ada satu transaksi di kota itu. Sebaliknya, ringkasan kategori membandingkan total kategori dengan total keseluruhan, sehingga jumlah keduanya adalah 100 persen. Salah menafsirkan penyebut adalah kesalahan analisis yang paling umum pada perhitungan persentase.

Rata-rata Berbobot dengan agg() dan div() #

Pola yang sama juga berguna untuk rata-rata berbobot, yang muncul ketika kolom harga bervariasi antar transaksi. Rata-rata sederhana mean() memperlakukan setiap transaksi sama beratnya, padahal yang kita sering butuhkan adalah total nilai dibagi total kuantitas. Skrip berikut menghitungnya dengan named aggregation dari Chapter agg() dengan Beberapa Fungsi:

python
import pandas as pd

transaksi = pd.DataFrame({
    "kota": ["Jakarta", "Jakarta", "Bandung", "Bandung"],
    "qty": [10, 5, 8, 2],
    "harga_satuan": [2000, 3000, 2500, 4000],
})
transaksi["nilai"] = transaksi["qty"] * transaksi["harga_satuan"]
print(transaksi, "\n")

ringkas = transaksi.groupby("kota").agg(
    total_nilai=("nilai", "sum"),
    total_qty=("qty", "sum"),
)
ringkas["harga_rata"] = ringkas["total_nilai"].div(ringkas["total_qty"]).round(1)
print("rata-rata harga satuan berbobot qty per kota:")
print(ringkas)

Output yang Dihasilkan:

text
      kota  qty  harga_satuan  nilai
0  Jakarta   10          2000  20000
1  Jakarta    5          3000  15000
2  Bandung    8          2500  20000
3  Bandung    2          4000   8000 

rata-rata harga satuan berbobot qty per kota:
         total_nilai  total_qty  harga_rata
kota                                       
Bandung        28000         10      2800.0
Jakarta        35000         15      2333.3

Baris 8 menghitung nilai tiap transaksi sebagai qty * harga_satuan. Baris 11-14 merangkum per kota dengan named aggregation: total_nilai menjumlahkan nilai dan total_qty menjumlahkan kuantitas. Baris 15 membagi keduanya dengan div(), menghasilkan harga satuan rata-rata yang sebenarnya: Jakarta 2333.3, bukan 2500 dari mean() biasa, karena transaksi 2000 berbobot jauh lebih besar.

Dengan pola transform("sum") plus div(), hampir semua pertanyaan proporsi per kelompok dapat dijawab dalam dua baris kode. Referensi resmi untuk pendalaman ada pada panduan GroupBy , DataFrameGroupBy.transform , dan Series.div .

Chapter berikutnya menutup Section ini dengan sebuah studi kasus utuh. Kita akan menggabungkan agg(), transform(), filter(), dan penanganan proporsi di atas untuk menjawab empat pertanyaan analisis bisnis dari satu dataset penjualan.