Menghapus bukan satu-satunya jalan. Pada banyak kasus, mengisi kekosongan dengan nilai pengganti mempertahankan baris yang mungkin masih berguna, dengan syarat nilai penggantinya dipilih berdasarkan alasan yang jelas. Chapter ini melatih fillna() untuk nilai tetap dan statistik ringkas, serta ffill() dan bfill() untuk data yang urutannya bermakna. Ketiga strategi kita uji pada kolom yang berbeda dari dataset pesanan yang sama.

Tiga Strategi Pengisian #

StrategiPola kodeCocok untuk
Nilai konstantadf["kolom"].fillna(0)Sel kosong yang bermakna nol, mis. transaksi yang gagal tercatat
Statistik ringkasdf["kolom"].fillna(df["kolom"].median())Menjaga distribusi angka tanpa terdistorsi satu nilai ekstrem
Meneruskan nilaidf["kolom"].ffill()Data berurutan seperti deret waktu atau log

Tidak ada strategi yang benar mutlak; pilihannya menuntut pemahaman arti kolom di dunia nyata, dan dokumentasi pandas.DataFrame.fillna merangkum seluruh opsi parameter yang tersedia. Kita praktikkan ketiganya sekaligus pada dataset pesanan: kolom total_belanja diisi nol, kolom umur diisi median, dan kolom kota diisi dengan ffill():

python
import pandas as pd

pesanan = pd.DataFrame(
    {
        "id_pesanan": [
            "PSC-101", "PSC-102", "PSC-103", "PSC-104", "PSC-105",
            "PSC-106", "PSC-101", "PSC-102", "PSC-103", "PSC-107",
        ],
        "nama_pelanggan": [
            "Budi", "Siti", "Dewi", "Andi", "Rina",
            "Tono", "Budi", "Siti", "Dewi", "Sari",
        ],
        "kota": [
            "Jakarta", "Bandung", "Surabaya", "JAKARTA", "Bandung",
            None, "Jakarta", "Bandung", "Surabaya", "Medan",
        ],
        "umur": [29, 34, None, 41, 27, 38, 29, 34, None, 45],
        "total_belanja": [
            250000, 480000, 150000, None, 320000,
            275000, 250000, 480000, 165000, 190000,
        ],
    }
)

isi_nol = pesanan.copy()
isi_nol["total_belanja"] = isi_nol["total_belanja"].fillna(0)
print(isi_nol["total_belanja"])
print()

median_umur = pesanan["umur"].median()
print("Median umur :", median_umur)
isi_median = pesanan.copy()
isi_median["umur"] = isi_median["umur"].fillna(median_umur)
print(isi_median["umur"])
print()

isi_ffill = pesanan.copy()
isi_ffill["kota"] = isi_ffill["kota"].ffill()
print(isi_ffill["kota"])
print("Sisa kota kosong:", isi_ffill["kota"].isna().sum())

Output yang Dihasilkan:

text
0    250000.0
1    480000.0
2    150000.0
3         0.0
4    320000.0
5    275000.0
6    250000.0
7    480000.0
8    165000.0
9    190000.0
Name: total_belanja, dtype: float64

Median umur : 34.0
0    29.0
1    34.0
2    34.0
3    41.0
4    27.0
5    38.0
6    29.0
7    34.0
8    34.0
9    45.0
Name: umur, dtype: float64

0     Jakarta
1     Bandung
2    Surabaya
3     JAKARTA
4     Bandung
5     Bandung
6     Jakarta
7     Bandung
8    Surabaya
9       Medan
Name: kota, dtype: str
Sisa kota kosong: 0

Mari kita telaah peran setiap bagian skrip di atas:

  1. Baris 25-27: pesanan.copy() membuat salinan kerja agar tiga strategi tidak saling menimpa; fillna(0) mengisi total PSC-104 yang kosong menjadi 0.0.
  2. Baris 30-31 (median_umur = pesanan["umur"].median()): median dihitung dari delapan nilai terisi dan menghasilkan 34.0 karena pandas melewati NaN secara otomatis.
  3. Baris 32-34 (isi_median["umur"] = isi_median["umur"].fillna(median_umur)): dua sel kosong umur pada PSC-103 diisi 34.0.
  4. Baris 37-38 (isi_ffill["kota"] = isi_ffill["kota"].ffill()): method ffill meneruskan nilai terisi terakhir ke bawah; baris 5 yang kotanya kosong mewarisi "Bandung" dari baris 4.
  5. Baris 40: isna().sum() menyatakan kolom kota kini bebas kekosongan.

Memilih Nilai Pengganti yang Jujur #

Median lebih tahan terhadap nilai ekstrem daripada rata-rata: satu pesanan bernilai ratusan juta bisa menarik mean() jauh ke atas, sedangkan median() tetap berada di tengah data. Pada dataset kecil kita, mean() umur menghasilkan 34.625 dan median() 34.0; selisihnya kecil, tetapi pada data yang mengandung outlier selisih itu membengkak. Mengisi dengan nol menuntut kehati-hatian tersendiri: nol pada kolom total_belanja bermakna “transaksi tanpa nilai”, padahal arti sesungguhnya adalah “tidak diketahui”. Bila kedua arti itu berbeda, konstanta bukan pilihan yang jujur dan baris terbaik dibiarkan kosong atau dihapus.

ffill() hanya masuk akal bila urutan baris membawa makna, misalnya pencatatan kondisi per jam; baris kosong mewarisi kondisi terakhir yang tercatat. Untuk dataset pesanan yang urutan barisnya arbitrer, contoh ini semata latihan mekanisme dan tidak perlu kita tiru. Kebalikannya, bfill() mengisi dari nilai berikutnya; method ini berguna ketika kondisi pertama yang lebih mewakili, misalnya stok awal hari yang diketahui lebih dulu.

Kekosongan kini punya dua jalur penyelesaian: dihapus lewat dropna() atau diisi lewat fillna() beserta saudaranya. Masalah data berikutnya lebih senyap karena terlihat sah: baris yang tercatat dua kali. Chapter Menangani Nilai Duplikat mengajak kita mendeteksi dan menyingkirkan baris ganda dengan duplicated() dan drop_duplicates(), dan seluruh ringkasan perilaku missing value tetap bisa dirujuk ke panduan Working with missing data .