Tabel hasil penggabungan pada chapter sebelumnya kini panjang dan kaya — tetapi panjang belum tentu bentuk yang tepat untuk setiap keperluan. Pemilik Toko Kopi Kita ingin rekap penjualan per menu per bulan: menu berjajar ke bawah, bulan berderet ke kanan. Sebaliknya, banyak analisis justru menuntut kebalikannya: satu baris per pengamatan, kolom sedikit, dan nilai bermuara di satu kolom. Dua kebutuhan itu dilayani dua bentuk data — lebar dan panjang — dan pandas menyediakan jalan bolak-baliknya: melt melelehkan yang lebar, pivot menekuk yang panjang. Pada chapter ini kita berlatih berpindah bentuk, termasuk saat data ganda membuat pivot menolak bekerja dan pivot_table mengambil alih dengan agregasi.

Dua Wajah Data yang Sama #

Data rekap triwulan kita bisa ditulis dalam dua wajah yang memuat informasi persis sama. Wajah lebar meletakkan setiap bulan sebagai kolom — nyaman dibaca mata, mirip laporan yang ditempel di dinding:

menuJanuariFebruariMaret
kopi susu12095110
teh tawar808590
matcha latte605575
americano706560

Wajah panjang memindahkan nama bulan ke dalam barisnya sendiri: tiga kolom (menu, bulan, terjual), dua belas baris. Cuplikan enam barisnya tampak seperti ini:

menubulanterjual
kopi susuJanuari120
kopi susuFebruari95
kopi susuMaret110
teh tawarJanuari80
teh tawarFebruari85
………

Tidak ada data yang hilang maupun bertambah — yang berubah hanya letaknya. Karena itu mengubah bentuk (reshaping) adalah operasi yang aman untuk bolak-balik, dan panduan reshaping pandas menyebut keduanya sebagai representasi setara dari tabel yang sama.

Melelehkan Tabel Lebar dengan melt #

melt melelehkan kolom-kolom bulan menjadi pasangan nama-isi: nama kolom jatuh ke kolom baru (var_name), isinya jatuh ke kolom nilai (value_name). Kolom yang tidak ikut dilelehkan ditahan lewat id_vars — di sini menu yang menjadi identitas tiap baris.

python
import pandas as pd

rekap = pd.DataFrame({
    "menu": ["kopi susu", "teh tawar", "matcha latte", "americano"],
    "Januari": [120, 80, 60, 70],
    "Februari": [95, 85, 55, 65],
    "Maret": [110, 90, 75, 60],
})
print(rekap)
print()

panjang = rekap.melt(id_vars="menu", var_name="bulan", value_name="terjual")
print(panjang.head(6))
print()
print("Bentuk lebar:", rekap.shape, "-> bentuk panjang:", panjang.shape)

Output yang Dihasilkan:

text
           menu  Januari  Februari  Maret
0     kopi susu      120        95    110
1     teh tawar       80        85     90
2  matcha latte       60        55     75
3     americano       70        65     60

           menu     bulan  terjual
0     kopi susu   Januari      120
1     teh tawar   Januari       80
2  matcha latte   Januari       60
3     americano   Januari       70
4     kopi susu  Februari       95
5     teh tawar  Februari       85

Bentuk lebar: (4, 4) -> bentuk panjang: (12, 3)

Mari kita telaah pelelehannya:

  1. Baris 3-8 (rekap): bentuk lebar — empat baris, empat kolom, setiap baris menu memuat tiga angka sekaligus. Enam belas nilai dikenali mata dalam sekali pandang, tetapi kolomnya tumbuh setiap bulan baru.
  2. Baris 12 (melt(id_vars="menu", ...)): menu ditahan sebagai identitas; kolom Januari, Februari, dan Maret dilelehkan. Nama bulan mengalir ke kolom bulan (var_name), angkanya mengalir ke kolom terjual (value_name).
  3. Bentuk hasil: (4, 4) menjadi (12, 3) — empat menu kali tiga bulan sama dengan dua belas pengamatan, masing-masing kini berdiri sendiri sebagai satu baris. Menambah bulan April di bentuk panjang berarti menambah empat baris, bukan satu kolom baru.

Mengembalikan Bentuk Lebar dengan pivot #

pivot menekuk bentuk panjang kembali menjadi lebar: satu kolom menjadi index baris, satu kolom membentang menjadi kolom-kolom baru, satu kolom menjadi isinya. Ia adalah kebalikan persis dari melt untuk data yang pasangan kolomnya unik.

python
import pandas as pd

rekap = pd.DataFrame({
    "menu": ["kopi susu", "teh tawar", "matcha latte", "americano"],
    "Januari": [120, 80, 60, 70],
    "Februari": [95, 85, 55, 65],
    "Maret": [110, 90, 75, 60],
})
panjang = rekap.melt(id_vars="menu", var_name="bulan", value_name="terjual")

lebar = panjang.pivot(index="menu", columns="bulan", values="terjual")
print(lebar)
print()
print("Nama deret kolom:", lebar.columns.name)
print("Kolom hasil:", lebar.columns.tolist())

Output yang Dihasilkan:

text
bulan         Februari  Januari  Maret
menu                                  
americano           65       70     60
kopi susu           95      120    110
matcha latte        55       60     75
teh tawar           85       80     90

Nama deret kolom: bulan
Kolom hasil: ['Februari', 'Januari', 'Maret']

Mari kita telaah penekukannya:

  1. Baris 11 (pivot(index="menu", columns="bulan", values="terjual")): nilai menu menjadi index baris, nilai bulan membentang menjadi kolom, dan terjual mengisi persimpangannya — persis tabel rekap yang kita mulai.
  2. lebar.columns.name: nama kolom yang membentang (bulan) tersimpan sebagai nama deret kolom, bukan kolom biasa. Bila menu perlu kembali menjadi kolom biasa, lebar.reset_index() memindahkannya keluar dari index — kebiasaan yang baik sebelum menyimpan hasil.
  3. Urutan kolom hasil: Februari, Januari, Maret — terurut menurut abjad nama bulan, bukan urutan asli. Setelah membentang, urutan waktu tidak lagi melekat pada nama; itulah harga yang dibayar bentuk lebar dan alasan bentuk panjang lebih cocok untuk data berdimensi waktu.

Ketika pivot Menolak: Data Ganda dan pivot_table #

pivot hanya sanggup menerima data yang pasangan index-kolomnya unik — satu nilai untuk tiap persimpangan. Nyatanya, rekap dunia nyata sering memuat entri ganda: koreksi yang tercatat terpisah dari catatan awalnya. Tabel berikut memuat kopi susu Januari dua kali — 120 gelas tercatat lebih dulu, lalu koreksi 12 gelas menyusul.

python
import pandas as pd

panjang = pd.DataFrame({
    "menu": ["kopi susu", "kopi susu", "kopi susu",
             "teh tawar", "teh tawar", "teh tawar"],
    "bulan": ["Januari", "Februari", "Januari",
              "Januari", "Februari", "Maret"],
    "terjual": [120, 95, 12, 80, 85, 90],
})

try:
    panjang.pivot(index="menu", columns="bulan", values="terjual")
except ValueError as galat:
    print("Galat:", galat)
print()

rekap = panjang.pivot_table(index="menu", columns="bulan",
                            values="terjual", aggfunc="sum")
print(rekap)

Output yang Dihasilkan:

text
Galat: Index contains duplicate entries, cannot reshape

bulan      Februari  Januari  Maret
menu                               
kopi susu      95.0    132.0    NaN
teh tawar      85.0     80.0   90.0

Mari kita telaah dua sikap itu:

  1. Baris 11-14 (try/except ValueError): pivot menolak menebak nilai mana yang berhak mengisi persimpangan kopi susu-Januari, dan ia memilih berhenti — galat Index contains duplicate entries, cannot reshape. Kegagalan yang jelas lebih baik daripada tebakan diam-diam.
  2. Baris 17-18 (pivot_table(..., aggfunc="sum")): pivot_table menerima data ganda dengan satu syarat — kita harus menyatakan cara merangkumnya. aggfunc="sum" menjumlahkan entri yang berbagi persimpangan: 120 + 12 = 132 gelas untuk kopi susu Januari.
  3. Sel Maret milik kopi susu: kosong (NaN) karena tidak ada satu pun catatan untuk pasangan itu. Bila kekosongan berarti nol, tambahkan fill_value=0 agar tabel rekap berisi angka utuh — keputusan yang sama dengan strategi fillna pada chapter Menangani Missing Values .

Kapan Memakai Bentuk yang Mana #

Kedua bentuk bukan pesaing melainkan pegawai yang berbeda tugasnya. Panduannya ringkas:

KebutuhanBentuk yang tepatAlatnya
Analisis: menghitung, menyaring, menggabungkanPanjangmelt
Grafik berkelompok dan sumbu kategoriPanjangmelt
Penyimpanan yang tumbuh aman tiap periode baruPanjangcukup menambah baris
Laporan ringkas untuk dibaca manusiaLebarpivot / pivot_table
Perbandingan antarperiode berdampinganLebarpivot / pivot_table

Alasan di balik baris pertama tabel itu bisa kita buktikan. Misalnya kita ingin total terjual per menu dan per bulan dari data bentuk panjang — masing-masing cukup satu baris groupby .

python
import pandas as pd

rekap = pd.DataFrame({
    "menu": ["kopi susu", "teh tawar", "matcha latte", "americano"],
    "Januari": [120, 80, 60, 70],
    "Februari": [95, 85, 55, 65],
    "Maret": [110, 90, 75, 60],
})
panjang = rekap.melt(id_vars="menu", var_name="bulan", value_name="terjual")

per_menu = panjang.groupby("menu", as_index=False)["terjual"].sum()
print(per_menu)
print()
per_bulan = panjang.groupby("bulan", as_index=False)["terjual"].sum()
print(per_bulan)

Output yang Dihasilkan:

text
           menu  terjual
0     americano      195
1     kopi susu      325
2  matcha latte      190
3     teh tawar      255

      bulan  terjual
0  Februari      300
1   Januari      330
2     Maret      335

Mari kita telaah kemudahannya:

  1. Baris 11 dan 14 (groupby(...)["terjual"].sum()): total per menu dan total per bulan lahir dari pola yang sama — kelompokkan menurut satu kolom, jumlahkan kolom nilai. Pada bentuk lebar, total per bulan masih mudah (rekap["Januari"].sum()), tetapi total per menu harus menjumlahkan tiga kolom berbeda — dan bertambah setiap bulan baru.
  2. Hasil terurut menurut abjad: groupby mengurutkan kelompoknya secara bawaan; urutan waktu Januari-Februari-Maret tidak kembali dengan sendirinya. Bila laporan menuntut urutan bulan, kita bisa mengurutkannya kembali lewat kolom khusus urutan atau pd.Categorical — alat kategori yang sudah kita kenal pada chapter Memperbaiki Tipe Data .

Rangkuman #

  • Bentuk lebar meletakkan periode sebagai kolom dan nyaman dibaca; bentuk panjang memuat satu pengamatan per baris dan ramah analisis — keduanya representasi data yang sama.
  • melt(id_vars=..., var_name=..., value_name=...) melelehkan kolom menjadi pasangan nama-isi; bentuk (4, 4) menjadi (12, 3).
  • pivot(index=..., columns=..., values=...) menekuk kembali; nama kolom yang membentang menjadi columns.name, dan hasilnya terurut menurut abjad.
  • pivot menolak data ganda dengan ValueError; pivot_table merangkumnya lewat aggfunc, dengan fill_value untuk persimpangan yang kosong.
  • Kerja analisis dikerjakan pada bentuk panjang dengan groupby; bentuk lebar disiapkan menjelang laporan.

Bentuk data kini lentur: kita bisa menyimpannya panjang, menggabungkannya, lalu menyajikannya lebar kapan pun dibutuhkan. Satu hal terakhir sebelum Section ini usai: seluruh pembersihan yang telah kita jalani harus bisa diuji ulang secara berulang — bukan sekadar dipercaya. Kita tutup perjalanan dengan chapter Validasi Kualitas Data .