Idenya
Data sungguhan punya celah: seorang customer melewatkan isian kota, sebuah sensor mati, harga belum dimasukkan. pandas menandai nilai yang hilang sebagai NaN ("not a number"), atau None/NaT di beberapa tipe. Data yang hilang bukan gangguan yang harus dihapus; ia adalah informasi. Mengapa datanya hilang, dan apa yang harus dilakukan analisismu terhadapnya? Setiap pilihan (mengisi, membuang, menyimpan dan menandai) mengubah angkamu, jadi pilihan itu harus disengaja.
import pandas as pd
import numpy as np
customers = pd.DataFrame({
"name": ["Ayu", "Budi", "Citra", "Dimas", "Eka", "Fajar"],
"city": ["Jakarta", "Bandung", None, "Jakarta", np.nan, "Medan"],
"orders": [3, np.nan, 5, 1, 2, np.nan],
"spent": [1245000, 1145000, np.nan, 550000, 410000, 0],
})
print(customers)
name city orders spent
0 Ayu Jakarta 3.0 1245000.0
1 Budi Bandung NaN 1145000.0
2 Citra None 5.0 NaN
3 Dimas Jakarta 1.0 550000.0
4 Eka NaN 2.0 410000.0
5 Fajar Medan NaN 0.0
None dan np.nan sama-sama tampil sebagai nilai hilang. Perhatikan orders berubah menjadi float (3.0): kolom integer NumPy tidak bisa menyimpan NaN, jadi pandas menaikkannya menjadi float.
Menemukan nilai yang hilang
Modul ini memakai tabel customer kecil yang punya beberapa celah. Sebelum kamu bisa memutuskan apa yang dilakukan terhadap nilai yang hilang, kamu perlu tahu di mana letaknya dan berapa banyak. .isna() menandai setiap sel yang kosong, dan menjumlahkan tanda-tanda itu memberi hitungan per kolom:
print(customers.isna().sum())
print(customers[customers["city"].isna()])
name 0
city 2
orders 2
spent 1
dtype: int64
name city orders spent
2 Citra None 5.0 NaN
4 Eka NaN 2.0 410000.0
.isna() (atau kembarannya .isnull()) memberi True di tempat nilainya hilang; .notna() kebalikannya. Jumlahkan untuk menghitung per kolom, atau pakai sebagai mask untuk melihat baris yang terdampak. Melihat barisnya itu penting: mungkin semua kota yang hilang berasal dari satu formulir pendaftaran, yang memberi tahu sesuatu tentang penyebabnya.
NaN tidak sama dengan apa pun, termasuk dirinya sendiri. customers["city"] == np.nan bernilai False untuk setiap baris. Selalu pakai .isna() untuk menguji nilai yang hilang.
Bagaimana nilai yang hilang memengaruhi perhitungan
Inilah alasan nilai yang hilang perlu diputuskan, bukan diabaikan. Method ringkasan pandas diam-diam melewati nilai yang hilang, jadi jawaban yang kamu dapat bergantung pada apa arti celah-celah itu menurutmu:
print(customers["orders"].sum())
print(customers["orders"].mean())
print(customers["orders"].count(), len(customers))
11.0
2.75
4 6
Rata-ratanya 11 / 4 = 2,75, dihitung dari 4 nilai yang diketahui, bukan dari keenam customer. Itu sering masuk akal, tapi tidak selalu: kalau orders yang hilang sebenarnya berarti "nol order", rata-rata yang sebenarnya adalah 11 / 6 ≈ 1,83. Data yang sama, dua jawaban berbeda, tergantung apa arti nilai yang hilang. Itulah sebabnya kamu harus memutuskan.
Perhatikan juga bahwa spent bernilai 0 untuk Fajar dan NaN untuk Citra. Itu fakta yang berbeda: Fajar tidak membelanjakan apa pun; untuk Citra, kita tidak tahu.
Opsi 1: buang
Tanggapan paling sederhana adalah membuang baris (atau kolom) yang punya nilai hilang. Ini juga yang paling mudah berlebihan, jadi perhatikan berapa baris yang tersisa di setiap versi:
print(len(customers.dropna()))
print(len(customers.dropna(subset=["city"])))
print(customers.dropna(axis="columns").columns.tolist())
2
4
['name']
dropna()membuang baris yang punya nilai hilang apa pun: di sini hanya 2 dari 6 yang tersisa, dan itu biasanya terlalu agresif.dropna(subset=["city"])hanya melihat kolom yang kamu sebut. Inilah versi yang paling sering akan kamu pakai.dropna(axis="columns")justru membuang kolom yang punya nilai hilang.
Buang kalau baris yang hilang tidak bisa dipakai untuk pertanyaan yang sedang dijawab (kamu tidak bisa memetakan customer tanpa kota) dan kalau jumlahnya sedikit. Membuang 40% dataset bisa diam-diam membuat hasilmu bias.
Opsi 2: isi
Pilihan lainnya adalah menyimpan barisnya dan mengisi setiap celah dengan nilai yang masuk akal. Apa yang masuk akal tergantung kolomnya: kota yang hilang bisa menjadi "Unknown", dan jumlah order yang hilang mungkin sebenarnya berarti nol:
filled = customers.fillna({"city": "Unknown", "orders": 0})
print(filled[["name", "city", "orders"]])
name city orders
0 Ayu Jakarta 3.0
1 Budi Bandung 0.0
2 Citra Unknown 5.0
3 Dimas Jakarta 1.0
4 Eka Unknown 2.0
5 Fajar Medan 0.0
fillna dengan dictionary mengisi setiap kolom dengan nilainya masing-masing. Pilihan yang umum:
| Isi dengan | Kapan |
|---|---|
label seperti "Unknown" | kategori, supaya barisnya tetap muncul di total per kelompok |
0 | saat hilang benar-benar berarti tidak ada (tidak ada order tercatat = nol order) |
| median atau mean | nilai angka untuk perkiraan cepat; median tahan terhadap outlier |
nilai sebelumnya (ffill) | time series, di mana nilai terakhir yang diketahui masih berlaku |
median_spent = customers["spent"].median()
customers["spent_filled"] = customers["spent"].fillna(median_spent)
print(median_spent, customers["spent_filled"].tolist())
550000.0 [1245000.0, 1145000.0, 550000.0, 550000.0, 410000.0, 0.0]
Mengisi dengan median mempertahankan barisnya, tapi itu mengarang data. Kolomnya juga jadi terlihat lebih pasti dari kenyataannya. Pakai dengan sadar, dan biasanya bersama sebuah penanda.
Forward fill untuk time series
Untuk nilai yang dicatat dari waktu ke waktu, seperti jumlah stok harian, sering ada tebakan yang lebih baik daripada angka tetap. Kalau seseorang lupa menghitung stok hari Selasa, tebakan terbaiknya biasanya angka hari Senin, karena stok tidak berubah kecuali terjadi sesuatu:
stock = pd.Series([24, np.nan, np.nan, 18, np.nan], index=["Mon", "Tue", "Wed", "Thu", "Fri"])
print(stock.ffill().tolist())
[24.0, 24.0, 24.0, 18.0, 18.0]
.ffill() membawa nilai terakhir yang diketahui ke depan: kalau tidak ada yang mencatat stok hari Selasa, kemungkinan besar stoknya masih 24. .bfill() mengisi ke belakang. .interpolate() memperkirakan nilai di antaranya.
Opsi 3: simpan dan tandai
Sering kali langkah terbaik adalah menyimpan nilai yang hilang dan mencatat bahwa nilainya hilang, supaya langkah berikutnya (dan pembaca) bisa melihatnya:
customers["city_missing"] = customers["city"].isna()
print(customers[["name", "city", "city_missing"]])
name city city_missing
0 Ayu Jakarta False
1 Budi Bandung False
2 Citra None True
3 Dimas Jakarta False
4 Eka NaN True
5 Fajar Medan False
Kolom penanda memungkinkanmu melaporkan "4 dari 6 customer punya kota yang diketahui" dengan jujur, dan memungkinkan model atau dashboard memperlakukan yang tidak diketahui secara terpisah.
String kosong dan nilai hilang palsu
Tidak semua celah datang sebagai NaN. File export penuh dengan string kosong, "N/A", "-", "none" dan 0 yang dipakai sebagai "tidak diketahui". Ubah dulu menjadi nilai hilang yang sesungguhnya, supaya alat-alat di atas bekerja:
raw = pd.Series(["Jakarta", "", "N/A", " ", "Bandung", "-"])
clean = raw.str.strip().replace({"": np.nan, "N/A": np.nan, "-": np.nan})
print(clean.isna().sum())
4
Waktu terbaik untuk melakukan ini adalah saat membaca file: pd.read_csv(..., na_values=["N/A", "-"]), seperti yang kamu lihat di modul 2.
Mengapa ini penting untuk kode yang bersih
Buat keputusan soal data yang hilang secara eksplisit dan sejak awal, dalam satu langkah pembersihan, dengan komentar yang menjelaskan alasannya: "orders yang hilang berarti nol order: kolom ini hanya diisi kalau ada order". Hindari menyebar pemanggilan fillna(0) di seluruh analisis, di mana setiap pemanggilan diam-diam mengubah arti datanya. Dan jangan pernah membiarkan NaN sampai ke laporan tanpa penjelasan.
Rangkuman
NaNmenandai nilai yang hilang; kolom integer yang berisiNaNmenjadi float..isna().sum()menghitung per kolom; pakai.isna()sebagai mask untuk memeriksa baris. Jangan pernah membandingkan dengan== np.nan.sum,mean,countmelewati nilai yang hilang: putuskan dulu apa arti nilai yang hilang.dropna(subset=[...])membuang baris yang tidak punya kolom penting;dropna()biasa umumnya terlalu agresif.fillna({...})mengisi per kolom: label, 0, median, atauffill()untuk time series.- Tandai nilai yang hilang dengan kolom boolean kalau itu penting.
- Ubah nilai hilang palsu (
"","N/A","-") menjadiNaNterlebih dahulu.