Laporan omzet per kota sudah membantu, tetapi manajer toko sering menanyakan potongan yang lebih halus: berapa omzet kategori Sembako khusus di kota Bandung? Pertanyaan seperti itu melibatkan dua kunci sekaligus dan tidak bisa dijawab dengan groupby satu kolom tanpa langkah penyaringan tambahan. Pandas menerima list kolom pada groupby(), dan setiap kombinasi nilai unik dari kolom-kolom tersebut menjadi satu kelompok. Konsekuensinya, hasil agregasi membawa MultiIndex, yaitu index bertingkat dengan beberapa level nama. Chapter ini membahas cara membuat kelompok multi-kolom, membaca indexnya, dan menata ulang hasilnya menjadi tabel silang.

Daftar Kunci sebagai Argumen groupby #

Bentuk paling umum dari groupby beberapa kolom adalah df.groupby(["kota", "kategori"]). List yang dipakai sebagai argumen menentukan urutan level index: kolom pertama menjadi level paling luar dan kolom kedua menjadi level di dalamnya. pandas menghitung semua kombinasi nilai yang benar-benar muncul di data, bukan semua kombinasi teoretis; dataset transaksi kita memiliki sembilan kombinasi kota-kategori yang aktif berjualan. Karena itu jumlah kelompok hasil groupby bisa lebih banyak daripada jumlah nilai unik setiap kolomnya.

Membaca MultiIndex Hasil Agregasi #

Script berikut mengelompokkan transaksi berdasarkan dua kolom, menjumlahkan kolom total, lalu menunjukkan cara mengintip struktur index hasilnya.

python
import pandas as pd

transaksi = pd.DataFrame({
    "kota": ["Jakarta", "Jakarta", "Jakarta", "Jakarta",
             "Bandung", "Bandung", "Bandung", "Bandung",
             "Surabaya", "Surabaya", "Surabaya", "Surabaya"],
    "kategori": ["Minuman", "Minuman", "Makanan", "Sembako",
                 "Minuman", "Makanan", "Sembako", "Makanan",
                 "Minuman", "Sembako", "Makanan", "Minuman"],
    "produk": ["Kopi Gayo", "Teh Melati", "Roti Tawar", "Beras Premium",
               "Kopi Gayo", "Roti Tawar", "Beras Premium", "Mie Instan",
               "Teh Melati", "Beras Premium", "Mie Instan", "Kopi Gayo"],
    "jumlah": [3, 2, 4, 1, 5, 2, 3, 10, 6, 2, 8, 1],
    "harga": [18000, 12000, 15000, 68000, 18000, 15000, 68000, 3500,
              12000, 68000, 3500, 18000],
    "total": [54000, 24000, 60000, 68000, 90000, 30000, 204000, 35000,
              72000, 136000, 28000, 18000],
})

d = transaksi.groupby(["kota", "kategori"])["total"].sum()

print(d)
print()
print("nama level index :", list(d.index.names))
print("jumlah level     :", d.index.nlevels)
print()

print("omzet Bandung untuk semua kategori:")
print(d.loc["Bandung"])
print()

print("omzet Sembako di Bandung saja:", d.loc[("Bandung", "Sembako")])
print()

print("bentuk tabel silang dengan unstack():")
print(d.unstack())

Output yang Dihasilkan:

text
kota      kategori
Bandung   Makanan      65000
          Minuman      90000
          Sembako     204000
Jakarta   Makanan      60000
          Minuman      78000
          Sembako      68000
Surabaya  Makanan      28000
          Minuman      90000
          Sembako     136000
Name: total, dtype: int64

nama level index : ['kota', 'kategori']
jumlah level     : 2

omzet Bandung untuk semua kategori:
kategori
Makanan     65000
Minuman     90000
Sembako    204000
Name: total, dtype: int64

omzet Sembako di Bandung saja: 204000

bentuk tabel silang dengan unstack():
kategori  Makanan  Minuman  Sembako
kota                               
Bandung     65000    90000   204000
Jakarta     60000    78000    68000
Surabaya    28000    90000   136000

Mari kita bedah baris-baris penting script di atas:

  1. Baris 20: argumen ["kota", "kategori"] membentuk sembilan kelompok; hasilnya Series bernama total dengan index bertingkat dua level.
  2. Baris 24-25: d.index.names mengembalikan nama kedua level (kota dan kategori), sedangkan d.index.nlevels mengonfirmasi bahwa index memiliki dua level.
  3. Baris 29: d.loc["Bandung"] adalah akses parsial; level pertama saja disebut sehingga pandas mengembalikan Series omzet seluruh kategori di Bandung.
  4. Baris 32: d.loc[("Bandung", "Sembako")] menyebut kedua level lewat tuple dan mengembalikan satu angka, yaitu 204000.
  5. Baris 36: d.unstack() memutar level terdalam menjadi kolom sehingga hasilnya berbentuk tabel silang kota dan kategori.

Mengakses Satu Kombinasi Kunci #

Penulisan tuple ("Bandung", "Sembako") pada .loc mengikuti aturan yang konsisten: urutan elemennya dari level terluar ke terdalam, dan jumlahnya harus sama dengan jumlah level bila kita menginginkan satu angka saja. Bila sebuah level sengaja tidak disebut, pandas mengembalikan hasil untuk semua nilai level tersebut, seperti Series Bandung pada output tadi. Akses parsial semacam ini praktis untuk laporan satu wilayah karena kita tidak perlu menyaring DataFrame asal lagi.

Menata Hasil dengan unstack() #

Series MultiIndex bagus untuk komputasi lanjutan, tetapi laporan untuk manusia lebih nyaman dibaca dalam bentuk tabel silang. Method unstack() memindahkan level index terdalam menjadi kolom, sehingga baris menunjukkan kota dan kolom menunjukkan kategori. Operasi kebalikannya adalah stack(), yang mengembalikan kolom menjadi level index lagi; keduanya membentuk pasangan yang bisa diputar bolak-balik tanpa kehilangan informasi. Struktur index bertingkat seperti ini dijelaskan lebih rinci pada user guide MultiIndex resmi pandas.

Dengan dua kunci, satu baris groupby mampu menjawab pertanyaan yang pada SQL menuntut GROUP BY dua kolom, seperti dirangkum pada user guide groupby . Dataset transaksi yang sama akan terus kita pakai sehingga setiap angka mudah dicek ulang. Berikutnya kita fokus ke tahap apply: fungsi agregasi bawaan apa saja yang bisa dipanggil setelah pengelompokan, mulai dari sum() sampai nunique(), termasuk perbedaan count() dan size() yang sering tertukar.