Koreksi manual pada chapter sebelumnya menyasar kasus yang sudah kita ketahui. Ketika data baru terus mengalir, kita perlu pemeriksaan yang dapat dijalankan ulang kapan saja tanpa membaca baris demi baris: validasi rentang. Ide dasarnya sederhana; setiap kolom numerik diberi rentang wajar, lalu boolean mask menandai baris yang berada di luar rentang tersebut. Hasilnya berupa daftar pelanggaran yang terukur, bukan kesan sekilas.

Rentang Wajar sebagai Kontrak Data #

Aturan rentang ditetapkan bersama pemilik data dan ditulis eksplisit di kode, bukan disimpan di kepala. Pada skrip di bawah ini, dictionary aturan_rentang memasangkan nama kolom dengan tuple (batas_bawah, batas_atas), sehingga menambah aturan baru hanya berarti menambah satu baris dictionary. Kontrak semacam ini juga memudahkan rekan tim membaca ulang skrip beberapa bulan kemudian.

Pemilihan rentang mengikuti makna kolom, bukan sebaran datanya. Umur pelanggan manusia dibatasi 0 sampai 120 tahun, sedangkan persentase diskon dibatasi 0 sampai 100 karena nilainya memang persentase. Karena seluruh aturan hidup di satu dictionary, perubahan batas cukup dilakukan di satu tempat tanpa menyentuh logika pemeriksaannya.

Memeriksa Kolom dengan between #

Skrip berikut memeriksa dua kolom terhadap aturannya masing-masing, lalu menampilkan detail baris yang melanggar. Referensi resmi method ini ada di pandas.Series.between .

python
import pandas as pd

data = pd.DataFrame({
    "pelanggan": ["Budi", "Siti", "Dewi", "Andi", "Rina", "Tono", "Lina"],
    "umur": [29, 34, -5, 41, 127, 35, 31],
    "persen_diskon": [10, 15, 120, 8, 25, -3, 18],
})

aturan_rentang = {
    "umur": (0, 120),
    "persen_diskon": (0, 100),
}

for kolom, (batas_bawah, batas_atas) in aturan_rentang.items():
    mask_valid = data[kolom].between(batas_bawah, batas_atas)
    pelanggaran = int((~mask_valid).sum())
    print(f"{kolom}: rentang wajar {batas_bawah} sampai {batas_atas}, pelanggaran = {pelanggaran}")
    if pelanggaran > 0:
        print(data.loc[~mask_valid, ["pelanggan", kolom]].to_string(index=False))
    print()

mask_lolos = data["umur"].between(0, 120) & data["persen_diskon"].between(0, 100)
print("Baris yang lolos semua aturan :", int(mask_lolos.sum()), "dari", len(data))

Output yang Dihasilkan:

text
umur: rentang wajar 0 sampai 120, pelanggaran = 2
pelanggan  umur
     Dewi    -5
     Rina   127

persen_diskon: rentang wajar 0 sampai 100, pelanggaran = 2
pelanggan  persen_diskon
     Dewi            120
     Tono             -3

Baris yang lolos semua aturan : 4 dari 7

Mari kita telaah apa yang terjadi pada kode di atas:

  1. Baris 9-12 (aturan_rentang = {...}): dua aturan disimpan sebagai dictionary; kunci adalah nama kolom dan nilainya adalah tuple batas.
  2. Baris 14 (for kolom, (batas_bawah, batas_atas) in aturan_rentang.items():): loop dictionary memastikan setiap kolom dicek dengan aturannya sendiri tanpa kode yang berulang.
  3. Baris 15 (mask_valid = data[kolom].between(batas_bawah, batas_atas)): .between() menghasilkan mask True untuk nilai di dalam rentang, termasuk kedua ujungnya.
  4. Baris 16 (pelanggaran = int((~mask_valid).sum())): operator ~ membalik mask menjadi pelanggaran, dan .sum() menghitung barisnya; int() membuat angka tampil sebagai bilangan biasa, bukan np.int64.
  5. Baris 18-19 (if pelanggaran > 0: dan baris setelahnya): detail baris pelanggar ditampilkan dengan data.loc[~mask_valid, ["pelanggan", kolom]] hanya bila ada pelanggaran.

Output menunjukkan kedua kolom sama-sama memiliki dua pelanggaran: umur berisi -5 dan 127, sedangkan persen_diskon berisi 120 dan -3. Pelanggaran semacam ini adalah bahan kerja teknik koreksi dari chapter sebelumnya. Panduan seleksi dengan mask boolean dibahas lebih luas di user guide indexing .

Menggabungkan Beberapa Aturan Menjadi Satu Mask #

Validasi per kolom baru separuh pekerjaan; sering kali kita ingin tahu baris mana yang lolos semua aturan sekaligus. Dua mask .between() digabung dengan operator &, lalu .sum() pada mask hasilnya menghitung baris yang lolos. Dari tujuh baris data, empat baris lolos seluruh aturan; tiga sisanya perlu ditindaklanjuti dengan pola koreksi chapter Menangani Nilai Tidak Valid.

Laporan ringkas per kolom pada skrip ini sebenarnya sudah berbentuk mini laporan kualitas data. Pada chapter penutup section ini, kita mengembangkannya menjadi laporan lengkap yang merangkum seluruh temuan pembersihan.