Dashboard TokoKita menampilkan pendapatan harian Rp2,1 juta padahal kasir mencatat Rp3,4 juta. Data ada, pipeline berstatus sukses, tetapi angka yang dipercaya salah. Masalah seperti ini jarang disebabkan satu kesalahan besar; biasanya akumulasi nilai kosong, baris ganda, dan format yang bergeser diam-diam. Sebelum kita bisa memperbaiki kualitas data, kita perlu bahasa bersama untuk mendefinisikannya — dan itulah peran dimensi kualitas data (data quality dimensions). Chapter ini memetakan enam dimensi yang paling umum dipakai industri, lalu menghitung skor tiap dimensi pada tabel pesanan yang sengaja dibuat bermasalah.
Mengapa Kualitas Data Harus Diukur #
Kata “kualitas” tanpa definisi operasional hanya menghasilkan debat tanpa akhir: tim analytics bilang data pesanan buruk, tim kasir bilang tidak. Dimensi kualitas memecah kata “buruk” menjadi pernyataan yang bisa dihitung: berapa persen sel kosong, berapa baris melanggar format, berapa jam umur data terbaru. Angka-angka tersebut mengubah perdebatan menjadi pekerjaan: setiap dimensi yang gagal punya pemilik proses yang jelas dan langkah perbaikan yang berbeda.
Pengukuran juga memungkinkan kita menetapkan target layanan — misalnya “completeness minimal 99%” — lalu memantau trennya mingguan, persis seperti uptime untuk server. Tanpa angka, penilaian kualitas bergantung pada siapa yang paling keras bersuara di rapat.
Enam Dimensi Kualitas Data #
| Dimensi | Pertanyaan Kunci | Contoh Pelanggaran di TokoKita |
|---|---|---|
| Completeness | Apakah semua nilai yang seharusnya ada benar-benar ada? | kolom pelanggan kosong; partisi harian gagal load |
| Validity | Apakah nilai sesuai format dan rentang yang diharapkan? | total bernilai -5000; email tanpa tanda @ |
| Uniqueness | Apakah satu entitas tidak tercatat ganda? | ORD-002 muncul dua kali di tabel pesanan |
| Consistency | Apakah fakta yang sama bernilai sama di semua tabel? | total pesanan berbeda dari total pembayaran |
| Freshness | Apakah data cukup baru untuk keputusan hari ini? | data berhenti di hari Jumat padahal hari Senin |
| Accuracy | Apakah nilai mencerminkan kenyataan di lapangan? | alamat pelanggan sudah tidak sesuai domisili |
Lima dimensi pertama dapat dihitung langsung dari data itu sendiri: kita membandingkan isi tabel dengan aturan, tabel pembanding, atau penanda waktu. Accuracy adalah dimensi yang paling sulit diotomatisasi karena menuntut sumber kebenaran dari dunia nyata — hasil audit kasir, konfirmasi pelanggan, atau sensor — sehingga di praktik biasanya diverifikasi lewat sampling manual. Enam dimensi ini juga saling bertukar: proses deduplikasi yang agresif menaikkan uniqueness tetapi bisa membuang baris pesanan sungguhan dan menurunkan completeness.
Menghitung Skor Tiap Dimensi dengan pandas #
Kita ambil tabel pesanan berisi sepuluh baris dengan masalah campuran: dua sel kosong, satu order_id ganda, dua email tidak valid, satu total negatif. Titik pemeriksaan kita kunci pada waktu tetap supaya skor freshness deterministik:
"""Skor dimensi kualitas data TokoKita: completeness, uniqueness, validity, freshness."""
import datetime as dt
import re
import pandas as pd
# Titik pemeriksaan tetap supaya umur data deterministik antar run.
SEKARANG = dt.datetime(2026, 10, 20, 9, 0)
SLA_FRESHNESS_JAM = 24
pesanan = pd.DataFrame(
[
["ORD-001", "Budi", "[email protected]", 150000, dt.datetime(2026, 10, 20, 6, 12)],
["ORD-002", "Siti", "[email protected]", 320000, dt.datetime(2026, 10, 20, 5, 40)],
["ORD-003", "Dewi", "dewi example com", 99000, dt.datetime(2026, 10, 20, 4, 55)],
[None, "Budi", "[email protected]", 430000, dt.datetime(2026, 10, 19, 22, 8)],
["ORD-005", None, "[email protected]", -5000, dt.datetime(2026, 10, 19, 18, 30)],
["ORD-006", "Siti", "[email protected]", 260000, dt.datetime(2026, 10, 19, 14, 2)],
["ORD-007", "Dewi", "[email protected]", 87000, dt.datetime(2026, 10, 19, 9, 45)],
["ORD-002", "Siti", "[email protected]", 320000, dt.datetime(2026, 10, 20, 5, 40)],
["ORD-009", "Budi", "@example.com", 175000, dt.datetime(2026, 10, 18, 20, 17)],
["ORD-010", "Dewi", "[email protected]", 210000, dt.datetime(2026, 10, 18, 8, 5)],
],
columns=["order_id", "pelanggan", "email", "total", "created_at"],
)
# Completeness: persentase sel yang terisi.
sel_kosong = int(pesanan.isna().sum().sum())
completeness = 1 - sel_kosong / pesanan.size
# Uniqueness: order_id unik di antara baris yang terisi.
terisi = pesanan["order_id"].dropna()
uniqueness = terisi.nunique() / len(terisi)
# Validity: email sesuai pola dan total tidak negatif.
pola_email = r"^[\w.+-]+@[\w-]+\.[\w.-]+$"
email_ok = pesanan["email"].fillna("").str.match(pola_email)
total_ok = pesanan["total"] >= 0
validity = (email_ok & total_ok).sum() / len(pesanan)
# Freshness: umur baris terbaru terhadap titik pemeriksaan.
terbaru = pesanan["created_at"].max()
umur_jam = (SEKARANG - terbaru).total_seconds() / 3600
print(f"baris diperiksa : {len(pesanan)}")
print(f"{'dimensi':14}{'skor':>8} keterangan")
print(f"{'completeness':14}{completeness:>7.1%} {sel_kosong} sel kosong dari {pesanan.size}")
print(f"{'uniqueness':14}{uniqueness:>7.1%} {len(terisi) - terisi.nunique()} order_id ganda dari {len(terisi)} terisi")
print(f"{'validity':14}{validity:>7.1%} {int((~(email_ok & total_ok)).sum())} baris gagal aturan email/total")
status = "SEGAR" if umur_jam <= SLA_FRESHNESS_JAM else "TIDAK SEGAR"
print(f"{'freshness':14}{umur_jam:>6.1f}j baris terbaru {terbaru} -> {status} (SLA {SLA_FRESHNESS_JAM} jam)")Output yang Dihasilkan:
baris diperiksa : 10
dimensi skor keterangan
completeness 96.0% 2 sel kosong dari 50
uniqueness 88.9% 1 order_id ganda dari 9 terisi
validity 70.0% 3 baris gagal aturan email/total
freshness 2.8j baris terbaru 2026-10-20 06:12:00 -> SEGAR (SLA 24 jam)Mari kita telaah baris-baris kode di atas:
- Baris 8-9 (
SEKARANGdanSLA_FRESHNESS_JAM): titik pemeriksaan dan ambang SLA ditulis sebagai konstanta. Di pipeline produksi,SEKARANGdigantidatetime.now()dan SLA dibaca dari konfigurasi. - Baris 11-25 (
pesanan = pd.DataFrame(...)): sepuluh baris berisi lima jenis masalah — sel kosong di baris 4 dan 5, email rusak di baris 3 dan 9, total negatif di baris 5, danORD-002ganda di baris 2 serta 8. - Baris 28-29 (completeness):
isna()menandai sel kosong, lalu jumlahnya dibagi ukuran tabel; hasil 48 dari 50 sel terisi berarti 96%. - Baris 32-33 (uniqueness):
dropna()membuang baris kosong lebih dulu supayaNonetidak dihitung sebagai nilai ganda; sembilan baris terisi hanya punya delapan nilai unik. - Baris 36-39 (validity): satu baris dinyatakan valid hanya bila email cocok pola regex dan
totaltidak negatif — dua aturan digabung dengan operator&. - Baris 50-51 (freshness): umur dihitung dari baris
created_atpaling baru, lalu dibandingkan dengan SLA untuk menentukan status.
Membaca Skor dan Menindaklanjuti #
Skor di atas memperlihatkan pola yang sering muncul di dunia nyata: data sangat segar (2,8 jam) tetapi validitasnya rendah (70%). Freshness yang baik justru bisa menutupi masalah lain karena pipeline tetap berjalan dan data tetap mengalir — hanya isinya yang melanggar aturan. Setiap skor rendah mengarah ke tindakan dan pemilik yang berbeda:
completenessrendah: selidiki proses pengisian form di aplikasi kasir — pemiliknya tim aplikasi.uniquenessrendah: periksa logika deduplikasi dan idempotensi di tahap ingestion.validityrendah: tegakkan aturan format dan rentang di gerbang masuk data.freshnessrendah: telusuri jadwal pipeline dan kesehatan sumber upstream.
Fungsi pandas yang dipakai di sini (isna(), nunique(), str.match()) terdokumentasi lengkap di dokumentasi pandas
. Mengukur dimensi setelah data jatuh ke tabel hanya separuh pekerjaan; separuh lainnya adalah mencegah data bermasalah masuk sejak awal — pekerjaan yang dibahas pada chapter berikutnya: Data Validation.