Masalah dirumuskan, metrik disepakati, pipeline teruji, eksperimen tercatat, dataset terdokumentasi, dependensi terkunci — seluruh peralatan kerja siap. Sebelum hasil diserahkan, tinggal satu gerbang yang hanya bisa kita lewati sendiri: pemeriksaan kualitas hasil. Kekeliruan yang tertangkap di meja kita murah — cukup diperbaiki lalu dijalankan ulang; yang pertama kali terlihat di tangan pemilik kedai berharga mahal, karena yang ikut tercoreng bukan hanya satu angka, melainkan kepercayaan pada seluruh analisis. Bab ini memeriksa tiga kekeliruan yang paling sering lolos ke laporan — asumsi yang tidak diuji, angka yang tidak cocok antar sumber, dan besaran yang tidak wajar — lalu menutup Section dengan daftar periksa dan rangkaian seluruh alurnya.

Kekeliruan yang Berjalan Diam-Diam #

Kekeliruan yang sampai ke laporan jarang berbentuk galat program — galat sudah menahan kode kita jauh lebih awal. Yang lolos justru kekeliruan diam-diam: kode berjalan tanpa keluhan, angka keluar rapi, dan hasilnya salah. Sumbernya bermacam-macam: asumsi yang tidak diuji terhadap data, dua jalur perhitungan yang tidak saling cocok, satuan yang tertukar, atau pembagi yang keliru.

Kekeliruan diam-diamGejala ketika sampai ke pemakaiPemeriksaan penangkapnya
Asumsi yang tidak berlaku pada dataangka tampak benar, tafsirnya keliruuji asumsi eksplisit
Angka tidak cocok antar sumberdua bagian laporan saling bertentanganrekonsiliasi lintas tabel
Besaran tidak wajar“retensi 118%”, “rata-rata Rp330 ribu”uji batas kewajaran
Hasil tidak bisa dijalankan ulangangka berubah pada setiap eksekusiseed, data beku, requirements.txt

Tiga pemeriksaan pertama kita praktikkan dengan kode pada dataset transaksi Toko Kopi Kita yang sudah kita dokumentasikan di chapter Dokumentasi Dataset . Pemeriksaan keempat bukan pemeriksaan baru — ia adalah gabungan kebiasaan dari Reproducible Analysis hingga Pengelolaan Dependensi , dan cukup kita petik kembali pada daftar periksa akhir.

Memeriksa Asumsi secara Eksplisit #

Setiap analisis berdiri di atas asumsi, dan asumsi yang tidak ditulis tidak akan pernah diperiksa. Kebiasaan yang kita bangun: turunkan asumsi menjadi pernyataan yang bisa bernilai salah, lalu uji pernyataan itu terhadap data. Empat asumsi analisis kita misalnya: nilai transaksi positif, nilainya wajar untuk sebuah kedai, semua pelanggan adalah anggota terdaftar, dan seluruh tanggal berada di Januari 2026.

python
import pandas as pd

transaksi = pd.DataFrame({
    "tanggal": ["2026-01-05", "2026-01-05", "2026-01-06", "2026-01-06",
                "2026-01-07", "2026-01-07", "2026-01-07", "2026-01-08"],
    "pelanggan": ["Budi", "Siti", "Dewi", "Siti", "Budi", "Dewi", "nonmember", "Agus"],
    "menu": ["kopi susu", "americano", "matcha", "teh melati",
             "kopi susu", "matcha", "americano", "kopi susu"],
    "total": [24000, 18000, 28000, 18000, 24000, 28000, 22000, 32000],
})

asumsi = {
    "semua total bernilai positif": bool((transaksi["total"] > 0).all()),
    "semua total di bawah Rp100.000": bool((transaksi["total"] < 100000).all()),
    "semua pelanggan adalah anggota": bool((transaksi["pelanggan"] != "nonmember").all()),
    "tanggal berada di Januari 2026": bool(
        transaksi["tanggal"].between("2026-01-01", "2026-01-31").all()),
}

for uraian, lolos in asumsi.items():
    tanda = "OK" if lolos else "GAGAL"
    print(f"[{tanda}] {uraian}")

Output yang Dihasilkan:

text
[OK] semua total bernilai positif
[OK] semua total di bawah Rp100.000
[GAGAL] semua pelanggan adalah anggota
[OK] tanggal berada di Januari 2026

Mari kita bedah pola pemeriksaannya:

  1. transaksi = pd.DataFrame({...}): delapan baris data yang persis sama dengan chapter Dokumentasi Dataset — pemeriksaan paling tepat dijalankan pada data yang persis dianalisis, bukan pada salinan lain.
  2. Dictionary asumsi: tiap kunci adalah uraian asumsi dalam kalimat manusia, tiap nilai hasil ujinya — laporan pemeriksaannya terbaca tanpa membuka kode.
  3. (transaksi["total"] > 0).all(): operasi vektor pandas menghasilkan deret True/False per baris; .all() meringkas seluruh deret menjadi satu jawaban.
  4. transaksi["pelanggan"] != "nonmember": membandingkan satu kolom terhadap satu nilai — pola penyaringan biasa yang dibungkus .all() menjadi pertanyaan ya-tidak.

Satu asumsi tidak lolos uji: tidak semua pelanggan adalah anggota — baris nonmember memang ada di data. Datanya tidak rusak; asumsinya memang tidak berlaku, dan analisis harus menyesuaikan: pisahkan perhitungan untuk anggota terdaftar, atau perluas definisi pelanggan dan catat keputusannya. Tanpa pemeriksaan ini, rata-rata belanja “per pelanggan” akan diam-diam mencampur dua populasi berbeda.

Merekonsiliasi Angka Lintas Sumber #

Laporan yang sehat memuat angka yang dihitung dari jalur berbeda: penjualan harian dari tabel detail transaksi, dan rekap penutupan kasir yang dicatat sistem kasir secara terpisah. Rekonsiliasi mempertemukan keduanya: hitung ulang dari sumber independen, bandingkan, lalu selidiki setiap selisih. Dua angka berbeda untuk “penjualan 7 Januari” lebih cepat menurunkan kepercayaan pembaca daripada kekeliruan lain mana pun.

python
import pandas as pd

transaksi = pd.DataFrame({
    "tanggal": ["2026-01-05", "2026-01-05", "2026-01-06", "2026-01-06",
                "2026-01-07", "2026-01-07", "2026-01-07", "2026-01-08"],
    "pelanggan": ["Budi", "Siti", "Dewi", "Siti", "Budi", "Dewi", "nonmember", "Agus"],
    "menu": ["kopi susu", "americano", "matcha", "teh melati",
             "kopi susu", "matcha", "americano", "kopi susu"],
    "total": [24000, 18000, 28000, 18000, 24000, 28000, 22000, 32000],
})

penjualan_detail = (transaksi
                    .groupby("tanggal", as_index=False)["total"].sum()
                    .rename(columns={"total": "dari_detail"}))

rekap_kasir = pd.DataFrame({
    "tanggal": ["2026-01-05", "2026-01-06", "2026-01-07", "2026-01-08"],
    "dari_kasir": [42000, 46000, 52000, 32000],
})

banding = penjualan_detail.merge(rekap_kasir, on="tanggal")
banding["selisih"] = banding["dari_detail"] - banding["dari_kasir"]
print(banding.to_string(index=False))

total_detail = int(banding["dari_detail"].sum())
total_kasir = int(banding["dari_kasir"].sum())
print()
print("total dari detail :", total_detail)
print("total dari kasir  :", total_kasir)
print("rekonsiliasi       :", "COCOK" if total_detail == total_kasir else "TIDAK COCOK")

Output yang Dihasilkan:

text
   tanggal  dari_detail  dari_kasir  selisih
2026-01-05        42000       42000        0
2026-01-06        46000       46000        0
2026-01-07        74000       52000    22000
2026-01-08        32000       32000        0

total dari detail : 194000
total dari kasir  : 172000
rekonsiliasi       : TIDAK COCOK

Mari kita bedah jalur rekonsiliasinya:

  1. groupby("tanggal")["total"].sum(): menggulung delapan transaksi menjadi empat baris penjualan harian — angka jalur pertama, dihitung sendiri dari data mentah.
  2. rekap_kasir: angka jalur kedua dari catatan penutupan kasir; pada proyek nyata tabel ini dibaca dari berkas sistem kasir.
  3. merge(on="tanggal"): menggandengkan kedua tabel baris-per-baris sehingga perbandingan terjadi pada tanggal yang sama, bukan pada posisi baris yang kebetulan sejajar.
  4. Kolom selisih: hasil pengurangan langsung menunjukkan hari mana yang tidak sepakat — rekonsiliasi bukan sekadar pertanyaan cocok-tidak-cocok, melainkan penunjuk arah penyelidikan.

Selisih Rp22.000 terkunci pada satu tanggal: 7 Januari. Penyelidikan pada tabel detail menemukan penyebabnya — transaksi nonmember sebesar Rp22.000 hari itu tidak masuk rekap kasir karena pembayaran tunai dicatat di buku saat mesin kasir sedang diperiksa. Setelah rekap dikoreksi, kedua total bertemu di Rp194.000 — akar masalahnya sekaligus ketemu: transaksi mana yang tercecer, pada hari apa, seberapa besar nilainya.

Menguji Kewajaran Besaran #

Pemeriksaan ketiga memakai pengetahuan domain sebagai alat ukur: seberapa besar angka yang wajar? Proporsi tidak mungkin melebihi 1; rata-rata transaksi sebuah kedai tidak jauh dari harga menunya; jumlah pelanggan aktif tidak mungkin melebihi daftar anggota. Laporan bulanan otomatis kita mengeluarkan tiga angka; kita uji ketiganya terhadap batas yang bisa dipertanggungjawabkan:

python
laporan_bulanan = {
    # nama metrik: (nilai dilaporkan, batas bawah, batas atas, alasan batas)
    "retensi_anggota": (1.18, 0.0, 1.0, "proporsi tidak bisa melebihi 1"),
    "rata_rata_transaksi": (330000.0, 15000.0, 100000.0, "menu dijual Rp15-32 ribu"),
    "pelanggan_aktif": (187.0, 1.0, 500.0, "daftar anggota berisi 500 orang"),
}

print(f"{'metrik':20s} {'nilai':>9s}   status")
for nama, (nilai, bawah, atas, alasan) in laporan_bulanan.items():
    wajar = bawah <= nilai <= atas
    status = "wajar" if wajar else f"PERIKSA -> {alasan}"
    print(f"{nama:20s} {nilai:>9}   {status}")

Output yang Dihasilkan:

text
metrik                   nilai   status
retensi_anggota           1.18   PERIKSA -> proporsi tidak bisa melebihi 1
rata_rata_transaksi   330000.0   PERIKSA -> menu dijual Rp15-32 ribu
pelanggan_aktif          187.0   wajar

Mari kita bedah tiga keputusannya:

  1. Struktur laporan_bulanan: setiap metrik membawa nilai, batas bawah, batas atas, dan alasannya — supaya pembaca berikutnya tahu batas itu bukan angka sewenang-wenang.
  2. bawah <= nilai <= atas: perbandingan berantai Python menguji dua batas sekaligus dalam satu ekspresi yang terbaca seperti matematika tertulis.
  3. status = "wajar" if ... else f"PERIKSA -> {alasan}": keluaran menyertakan alasannya sehingga pemerik langsung tahu apa yang dicurigai.

Dua angka tertandai. Retensi 1,18 berarti 118% — mustahil bagi proporsi; tersangka utamanya pembagi yang keliru. Rata-rata transaksi Rp330.000 lebih dari sepuluh kali lipat menu termahal Rp32.000; kandidatnya satuan yang tertukar (seharusnya 33.000) atau jumlah transaksi yang salah baca (330 dari 3.300). Uji ini tidak memutuskan kandidat mana yang benar — ia menandai angka yang tidak boleh naik ke laporan sebelum pertanyaannya terjawab.

Daftar Periksa Sebelum Penyerahan #

Tiga pemeriksaan di atas, ditambah kebiasaan reproducibility dari chapter-chapter sebelumnya, kita rangkum menjadi daftar periksa terakhir. Setiap butir berupa pertanyaan yang jawabannya bisa dibuktikan — bukan perasaan “sepertinya sudah benar”:

ButirPertanyaan kunciBukti penuntasnya
Masalahapakah analisis menjawab pertanyaan yang dirumuskan?temuan dipetakan balik ke rumusan masalah
Metrikapakah angka utama sesuai definisi yang disepakati?rumus dan perhitungannya terdokumentasi
Asumsiapakah setiap asumsi diuji terhadap data?hasil pemeriksaan asumsi tercatat
Rekonsiliasiapakah angka cocok dengan sumber independen?tabel perbandingan berselisih nol
Besaranapakah setiap angka masuk akal menurut domain?uji batas kewajaran lolos tanpa tanda
Reproduksiapakah hasil identik saat dijalankan ulang?seed tercatat, data beku, requirements.txt
Dokumentasiapakah orang lain bisa memahami dan mengulang?kamus data, README, dan log eksperimen

Satu Alur Utuh: Dari Masalah hingga Penyerahan #

Bab ini menutup perjalanan Section Data Science Workflow. Sebelas babnya membentuk alur yang saling mengunci — tabel berikut merangkainya beserta hasil kerjanya:

TahapBabHasil kerja
RumusanMerumuskan Masalahpertanyaan analitik yang jelas dan terukur
MetrikMenentukan Metrik Keberhasilandefinisi keberhasilan sebelum analisis dimulai
DataMengumpulkan dan Memahami Datadata terkumpul dan isinya terpetakan
RencanaMenyusun Rencana Analisislangkah kerja yang disepakati tim
PemrosesanMembangun Pipeline Pemrosesanpipeline pembersihan yang dapat diulang
ReproduksiReproducible Analysishasil identik saat dijalankan ulang
VersiVersion Control untuk Datariwayat kode dan data yang terlacak
EksperimenEksperimen dan Pencatatanlog percobaan beserta keputusannya
DokumentasiDokumentasi Datasetkamus data dan README
LingkunganPengelolaan Dependensirequirements.txt dan uji lingkungan
PemeriksaanPemeriksaan Kualitas Hasil (bab ini)asumsi teruji, angka terekonsiliasi, besaran wajar

Alur ini bukan garis lurus sekali jalan: pemeriksaan yang menemukan asumsi keliru bisa mengembalikan kita ke pipeline, dan rekonsiliasi yang gagal bisa memaksa pengumpulan data diulang. Yang membuat putaran seperti itu murah adalah bab-bab sebelumnya — pipeline teruji, eksperimen tercatat, lingkungan terkunci — sehingga setiap perbaikan bisa dijalankan ulang dengan yakin bahwa hanya perbaikannya yang berubah.

Rangkuman #

  • Kekeliruan yang paling mahal bukan galat program, melainkan kekeliruan diam-diam: asumsi yang tidak berlaku, angka yang tidak cocok antar sumber, dan besaran yang tidak wajar.
  • Asumsi diturunkan menjadi pernyataan yang bisa salah lalu diuji terhadap data; asumsi yang tidak berlaku menuntut penyesuaian analisis, bukan dibiarkan begitu saja.
  • Rekonsiliasi menghitung ulang dari sumber independen dan menyelidiki setiap selisih sampai ke akar penyebabnya, bukan menyamakan angka begitu saja.
  • Uji kewajaran memakai batas dari pengetahuan domain; ia menandai angka untuk diperiksa sebelum naik ke laporan.
  • Daftar periksa penyerahan merangkai seluruh kebiasaan Section; butir yang bisa diperiksa mesin sebaiknya menjadi uji pytest.

Hasil analisis kini terperiksa dan layak diserahkan — tetapi angka yang benar belum tentu sampai. Cara kita menyampaikannya menentukan apakah temuan dipahami, diingat, dan ditindaklanjuti. Itulah pekerjaan Section berikutnya: Komunikasi dan Pelaporan , dimulai dari menyusun cerita di balik angka pada bab Menyusun Narasi Data .

Referensi #

  1. pandas — pandas.DataFrame.groupby
  2. pandas — pandas.DataFrame.merge
  3. pytest — Documentation