MA1420 · DATA SAINS · SESI 09

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?

💡 ILUSTRASI — CATATAN ABSENSI MANUAL

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 MasalahContoh Masalah yang DitimbulkanFrekuensi
Entry manual manusiaSalah ketik, kapitalisasi tidak konsisten, spasi berlebihSangat Tinggi
Integrasi multi-sumberFormat berbeda, skema kolom berbeda, unit ukuran berbedaSangat Tinggi
Sensor / perangkatBacaan tidak valid (suhu -999), missing saat sinyal hilangTinggi
Migrasi sistemData lama tidak sesuai skema baru, field yang dipetakan salahSedang
Web scrapingTag HTML tersisa, encoding karakter salah, data parsialTinggi
Privasi/AnonimisasiNilai diganti placeholder seperti "N/A", "0", atau "Unknown"Sedang

2. Pipeline Pembersihan Data

🔍 AUDIT Kenali masalah: profiling, missing %, dtype, distribusi
🗑️ DUPLIKAT Deteksi dan hapus baris/kolom duplikat
🔧 FORMAT Standarisasi tipe, kapitalisasi, unit ukuran
🩹 MISSING Hapus atau imputasi nilai yang hilang
🎯 OUTLIER Deteksi dan tangani nilai ekstrem
VALIDASI Verifikasi akhir: konsistensi, logika bisnis
⚠️ PRINSIP PENTING

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.

💡 ILUSTRASI — DAFTAR HADIR GANDA

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).

#
nim
nama
nilai
kelas
status
0
2021001
Andi
85
A
✓ unik
1
2021002
Budi
78
A
✓ unik
2
2021001
Andi
85
A
⚠ duplikat
3
2021003
Citra
92
B
✓ unik
4
2021002
Budhi
78
A
⚠ partial

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.

"jakarta", " JAKARTA", "Jakarta Pusat"
Kapitalisasi + spasi + varian nama
"Jakarta"
Standarisasi .str.title() + strip + mapping
"L", "Laki-laki", "laki laki", "male", "1"
Berbagai representasi jenis kelamin
"L"
Dictionary mapping ke nilai standar
"Rp 5.500.000", "5500000", "5,5 juta"
Format angka tidak seragam
5500000.0 (float)
str.replace + regex + astype
"17/08/2024", "2024-08-17", "17 Aug 2024"
Format tanggal berbeda-beda
Timestamp('2024-08-17')
pd.to_datetime dengan format eksplisit
"TRUE", "Ya", "yes", "1", True
Boolean tidak konsisten
True / False (bool)
Mapping eksplisit ke dtype bool

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.

💡 ILUSTRASI — OUTLIER NOISE vs SINYAL

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.

Metode IQR (Tukey's Fence)
Cocok untuk distribusi tidak normal, robust terhadap outlier itu sendiri
Batas Bawah = Q1 − 1.5 × IQR Batas Atas = Q3 + 1.5 × IQR
✓ Tidak mengasumsikan distribusi normal
✓ Mudah dipahami dan diinterpretasikan
✗ Bisa terlalu agresif untuk distribusi dengan ekor panjang
Metode Z-Score
Cocok untuk distribusi mendekati normal; outlier = |z| > 3
z = (x − x̄) / s Outlier jika |z| > 3
✓ Standar dan banyak digunakan
✗ Mean dan std sendiri sensitif terhadap outlier
✗ Asumsi normalitas harus terpenuhi

Opsi Penanganan Outlier

StrategiKapan DigunakanCara
Hapus barisJelas kesalahan input, tidak mungkin terjadi di dunia nyatadf[mask] setelah identifikasi
Winsorizing/CappingIngin pertahankan baris tapi batasi pengaruh nilai ekstremGanti dengan batas IQR atau persentil 1%-99%
Transformasi logData sangat skewed kanan (income, populasi)np.log1p() — distribusi menjadi lebih simetris
PertahankanOutlier adalah sinyal penting (fraud, anomali)Biarkan, tambahkan fitur flag "is_outlier"
Analisis terpisahOutlier dari subpopulasi berbedaSegmentasi, 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.

Cek rentang nilai yang valid Umur tidak boleh negatif atau > 150. Nilai ujian harus 0–100. IPK harus 0.00–4.00.
Cek konsistensi antar kolom Tanggal lulus tidak boleh sebelum tanggal masuk. Usia saat wisuda tidak boleh < 17 tahun.
Cek referential integrity Jika ada kolom "kode_kelas", pastikan semua nilainya ada di tabel master kelas.
Cek uniqueness constraint NIM, nomor KTP, email — seharusnya unik per individu. Duplikat di sini adalah error.
Cek distribusi dan proporsi Jika 95% mahasiswa tiba-tiba nilainya A semua — ada yang salah dengan data, bukan mahasiswanya.

7. Praktik: Pipeline Data Cleaning Lengkap di Python

7.1 Audit Awal dan Profiling

PYTHON · AUDIT DATA AWAL
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())
📤 OUTPUT
=== 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

PYTHON · PIPELINE CLEANING STEP-BY-STEP
# ── 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()}")
📤 OUTPUT
[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

PYTHON · 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']])
📤 OUTPUT
=== 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

🧩 PERTANYAAN 1 — OUTLIER
Dataset gaji karyawan: Q1=5jt, Q3=12jt. Seorang direktur bergaji 45jt. Dengan metode IQR, apakah gaji direktur ini outlier?
Benar! A. IQR = 12 − 5 = 7 juta. Batas Atas = 12 + 1.5×7 = 22.5 juta. Karena 45 > 22.5, direktur adalah outlier secara statistik. Namun ini bukan berarti harus dihapus! Ini adalah outlier yang bermakna (bukan kesalahan input). Keputusan: pertahankan tapi tambahkan flag "is_outlier", atau analisis data manajemen secara terpisah.
🧩 PERTANYAAN 2 — DUPLIKAT
Dalam dataset transaksi, dua baris memiliki transaction_id berbeda tapi semua kolom lainnya (nama, jumlah, waktu, merchant) persis sama. Tindakan terbaik adalah?
Benar! C. Transaction_id berbeda tidak selalu berarti dua transaksi berbeda — sistem yang bermasalah bisa generate dua ID untuk satu transaksi yang sama (double posting). Dalam konteks keuangan, ini kritis: membayar dua kali karena satu transaksi. Langkah terbaik: investigasi dengan tim bisnis, cek timestamp selisih, dan hubungi sumber data untuk konfirmasi sebelum mengambil keputusan hapus/pertahankan.
🧩 PERTANYAAN 3 — VALIDASI
Setelah cleaning, distribusi data nilai UAS menunjukkan 98% mahasiswa mendapat nilai A (90–100). Padahal biasanya distribusi nilai lebih merata. Apa yang paling mungkin terjadi?
Benar! B. Distribusi yang tidak masuk akal (98% nilai A) adalah tanda bahaya — ini adalah bagian dari validasi distribusi dan proporsi. Kemungkinan: data hanya berisi mahasiswa yang nilai A saja (filtering salah), kolom nilai tertukar, atau nilai diimput ulang dengan nilai standar. Validasi bisnis bukan hanya tentang rentang nilai (0–100) tapi juga tentang apakah distribusi keseluruhan masuk akal secara konteks.
📋 Ringkasan Sesi 9