Menghitung lebih dulu, membersihkan kemudian — prinsip itu berlaku untuk nilai kosong. Sebelum memutuskan menghapus atau mengisi, kita perlu jawaban pasti atas dua pertanyaan: berapa banyak sel kosong di setiap kolom, dan baris mana saja yang terdampak. Chapter ini melatih dua fungsi pendeteksi utama pandas, isna() dan notna(), lalu menggabungkannya dengan sum() serta penyaringan boolean dari Section sebelumnya. Dataset pesanan kotor tetap kita pakai agar hasilnya mudah dicocokkan dengan Chapter-Chapter sebelumnya.

Peta Boolean Sel Kosong #

isna() mengembalikan struktur data baru dengan bentuk sama seperti aslinya, tetapi isinya True untuk sel kosong dan False untuk sel terisi. Kebalikannya, notna() memberi True untuk sel yang terisi. Karena hasilnya boolean, kita bisa membacanya sebagai peta lokasi kekosongan pada seluruh tabel. Mari kita jalankan keduanya pada dataset pesanan:

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,
        ],
    }
)

print(pesanan.isna())
print()
print("Sel kosong per kolom:")
print(pesanan.isna().sum())
print("Total sel kosong :", pesanan.isna().sum().sum())
print()
print("Baris dengan kota kosong:")
print(pesanan[pesanan["kota"].isna()])
print()
print("Sel terisi per kolom (notna):")
print(pesanan.notna().sum())

Output yang Dihasilkan:

text
   id_pesanan  nama_pelanggan   kota   umur  total_belanja
0       False           False  False  False          False
1       False           False  False  False          False
2       False           False  False   True          False
3       False           False  False  False           True
4       False           False  False  False          False
5       False           False   True  False          False
6       False           False  False  False          False
7       False           False  False  False          False
8       False           False  False   True          False
9       False           False  False  False          False

Sel kosong per kolom:
id_pesanan        0
nama_pelanggan    0
kota              1
umur              2
total_belanja     1
dtype: int64
Total sel kosong : 4

Baris dengan kota kosong:
  id_pesanan nama_pelanggan kota  umur  total_belanja
5    PSC-106           Tono  NaN  38.0       275000.0

Sel terisi per kolom (notna):
id_pesanan        10
nama_pelanggan    10
kota               9
umur               8
total_belanja      9
dtype: int64

Mari kita telaah peran setiap bagian skrip di atas:

  1. Baris 25 (print(pesanan.isna())): matriks boolean berukuran 10x5; hanya 4 sel berisi True, tepat pada lokasi kekosongan yang kita tanam.
  2. Baris 28 (pesanan.isna().sum()): sum() menjumlahkan kolom demi kolom; True dihitung 1 dan False dihitung 0, sehingga hasilnya banyaknya sel kosong per kolom.
  3. Baris 29 (pesanan.isna().sum().sum()): penjumlahan kedua meratakan seluruh matriks menjadi satu angka, yaitu 4 sel kosong.
  4. Baris 31-32 (pesanan[pesanan["kota"].isna()]): Series boolean dipakai sebagai mask sehingga hanya baris PSC-106 milik Tono yang tampil.
  5. Baris 35 (pesanan.notna().sum()): kebalikan isna(); kolom umur terisi 8 dari 10 sel.

Dari Hitungan ke Persentase #

Angka absolut berubah makna ketika ukuran tabel berbeda; 4 sel kosong pada tabel 50 sel jauh lebih serius daripada 4 sel kosong pada tabel 5 juta sel. Untuk membandingkan antar kolom maupun antar file, gunakan rata-rata: pesanan.isna().mean() * 100 menghasilkan persentase kekosongan per kolom, dan pada data kita kolom umur kehilangan 20 persen. Kombinasi isna().mean() sering muncul di laporan kualitas data industri karena ringkas dan langsung terbaca. Panduan Working with missing data memuat pola serupa sebagai titik awal investigasi.

Menyaring Baris yang Terdampak #

Mask dari isna() bisa langsung dimasukkan ke kurung siku DataFrame, persis pola filtering boolean yang kita pelajari pada Section Indexing dan Seleksi Data. Bila baris yang dicari kosong pada salah satu dari dua kolom, gabungkan dua mask dengan operator | dan tanda kurung, misalnya pesanan[pesanan["kota"].isna() | pesanan["umur"].isna()] yang menampilkan 3 baris pada data kita. Sebaliknya, untuk melihat baris yang benar-benar lengkap, gunakan pesanan[pesanan.notna().all(axis=1)]; jumlahnya sama persis dengan baris yang tersisa setelah dropna(), yang kita bedah pada Chapter berikutnya.

Kini kita mampu memetakan kekosongan dengan presisi: berapa banyak, di kolom apa, dan di baris mana. Fungsi pandas.DataFrame.isna beserta saudaranya pandas.DataFrame.notna juga berlaku pada Series dengan perilaku yang sama. Tersisa satu keputusan strategi: menghapus baris yang kosong atau mengisinya dengan nilai pengganti. Chapter Menghapus Nilai Kosong membahas jalur pertama melalui dropna() beserta parameter how dan subset.