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:
| menu | Januari | Februari | Maret |
|---|---|---|---|
| kopi susu | 120 | 95 | 110 |
| teh tawar | 80 | 85 | 90 |
| matcha latte | 60 | 55 | 75 |
| americano | 70 | 65 | 60 |
Wajah panjang memindahkan nama bulan ke dalam barisnya sendiri: tiga kolom (menu, bulan, terjual), dua belas baris. Cuplikan enam barisnya tampak seperti ini:
| menu | bulan | terjual |
|---|---|---|
| kopi susu | Januari | 120 |
| kopi susu | Februari | 95 |
| kopi susu | Maret | 110 |
| teh tawar | Januari | 80 |
| teh tawar | Februari | 85 |
| … | … | … |
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.
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:
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:
- 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. - Baris 12 (
melt(id_vars="menu", ...)):menuditahan sebagai identitas; kolomJanuari,Februari, danMaretdilelehkan. Nama bulan mengalir ke kolombulan(var_name), angkanya mengalir ke kolomterjual(value_name). - 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.
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:
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:
- Baris 11 (
pivot(index="menu", columns="bulan", values="terjual")): nilaimenumenjadi index baris, nilaibulanmembentang menjadi kolom, danterjualmengisi persimpangannya — persis tabel rekap yang kita mulai. lebar.columns.name: nama kolom yang membentang (bulan) tersimpan sebagai nama deret kolom, bukan kolom biasa. Bilamenuperlu kembali menjadi kolom biasa,lebar.reset_index()memindahkannya keluar dari index — kebiasaan yang baik sebelum menyimpan hasil.- 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.
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:
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.0Mari kita telaah dua sikap itu:
- Baris 11-14 (
try/except ValueError):pivotmenolak menebak nilai mana yang berhak mengisi persimpangankopi susu-Januari, dan ia memilih berhenti — galatIndex contains duplicate entries, cannot reshape. Kegagalan yang jelas lebih baik daripada tebakan diam-diam. - Baris 17-18 (
pivot_table(..., aggfunc="sum")):pivot_tablemenerima 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. - Sel
Maretmilik kopi susu: kosong (NaN) karena tidak ada satu pun catatan untuk pasangan itu. Bila kekosongan berarti nol, tambahkanfill_value=0agar tabel rekap berisi angka utuh — keputusan yang sama dengan strategifillnapada chapter Menangani Missing Values .
Kapan Memakai Bentuk yang Mana #
Kedua bentuk bukan pesaing melainkan pegawai yang berbeda tugasnya. Panduannya ringkas:
| Kebutuhan | Bentuk yang tepat | Alatnya |
|---|---|---|
| Analisis: menghitung, menyaring, menggabungkan | Panjang | melt |
| Grafik berkelompok dan sumbu kategori | Panjang | melt |
| Penyimpanan yang tumbuh aman tiap periode baru | Panjang | cukup menambah baris |
| Laporan ringkas untuk dibaca manusia | Lebar | pivot / pivot_table |
| Perbandingan antarperiode berdampingan | Lebar | pivot / 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
.
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:
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 335Mari kita telaah kemudahannya:
- 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. - Hasil terurut menurut abjad:
groupbymengurutkan 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 ataupd.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 menjadicolumns.name, dan hasilnya terurut menurut abjad.pivotmenolak data ganda denganValueError;pivot_tablemerangkumnya lewataggfunc, denganfill_valueuntuk 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 .