Sepanjang Section ini kita menghapus duplikat, mengisi nilai kosong, memperbaiki tipe, dan merapikan bentuk tabel — dan setiap pembersihan itu menuntut kepercayaan bahwa hasilnya memang bersih. Masalahnya, “percaya” bukanlah alat kerja data scientist: pembersihan yang dilakukan sekali secara manual tidak bisa diuji ulang besok, dan data yang minggu ini bersih bisa kotor lagi pada pengiriman berikutnya. Chapter penutup ini merangkai seluruh pembersihan menjadi satu bingkai yang bisa dijalankan berulang: aturan validasi yang tegas, pemeriksaan integritas antar tabel, dan laporan kualitas yang mendokumentasikan hasilnya — semuanya dalam bentuk kode, bukan dalam bentuk ingatan.

Dua Tabel untuk Divalidasi #

Bahan uji kita adalah hasil akhir dari perjalanan Section ini: tabel produk yang bersih dan tabel penjualan yang menyertainya. Keduanya sengaja kecil agar fokus kita tertuju pada kerangka pemeriksaannya, bukan pada banyaknya baris:

csv
id_produk,produk,harga
P01,kopi susu,24000
P02,matcha latte,28000
P03,americano,22000
P04,teh tawar,20000
P05,es jeruk,18000
csv
tanggal,id_produk,jumlah
2026-02-10,P01,12
2026-02-10,P02,8
2026-02-10,P03,5
2026-02-11,P01,15
2026-02-11,P04,6
2026-02-11,P02,7
2026-02-12,P05,4
2026-02-12,P01,9
2026-02-12,P03,3
2026-02-12,P02,2
python
import pandas as pd

produk = pd.read_csv("produk.csv")
penjualan = pd.read_csv("penjualan.csv")

print("tabel produk:", produk.shape, "| tabel penjualan:", penjualan.shape)
print(produk.to_string(index=False))
print()
print(penjualan.head(5).to_string(index=False))

Output yang Dihasilkan:

text
tabel produk: (5, 3) | tabel penjualan: (10, 3)
id_produk       produk  harga
      P01    kopi susu  24000
      P02 matcha latte  28000
      P03    americano  22000
      P04    teh tawar  20000
      P05     es jeruk  18000

   tanggal id_produk  jumlah
2026-02-10       P01      12
2026-02-10       P02       8
2026-02-10       P03       5
2026-02-11       P01      15
2026-02-11       P04       6

Aturan Validasi dengan assert #

Inti validasi adalah menuangkan harapan kita tentang data menjadi aturan eksplisit — harga harus positif, jumlah pesanan tidak boleh nol, tanggal harus masuk akal. Python menyediakan assert untuk maksud ini: jika kondisinya salah, program berhenti dengan AssertionError membawa pesan kita, alih-alih melanjatkan diam-diam dengan data yang rusak. Aturan itu kita bungkus sebagai fungsi agar bisa dipanggil berulang pada setiap pengiriman data baru:

python
import pandas as pd

produk = pd.read_csv("produk.csv")

def periksa_harga(df):
    """Pastikan seluruh harga positif."""
    bermasalah = df[df["harga"] <= 0]
    assert bermasalah.empty, f"harga tidak positif pada: {bermasalah['id_produk'].tolist()}"

# data bersih: lolos tanpa keluhan
periksa_harga(produk)
print("Pemeriksaan harga pada data bersih: LULUS")

# data uji dengan satu harga salah
produk_uji = produk.copy()
produk_uji.loc[produk_uji["id_produk"] == "P04", "harga"] = 0
try:
    periksa_harga(produk_uji)
except AssertionError as e:
    print("Pemeriksaan harga pada data uji: GAGAL")
    print("AssertionError:", e)

Output yang Dihasilkan:

text
Pemeriksaan harga pada data bersih: LULUS
Pemeriksaan harga pada data uji: GAGAL
AssertionError: harga tidak positif pada: ['P04']

Mari kita telaah baris-baris pentingnya:

  1. Baris 5-7 (fungsi periksa_harga): saringan df["harga"] <= 0 mengumpulkan baris bermasalah; assert bermasalah.empty hanya lewat bila tidak ada satu pun. Pesan galatnya menyebut ID produk mana yang bermasalah — pesan yang bisa ditindaklanjuti, bukan sekadar “ada yang salah”.
  2. Baris 9-10 (pemanggilan pada data bersih): tidak ada keluaran cetak karena aturannya lolos — kita cetak statusnya sendiri sebagai catatan jalanan.
  3. Baris 13-18 (data uji sengaja dirusak): satu harga diubah ke 0, lalu aturan yang sama menangkapnya persis di tempatnya. Pola “data bersih + data uji yang sengaja salah” adalah cara menguji pemeriksa itu sendiri: aturan yang tidak pernah gagal pada ujian mungkin tidak memeriksa apa-apa.

Integritas Kunci Antar Tabel #

Validasi satu tabel belum cukup ketika analisis kita bekerja pada gabungan beberapa tabel: baris penjualan yang menunjuk produk yang tidak ada akan hilang diam-diam saat digabungkan. Tiga pemeriksaan berikut menjaga hubungan itu:

python
import pandas as pd

produk = pd.read_csv("produk.csv")
penjualan = pd.read_csv("penjualan.csv")

kunci_lepas = set(penjualan["id_produk"]) - set(produk["id_produk"])
print("id_produk di penjualan yang tidak ada di produk:", sorted(kunci_lepas))

duplikat_kunci = produk["id_produk"][produk["id_produk"].duplicated()]
print("id_produk ganda di tabel produk:", duplikat_kunci.tolist())

cek = penjualan.merge(produk, on="id_produk", how="left", indicator=True)
tak_cocok = cek[cek["_merge"] == "left_only"]
print("Baris penjualan tanpa padanan produk:", len(tak_cocok))

Output yang Dihasilkan:

text
id_produk di penjualan yang tidak ada di produk: []
id_produk ganda di tabel produk: []
Baris penjualan tanpa padanan produk: 0

Mari kita telaah baris-baris pentingnya:

  1. Baris 6-7 (selisih himpunan): set(penjualan) - set(produk) menampung semua kunci yang dipakai tetapi tidak dikenal — istilah teknisnya orphan key. Selisih kosong berarti setiap pesanan punya padanan produknya.
  2. Baris 9-10 (kunci ganda): duplicated() pada kolom kunci menandai ID yang muncul lebih dari sekali — kunci ganda di tabel referensi membuat merge menggandakan baris secara diam-diam, salah satu sumber kesalahan paling licik pada data gabungan.
  3. Baris 12-14 (merge + _merge): penggabungan uji dengan indicator=True menandai asal setiap baris; baris "left_only" adalah baris penjualan yang gagal menemukan produknya. Ini pemeriksaan yang sama dengan selisih himpunan, tetapi siap diperluas menjadi laporan per baris.

Laporan Kualitas yang Mendokumentasikan #

Aturan-aturan di atas mencapai bentuk akhirnya sebagai satu laporan: daftar pemeriksaan, status lulus atau gagal, dan keterangan singkat — persis seperti DataFrame.merge dan fungsi lain yang kita pakai, laporan ini bisa dijalankan ulang kapan pun datanya berubah:

python
import pandas as pd

produk = pd.read_csv("produk.csv")
penjualan = pd.read_csv("penjualan.csv", parse_dates=["tanggal"])

pemeriksaan = [
    ("Harga positif", (produk["harga"] > 0).all(),
     f"harga terendah {produk['harga'].min()}"),
    ("Jumlah pesanan positif", (penjualan["jumlah"] > 0).all(),
     f"jumlah terkecil {penjualan['jumlah'].min()}"),
    ("Kolom kosong", produk.isna().sum().sum() + penjualan.isna().sum().sum() == 0,
     "total sel kosong: 0"),
    ("Kunci unik di produk", ~produk["id_produk"].duplicated().any(),
     f"{len(produk)} produk, semua unik"),
    ("Semua pesanan berpadanan", set(penjualan["id_produk"]) <= set(produk["id_produk"]),
     f"{len(penjualan)} baris tersambung"),
    ("Rentang tanggal wajar", (penjualan["tanggal"].dt.year == 2026).all(),
     "2026-02-10 sampai 2026-02-12"),
]

laporan = pd.DataFrame(pemeriksaan, columns=["pemeriksaan", "lulus", "keterangan"])
laporan["status"] = ["LULUS" if l else "GAGAL" for l in laporan["lulus"]]
print(laporan[["pemeriksaan", "status", "keterangan"]].to_string(index=False))
print()
print("Total pemeriksaan:", len(laporan), "| Gagal:", (~laporan["lulus"]).sum())

Output yang Dihasilkan:

text
             pemeriksaan status                   keterangan
           Harga positif  LULUS         harga terendah 18000
  Jumlah pesanan positif  LULUS            jumlah terkecil 2
            Kolom kosong  LULUS          total sel kosong: 0
    Kunci unik di produk  LULUS         5 produk, semua unik
Semua pesanan berpadanan  LULUS          10 baris tersambung
   Rentang tanggal wajar  LULUS 2026-02-10 sampai 2026-02-12

Total pemeriksaan: 6 | Gagal: 0

Mari kita telaah baris-baris pentingnya:

  1. Baris 6-17 (daftar pemeriksaan): setiap aturan menjadi satu tuple tiga anggota — nama yang terbaca manusia, kondisi boolean, dan keterangan yang menyimpan angka pendukung. Nama yang deskriptif membuat laporan bisa dibaca pemangku kepentingan tanpa membuka kode.
  2. Baris 19-21 (tabel laporan): daftar tuple dibungkus menjadi DataFrame; kolom lulus diterjemahkan menjadi LULUS/GAGAL yang seragam. Enam pemeriksaan, semuanya lulus — data hari ini siap analisis, dan besok laporan yang sama akan menghakimi data yang baru tiba.
  3. Baris 24 (hitungan akhir): satu baris ringkasan memudahkan pemeriksaan cepat di alur kerja otomatis — nol gagal berarti lanjut; ada gagal berarti berhenti dan perbaiki sumbernya.

Rangkuman #

  • Validasi menuangkan harapan tentang data menjadi aturan eksplisit; assert menjalankannya dan menghentikan analisis sebelum data rusak menjalar ke hilir.
  • Bungkus aturan sebagai fungsi dan uji dengan dua sisi: data bersih harus lulus, data yang sengaja dirusak harus tertangkap — aturan yang tak pernah gagal ujian patut dicurigai.
  • Integritas antar tabel dijaga tiga pemeriksaan: kunci yang dipakai tetapi tak dikenal (orphan key), kunci ganda di tabel referensi, dan baris "left_only" pada merge uji.
  • Laporan kualitas berbentuk tabel (nama pemeriksaan, status, keterangan) mendokumentasikan hasil pembersihan dan bisa dijalankan ulang setiap data baru tiba.
  • Simpan aturan dalam satu berkas yang dibaca data mentah — kebiasaan yang membuat pembersihan dari Section ini berubah dari kejadian sekali jadi menjadi proses yang berulang dan bisa diandalkan.

Data yang bersih dan tervalidasi adalah tiket masuk, bukan tujuan akhir. Pertanyaan berikutnya justru baru dimulai ketika data siap: apa pola, tren, dan hubungan yang disembunyikannya? Itulah medan Section berikutnya, Eksplorasi Data .

Referensi #

  1. pandas — API Reference: DataFrame.merge
  2. pandas — User Guide: Missing Data
  3. pandas — Getting Started: Intro Tutorials