Operasi pandas jarang gagal keras karena masalah index — justru itu yang membuatnya licik. Dua Series dengan index berbeda tetap bisa dijumlahkan, hasilnya berisi NaN tanpa satu pun error, dan baris yang “menghilang” dari laporan baru disadari belakangan. Chapter ini melatih kita melacak tiga masalah index yang paling sering terjadi: NaN akibat reindex() dan alignment, index duplikat yang melipatgandakan hasil seleksi, serta kebingungan label versus posisi setelah filter.
Aturan Alignment yang Sering Terlupakan #
Saat dua Series atau dua DataFrame dioperasikan (misalnya dijumlahkan), pandas tidak membandingkan posisi baris, melainkan mencocokkan label index terlebih dahulu. Hasilnya mengikuti gabungan (union) semua label dari kedua sisi; label yang hanya ada di satu sisi diberi nilai NaN. Perilaku ini disebut alignment dan berjalan diam-diam tanpa error. Script berikut memperagakan alignment, reindex(), index duplikat, dan perbedaan label versus posisi dalam satu rangkaian debug yang bisa dijalankan ulang.
import pandas as pd
omzet = pd.DataFrame({"omzet": [150_000, 120_000, 95_000]}, index=["jan", "feb", "mar"])
omzet.index.name = "bulan"
print("--- reindex menambah label baru ---")
hasil = omzet.reindex(["jan", "feb", "mar", "apr"])
print(hasil)
print("label tanpa data:", hasil[hasil["omzet"].isna()].index.tolist())
print()
print("--- alignment penjumlahan dua Series ---")
januari = pd.Series({"Jakarta": 150, "Bandung": 120, "Surabaya": 95})
februari = pd.Series({"Bandung": 130, "Surabaya": 100, "Medan": 60})
total = januari + februari
print(total)
print("total mentah :", total.sum())
total = total.fillna(0)
print("setelah fillna(0):", total.to_dict())
print("total bersih :", total.sum())
print()
print("--- index duplikat ---")
transaksi = pd.DataFrame(
{
"kode": ["TRX-001", "TRX-002", "TRX-003"],
"cabang": ["Jakarta", "Bandung", "Jakarta"],
"omzet": [150_000, 120_000, 95_000],
},
index=[0, 1, 0],
)
print("is_unique :", transaksi.index.is_unique)
print("duplicated:", transaksi.index.duplicated().tolist())
print(transaksi.loc[0])
print("jumlah baris loc[0]:", len(transaksi.loc[0]))
print()
print("--- label vs posisi setelah filter ---")
urut = pd.DataFrame({"omzet": [10, 20, 30, 40]})
terpilih = urut[urut["omzet"] > 10]
print(terpilih)
print("label 2 :", terpilih.loc[2, "omzet"])
print("posisi 2:", terpilih.iloc[2, 0])
print()
print("setelah reset_index(drop=True):")
print(terpilih.reset_index(drop=True))
print()
print("--- loc dipakai padahal index string ---")
berlabel = pd.DataFrame({"omzet": [50, 60]}, index=["A", "B"])
try:
berlabel.loc[0]
except KeyError as exc:
print("loc[0] ->", type(exc).__name__, "|", str(exc))
try:
berlabel.iloc["A"]
except TypeError as exc:
print('iloc["A"] ->', type(exc).__name__, "|", str(exc).splitlines()[0])Output yang Dihasilkan:
--- reindex menambah label baru ---
omzet
bulan
jan 150000.0
feb 120000.0
mar 95000.0
apr NaN
label tanpa data: ['apr']
--- alignment penjumlahan dua Series ---
Bandung 250.0
Jakarta NaN
Medan NaN
Surabaya 195.0
dtype: float64
total mentah : 445.0
setelah fillna(0): {'Bandung': 250.0, 'Jakarta': 0.0, 'Medan': 0.0, 'Surabaya': 195.0}
total bersih : 445.0
--- index duplikat ---
is_unique : False
duplicated: [False, False, True]
kode cabang omzet
0 TRX-001 Jakarta 150000
0 TRX-003 Jakarta 95000
jumlah baris loc[0]: 2
--- label vs posisi setelah filter ---
omzet
1 20
2 30
3 40
label 2 : 30
posisi 2: 40
setelah reset_index(drop=True):
omzet
0 20
1 30
2 40
--- loc dipakai padahal index string ---
loc[0] -> KeyError | 0
iloc["A"] -> TypeError | Cannot index by location index with a non-integer keyMari kita bedah bagian pentingnya:
omzet.reindex(["jan", "feb", "mar", "apr"]):reindex()membentuk DataFrame baru dengan label yang kita minta. Labelaprtidak ada di data asli, sehingga barisnya berisiNaN, dan dtype kolom naik dariint64kefloat64karenaNaNtidak memiliki representasi integer. Ketika jumlah baris laporan tidak cocok,reindex()adalah penyebab yang layak diperiksa lebih dulu.januari + februari: kedua Series dicocokkan berdasarkan nama cabang. Hasilnya memuat union labelBandung,Jakarta,Medan,Surabaya;JakartabernilaiNaNkarena tidak ada difebruari, danMedanbernilaiNaNkarena tidak ada dijanuari. Nilaitotal mentah : 445.0dihitung dengan melewatkanNaN, jadi laporan total bisa terlihat “selesai” padahal dua cabang belum dihitung.total.fillna(0): perbaikan yang disengaja setelah kita memahami kenapaNaNmuncul. Langkah debugging yang benar adalah menelusuri sumberNaNdulu (cetak index, hitungisna().sum()), bukan langsung menutupnya.transaksi.loc[0]: index DataFrame ini adalah[0, 1, 0], sehingga satu pemanggilanloc[0]mengembalikan dua baris, yaituTRX-001danTRX-003. Flagtransaksi.index.is_uniquemelaporkanFalsedanduplicated()menandai posisi duplikat. Jika hasil seleksi lebih banyak dari perkiraan, periksa keunikan index sebelum menyalahkan logika lain.terpilih.loc[2, "omzet"]vsterpilih.iloc[2, 0]: setelah filteromzet > 10, label2masih menunjuk baris bernilai30, sedangkan posisi ke-2 (iloc[2]) menunjuk baris bernilai40. Kedua cara memilih baris berbeda hasil karena label tidak dihitung ulang setelah filter.reset_index(drop=True)mengembalikan penomoran berurutan sehingga label dan posisi kembali sejajar.berlabel.loc[0]: index DataFrame ini berupa string["A", "B"], jadi label0tidak ada dan munculKeyError. Sebaliknyaberlabel.iloc["A"]melemparTypeErrorkarenailochanya menerima posisi. Pesan error keduanya berbeda dan langsung mengarahkan kita ke sumber kebingungan.
Daftar Periksa Saat Hasil Operasi Tidak Sesuai #
| Gejala | Pemeriksaan pertama |
|---|---|
NaN banyak muncul setelah operasi antar objek | Bandingkan kedua index dengan index.equals() atau cetak keduanya |
| Jumlah baris seleksi lebih banyak dari perkiraan | index.is_unique dan index.duplicated() |
| Nilai berubah dtype setelah operasi | NaN dari alignment menaikkan kolom integer ke float64 |
KeyError padahal barisnya terlihat ada | Apakah kodenya pakai loc padahal maksudnya iloc, atau sebaliknya |
Dengan index dan alignment terkendali, sumber masalah klasik berikutnya justru terjadi ketika dua DataFrame digabungkan: jumlah baris hasil merge yang tidak sesuai ekspektasi. Chapter Debugging Merge membahas alat pemeriksaan untuk kasus itu.
Referensi resmi untuk chapter ini: Indexing and selecting data , pandas.DataFrame.reindex , dan pandas.Index.is_unique .