melt() bekerja pada kolom biasa, sedangkan stack() dan unstack() bekerja pada struktur index. stack() menumpuk kolom menjadi level index terdalam sehingga DataFrame lebar berubah menjadi Series panjang ber-MultiIndex; unstack() memindahkan level index menjadi kolom sebaliknya. Keduanya adalah mesin pembentuk ulang di balik pivot() dan melt(), sehingga memahaminya membuat perilaku kedua method itu terasa jelas. Pada Chapter ini kita memutar tabel penjualan dua kota dengan stack dan unstack, memilih level yang dipindahkan, dan mencatat perubahan perilaku penting pada pandas 3.0.

stack(): Kolom Menjadi Level Index Terdalam #

Method stack() tanpa argumen memindahkan seluruh label kolom ke level index baru yang menempel di posisi terdalam. File berikut memakai tabel dengan index bernama bulan dan nama axis kolom kota, lalu memutar-mutarnya bolak-balik.

python
import pandas as pd

lebar = pd.DataFrame({
    "Bandung": [120, 135],
    "Jakarta": [200, 180],
}, index=["Jan", "Feb"])
lebar.index.name = "bulan"
lebar.columns.name = "kota"

print(lebar)
print()

# stack: kolom -> level index terdalam
st = lebar.stack()
print(st)
print()
print(st.index.names)
print()

# unstack membalikkan stack
print(st.unstack())
print()

# unstack(level=...) memilih level yang dipindahkan
panjang = pd.DataFrame({
    "kota": ["Bandung", "Bandung", "Jakarta"],
    "bulan": ["Jan", "Feb", "Jan"],
    "omzet": [120, 135, 200],
})
multi = panjang.set_index(["kota", "bulan"])["omzet"]
print(multi.unstack(level="kota"))
print()

# Kombinasi yang tidak ada menjadi NaN, bisa diisi fill_value
print(multi.unstack(fill_value=0))
print()

# stack implementasi baru TIDAK menghapus baris NaN
dengan_nan = pd.DataFrame({
    "A": [1.0, 2.0],
    "B": [3.0, None],
}, index=["x", "y"])
dengan_nan.index.name = "id"
dengan_nan.columns.name = "grup"
print(dengan_nan.stack())
print()

# Parameter dropna sudah dihapus pada pandas 3.0
try:
    dengan_nan.stack(dropna=False)
except ValueError as e:
    print("ValueError:", e)

Output yang Dihasilkan:

text
kota   Bandung  Jakarta
bulan                  
Jan        120      200
Feb        135      180

bulan  kota   
Jan    Bandung    120
       Jakarta    200
Feb    Bandung    135
       Jakarta    180
dtype: int64

['bulan', 'kota']

kota   Bandung  Jakarta
bulan                  
Jan        120      200
Feb        135      180

kota   Bandung  Jakarta
bulan                  
Feb      135.0      NaN
Jan      120.0    200.0

bulan    Feb  Jan
kota             
Bandung  135  120
Jakarta    0  200

id  grup
x   A       1.0
    B       3.0
y   A       2.0
    B       NaN
dtype: float64

ValueError: dropna must be unspecified as the new implementation does not introduce rows of NA values. This argument will be removed in a future version of pandas.

Mari kita telaah bagian-bagian pentingnya:

  1. Baris 12-17 (stack): tabel 2x2 menjadi Series enam baris; label kolom Bandung dan Jakarta pindah menjadi level index kedua, terlihat dari st.index.names yang berisi ['bulan', 'kota'].
  2. Baris 18-20 (unstack): st.unstack() mengembalikan bentuk lebar awal; stack() lalu unstack() merupakan pasangan bolak-balik yang saling membalikkan.
  3. Baris 21-28 (unstack(level="kota")): pada MultiIndex dua level, argumen level menentukan level mana yang menjadi kolom; nama level ("kota") atau posisi angka sama-sama diterima.
  4. Baris 30-32 (fill_value): kombinasi Jakarta-Feb tidak ada di data, sehingga unstack() mengisinya NaN dan fill_value=0 menggantinya.
  5. Baris 33-46: None pada sel kolom B bertahan sebagai NaN setelah stack(); implementasi baru pandas 3.0 tidak menghapus baris ber-NaN, dan argumen dropna yang dulu mengaturnya sudah dihapus (galatnya dicetak di baris 42-46).

unstack(): Level Index Menjadi Kolom #

unstack() adalah kebalikan stack dan lazim dipakai setelah agregasi groupby ber-MultiIndex; hasil groupby(["kota", "bulan"]).sum() lebih mudah dibandingkan antar kolom setelah unstack(). Level terdalam menjadi kolom secara default, sehingga multi.unstack() tanpa argumen setara dengan multi.unstack(level=-1). Ketika kombinasi level tidak lengkap, sel yang tidak memiliki sumber diisi NaN, dan parameter fill_value menggantinya dengan nilai pilihan kita; pada data lengkap, argumen tersebut tidak berefek apa pun.

Perilaku Baru stack() pada pandas 3.0 #

Pandas 3.0 memakai implementasi stack baru yang membawa dua perubahan nyata. Pertama, baris ber-NaN tidak lagi dihapus saat menumpuk; tabel dengan sel kosong menghasilkan baris NaN yang terlihat seperti keluaran di atas, dan Anda membersihkannya sendiri dengan st.dropna() bila perlu. Kedua, parameter dropna dihapus sehingga stack(dropna=False) langsung memunculkan ValueError, dan argumen future_stack yang sempat menjadi jembatan di pandas 2.x kini usang; cukup panggil stack() tanpa argumen tambahan. Perubahan ini tercantum di whatsnew pandas 3.0 .

Referensi lengkapnya ada di user guide reshaping beserta dokumentasi method pandas.DataFrame.stack dan pandas.DataFrame.unstack . Dengan stack dan unstack di tangan, kita sudah menguasai seluruh alat pembentuk ulang; Chapter berikutnya merangkum kapan format lebar atau panjang lebih pantas dipakai.