Pembersihan Data
(Data Cleaning)
Dikatakan bahwa data scientist menghabiskan 60–80% waktunya untuk membersihkan data — bukan untuk analisis atau modeling. Data kotor menghasilkan model yang salah, meski algoritmanya sempurna. "Garbage in, garbage out."
1. Mengapa Data Bisa Kotor?
Bayangkan absensi mahasiswa dicatat oleh 5 dosen berbeda selama satu semester. Dosen A menulis nama "Andi Pratama", dosen B menulis "ANDI PRATAMA", dosen C menulis "A. Pratama", dan dosen D tidak mengisi sama sekali. Sistem absensi akan menghitung ini sebagai 4 orang berbeda.
Inilah masalah dunia nyata. Data kotor bukan berarti data "palsu" — sering kali berasal dari proses pengumpulan yang berbeda, sistem yang tidak terintegrasi, atau kesalahan input manusia yang wajar.
| Sumber Masalah | Contoh Masalah yang Ditimbulkan | Frekuensi |
|---|---|---|
| Entry manual manusia | Salah ketik, kapitalisasi tidak konsisten, spasi berlebih | Sangat Tinggi |
| Integrasi multi-sumber | Format berbeda, skema kolom berbeda, unit ukuran berbeda | Sangat Tinggi |
| Sensor / perangkat | Bacaan tidak valid (suhu -999), missing saat sinyal hilang | Tinggi |
| Migrasi sistem | Data lama tidak sesuai skema baru, field yang dipetakan salah | Sedang |
| Web scraping | Tag HTML tersisa, encoding karakter salah, data parsial | Tinggi |
| Privasi/Anonimisasi | Nilai diganti placeholder seperti "N/A", "0", atau "Unknown" | Sedang |
2. Pipeline Pembersihan Data
Selalu simpan data asli (raw data) secara terpisah dan jangan pernah menimpa file aslinya. Buat kolom/file baru untuk versi yang sudah dibersihkan. Dokumentasikan setiap langkah cleaning — apa yang diubah, mengapa, dan berapa banyak baris yang terpengaruh.
3. Deteksi dan Penanganan Data Duplikat
Data duplikat terjadi ketika satu entitas yang sama muncul lebih dari satu kali dalam dataset. Ini bisa menyebabkan model belajar bias karena data tertentu "lebih sering" dari yang seharusnya.
Bayangkan mahasiswa mengisi daftar hadir kertas DAN sistem digital sekaligus. Saat data digabungkan, nama yang sama muncul dua kali. Jika kita hitung kehadiran, hasilnya berlebihan dan menyesatkan.
Duplikat bisa exact (semua kolom sama) atau partial (hanya sebagian kolom sama, misalnya NIM sama tapi nama sedikit berbeda karena typo).
Baris 2 = exact duplicate (semua kolom sama persis). Baris 4 = partial duplicate (NIM+nilai+kelas sama, nama beda karena typo "Budhi").
4. Inkonsistensi Format dan Nilai
Inkonsistensi terjadi ketika nilai yang secara semantis sama ditulis dalam format berbeda. Ini salah satu masalah yang paling umum dan paling sering luput dari perhatian.
5. Deteksi dan Penanganan Outlier
Outlier adalah nilai yang jauh berbeda dari mayoritas data. Outlier bisa berupa noise (kesalahan pengukuran/input) atau sinyal penting (peristiwa langka yang bermakna). Keduanya memerlukan penanganan berbeda.
Noise (perlu dibuang/diperbaiki): Nilai berat badan 999 kg dalam data pasien rumah sakit — jelas kesalahan input, bukan berat sesungguhnya.
Sinyal penting (perlu dipertahankan): Transaksi kartu kredit senilai Rp 50 juta di antara transaksi harian Rp 100–500 ribu — ini bisa merupakan penipuan yang justru wajib dipertahankan dalam model deteksi fraud.
Opsi Penanganan Outlier
| Strategi | Kapan Digunakan | Cara |
|---|---|---|
| Hapus baris | Jelas kesalahan input, tidak mungkin terjadi di dunia nyata | df[mask] setelah identifikasi |
| Winsorizing/Capping | Ingin pertahankan baris tapi batasi pengaruh nilai ekstrem | Ganti dengan batas IQR atau persentil 1%-99% |
| Transformasi log | Data sangat skewed kanan (income, populasi) | np.log1p() — distribusi menjadi lebih simetris |
| Pertahankan | Outlier adalah sinyal penting (fraud, anomali) | Biarkan, tambahkan fitur flag "is_outlier" |
| Analisis terpisah | Outlier dari subpopulasi berbeda | Segmentasi, bangun model terpisah |
6. Validasi Data — Aturan Bisnis dan Logika
Setelah membersihkan format dan mengisi missing, langkah terakhir adalah memastikan data masuk akal secara logika bisnis/domain. Data bisa valid secara teknis tapi tidak valid secara semantis.
7. Praktik: Pipeline Data Cleaning Lengkap di Python
7.1 Audit Awal dan Profiling
import pandas as pd
import numpy as np
# Dataset mahasiswa yang kotor (simulasi data nyata)
data_kotor = {
'nim' :['2021001','2021002','2021001','2021003','2021004','2021002'],
'nama' :['andi pratama','BUDI','andi pratama','Citra','Dewi','budhi'],
'nilai' :[85,78,85,92,999,78], # 999 = outlier/kesalahan input
'kelas' :['A','a','A','B','B','A'],
'kota' :[' jakarta','BANDUNG',' jakarta',np.nan,'Surabaya','BANDUNG']
}
df = pd.DataFrame(data_kotor)
# ── AUDIT AWAL ────────────────────────────────────
print("=== AUDIT AWAL ===")
print(f"Shape : {df.shape}")
print(f"Duplikat : {df.duplicated().sum()} baris exact duplikat")
print(f"NIM duplikat : {df['nim'].duplicated().sum()} NIM muncul >1x")
print("\nMissing Values:")
print(df.isnull().sum())
print("\nStatistik nilai:")
print(df['nilai'].describe())
=== AUDIT AWAL ===
Shape : (6, 5)
Duplikat : 1 baris exact duplikat
NIM duplikat : 2 NIM muncul >1x
Missing Values:
nim 0
nama 0
nilai 0
kelas 0
kota 1
dtype: int64
Statistik nilai:
count 6.000000
mean 186.167 ← sangat tinggi karena outlier 999!
std 352.895
min 78.000
max 999.000 ← jelas outlier
7.2 Pipeline Pembersihan Lengkap
# ── STEP 1: Hapus exact duplicates ───────────────────
df_clean = df.drop_duplicates().reset_index(drop=True)
print(f"[1] Setelah drop_duplicates: {len(df_clean)} baris (dari {len(df)})")
# ── STEP 2: Standarisasi teks ─────────────────────────
df_clean['nama'] = df_clean['nama'].str.strip().str.title()
df_clean['kelas'] = df_clean['kelas'].str.upper()
df_clean['kota'] = df_clean['kota'].str.strip().str.title()
print("[2] Teks distandarisasi (title case + strip)")
# ── STEP 3: Tangani missing values ───────────────────
kota_modus = df_clean['kota'].mode()[0]
df_clean['kota'] = df_clean['kota'].fillna(kota_modus)
print(f"[3] Missing 'kota' diisi modus: '{kota_modus}'")
# ── STEP 4: Tangani outlier nilai (IQR method) ───────
Q1 = df_clean['nilai'].quantile(0.25)
Q3 = df_clean['nilai'].quantile(0.75)
IQR = Q3 - Q1
batas_atas = Q3 + 1.5 * IQR
batas_bawah = Q1 - 1.5 * IQR
outlier_mask = (df_clean['nilai'] > batas_atas) | (df_clean['nilai'] < batas_bawah)
print(f"[4] Outlier nilai: {outlier_mask.sum()} baris (batas: {batas_bawah:.0f}–{batas_atas:.0f})")
df_clean = df_clean[~outlier_mask].reset_index(drop=True)
# ── STEP 5: Tangani partial duplicates (NIM duplikat) ─
nim_dup = df_clean[df_clean.duplicated(subset=['nim'], keep=False)]
print(f"[5] Partial dup (NIM sama): {len(nim_dup)} baris → pertahankan first occurrence")
df_clean = df_clean.drop_duplicates(subset=['nim'], keep='first').reset_index(drop=True)
# ── STEP 6: Validasi akhir ────────────────────────────
print("\n=== HASIL AKHIR ===")
print(df_clean)
print("\nStatistik nilai setelah cleaning:")
print(df_clean['nilai'].describe())
print(f"\nTotal NaN tersisa: {df_clean.isnull().sum().sum()}")
[1] Setelah drop_duplicates: 5 baris (dari 6)
[2] Teks distandarisasi (title case + strip)
[3] Missing 'kota' diisi modus: 'Bandung'
[4] Outlier nilai: 1 baris (batas: 59–113) ← 999 terdeteksi dan dihapus
[5] Partial dup (NIM sama): 2 baris → pertahankan first occurrence
=== HASIL AKHIR ===
nim nama nilai kelas kota
0 2021002 Budi 78 A Bandung
1 2021003 Citra 92 B Bandung
2 2021001 Andi Pratama 85 A Jakarta ← nama distandarisasi
Statistik nilai setelah cleaning:
count 3.000000
mean 85.000 ← jauh lebih representatif dari 186 sebelumnya
min 78.000
max 92.000
Total NaN tersisa: 0
7.3 Validasi Aturan Bisnis
# Dataset mahasiswa dengan kolom tambahan untuk validasi silang
df_val = pd.DataFrame({
'nim' : ['2021001','2021002','2021003','2021004'],
'tgl_lahir' : pd.to_datetime(['2002-05-17','2001-03-05','2025-11-22','2003-07-01']),
'tgl_masuk' : pd.to_datetime(['2021-09-01']*4),
'ipk' : [3.75, 4.50, 3.20, 2.85], # IPK 4.50 tidak valid!
'nilai_uts' : [85, 78, 92, -5] # -5 tidak valid!
})
print("=== LAPORAN VALIDASI ATURAN BISNIS ===")
# 1. Cek IPK rentang valid (0.00-4.00)
ipk_invalid = df_val[df_val['ipk'] > 4.0]
print(f"IPK > 4.0 (tidak valid): {len(ipk_invalid)} baris")
print(ipk_invalid[['nim','ipk']])
# 2. Cek nilai UTS (0-100)
nilai_invalid = df_val[(df_val['nilai_uts'] < 0) | (df_val['nilai_uts'] > 100)]
print(f"\nNilai UTS di luar 0–100: {len(nilai_invalid)} baris")
# 3. Cek tanggal lahir logis (harus < tanggal masuk, umur wajar)
today = pd.Timestamp('today')
umur = (df_val['tgl_masuk'] - df_val['tgl_lahir']).dt.days / 365.25
umur_aneh = df_val[(umur < 15) | (umur > 60)]
print(f"\nUmur saat masuk tidak wajar (<15 atau >60 th): {len(umur_aneh)} baris")
print(umur_aneh[['nim','tgl_lahir']])
=== LAPORAN VALIDASI ATURAN BISNIS ===
IPK > 4.0 (tidak valid): 1 baris
nim ipk
1 2021002 4.50 ← IPK 4.50 jelas kesalahan input
Nilai UTS di luar 0–100: 1 baris
nim nilai_uts
3 2021004 -5 ← nilai negatif tidak mungkin
Umur saat masuk tidak wajar (<15 atau >60 th): 1 baris
nim tgl_lahir
2 2021003 2025-11-22 ← lahir 2025 masuk 2021, mustahil!
Uji Pemahaman Sesi 9
- Data scientist menghabiskan 60–80% waktu untuk cleaning — ini investasi, bukan pemborosan. "Garbage in, garbage out."
- Pipeline 6 langkah: Audit → Duplikat → Format → Missing → Outlier → Validasi
- Simpan selalu raw data asli; buat salinan baru untuk versi bersih; dokumentasikan setiap perubahan
- Duplikat exact: drop_duplicates(). Partial duplikat: drop_duplicates(subset=[...]) setelah investigasi
- Inkonsistensi: .str.strip(), .str.title(), .str.upper(), dictionary mapping untuk standarisasi
- Outlier bisa noise (hapus/cap) atau sinyal penting (pertahankan + flag). Konteks domain sangat penting
- Deteksi outlier: IQR (robust, tidak perlu asumsi normal) atau Z-score (asumsi normal)
- Winsorizing: ganti nilai outlier dengan batas persentil, bukan hapus baris
- Validasi bisnis: cek rentang valid, konsistensi antar kolom, referential integrity, dan distribusi keseluruhan
- Python: drop_duplicates(), fillna(), quantile(), IQR masking, str accessor, aturan bisnis dengan boolean masking