Data Cleaning Lanjutan: Menangani Outlier dan Inkonsistensi pada Dataset Real-World

Lhuqita Fazry
Data Science Pandas Data Cleaning Python Data Preprocessing
Data Cleaning Lanjutan: Menangani Outlier dan Inkonsistensi pada Dataset Real-World

Dataset dari dunia nyata hampir tidak pernah dalam kondisi siap analisis. Nilai ekstrem yang tidak wajar, format penulisan yang tidak seragam, hingga baris duplikat yang lolos dari sistem input adalah masalah klasik yang harus diselesaikan sebelum data digunakan untuk modeling.

Teknik dasar seperti dropna() dan fillna() menangani missing values, tetapi tidak cukup untuk menghadapi outlier dan inkonsistensi struktural. Outlier dapat mendistorsi distribusi dan menyebabkan korelasi antar fitur menjadi menyesatkan. Inkonsistensi format membuat operasi join gagal dan agregasi menghasilkan angka yang salah.

Mendeteksi Outlier dengan Metode Statistik

Outlier adalah nilai yang berada jauh dari mayoritas observasi dalam dataset. Dalam konteks statistik, kita perlu mendefinisikan "jauh" secara kuantitatif. Dua metode yang paling umum digunakan adalah Interquartile Range (IQR) dan Z-Score.

Metode IQR menghitung kuartil pertama (Q1) dan kuartil ketiga (Q3), lalu menetapkan batas: bawah = Q1 − 1.5 × IQR dan atas = Q3 + 1.5 × IQR. Nilai di luar rentang ini adalah outlier. IQR cocok untuk data skewed karena tidak mengasumsikan distribusi normal.

Metode Z-Score menghitung jarak setiap nilai dari mean dalam satuan standard deviation dengan threshold ±3. Metode ini lebih cocok untuk data yang mendekati distribusi normal.

pythonpython
import pandas as pd
import numpy as np

np.random.seed(42)
n = 1000
data_normal = np.random.normal(50, 10, n)
data_skewed = np.random.exponential(20, n)

outlier_idx = np.random.choice(n, 30, replace=False)
data_normal[outlier_idx] = np.random.uniform(90, 120, 30)
data_skewed[outlier_idx] = np.random.uniform(120, 200, 30)

df = pd.DataFrame({
    'nilai_normal': data_normal,
    'nilai_skewed': data_skewed
})

# Deteksi outlier dengan IQR
def detect_outliers_iqr(df, column):
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR
    outliers = df[(df[column] < lower) | (df[column] > upper)]
    return len(outliers), lower, upper

# Deteksi outlier dengan Z-Score
def detect_outliers_zscore(df, column, threshold=3):
    mean = df[column].mean()
    std = df[column].std()
    z_scores = np.abs((df[column] - mean) / std)
    outliers = df[z_scores > threshold]
    return len(outliers), mean, std

iqr_count, _, _ = detect_outliers_iqr(df, 'nilai_normal')
z_count, _, _ = detect_outliers_zscore(df, 'nilai_normal')

print(f"Outlier terdeteksi (IQR) pada data normal: {iqr_count}")
print(f"Outlier terdeteksi (Z-Score) pada data normal: {z_count}")

iqr_count_skew, _, _ = detect_outliers_iqr(df, 'nilai_skewed')
z_count_skew, _, _ = detect_outliers_zscore(df, 'nilai_skewed')

print(f"Outlier terdeteksi (IQR) pada data skewed: {iqr_count_skew}")
print(f"Outlier terdeteksi (Z-Score) pada data skewed: {z_count_skew}")

Output:

text
Outlier terdeteksi (IQR) pada data normal: 36
Outlier terdeteksi (Z-Score) pada data normal: 30
Outlier terdeteksi (IQR) pada data skewed: 74
Outlier terdeteksi (Z-Score) pada data skewed: 32

Perhatikan bahwa pada data skewed, IQR cenderung lebih akurat karena tidak terpengaruh oleh ekstremitas distribusi, sementara Z-Score bisa menghasilkan false positive karena mean dan standard deviation ikut terdistorsi oleh outlier itu sendiri.

Strategi Menangani Outlier tanpa Kehilangan Informasi

Setelah outlier terdeteksi, keputusan handling harus didasarkan pada konteks bisnis dan proporsi outlier.

Trimming menghapus seluruh baris yang mengandung outlier. Aman hanya jika outlier di bawah 5% dataset. Jika terlalu banyak baris dihapus, kita kehilangan informasi dari kolom lain yang valid.

Capping (Winsorizing) mengganti nilai outlier dengan nilai batas IQR. Ukuran dataset tetap utuh, hanya nilai ekstrem yang dimodifikasi.

Imputation dengan median cocok untuk data highly skewed karena median lebih robust terhadap outlier dibandingkan mean.

Data Science with Python
Data Science • Beginner

Data Science with Python

Master the art of data analysis, visualization, and predictive modeling.

Daftar
pythonpython
def handle_outliers(df, column, method='cap'):
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR

    if method == 'trim':
        return df[(df[column] >= lower) & (df[column] <= upper)]

    elif method == 'cap':
        df_clean = df.copy()
        df_clean[column] = df_clean[column].clip(lower, upper)
        return df_clean

    elif method == 'median_impute':
        df_clean = df.copy()
        median_val = df_clean[column].median()
        mask = (df_clean[column] < lower) | (df_clean[column] > upper)
        df_clean.loc[mask, column] = median_val
        return df_clean

# Bandingkan statistik sebelum dan sesudah handling
print("Statistik awal - data skewed:")
print(df['nilai_skewed'].describe())

df_clean = handle_outliers(df, 'nilai_skewed', method='cap')
print("\nStatistik setelah capping:")
print(df_clean['nilai_skewed'].describe())

df_imputed = handle_outliers(df, 'nilai_skewed', method='median_impute')
print("\nStatistik setelah median imputation:")
print(df_imputed['nilai_skewed'].describe())

Output:

text
Statistik awal - data skewed:
count    1000.000000
mean       24.411178
std        30.906848
min         0.064469
25%         6.013567
50%        15.177003
75%        29.823633
max       197.654124
Name: nilai_skewed, dtype: float64

Statistik setelah capping:
count    1000.000000
mean       20.850458
std        18.698742
min         0.064469
25%         6.013567
50%        15.177003
75%        29.823633
max        65.538731
Name: nilai_skewed, dtype: float64

Statistik setelah median imputation:
count    1000.000000
mean       17.123690
std        13.791139
min         0.064469
25%         6.013567
50%        15.163642
75%        24.273540
max        64.166951
Name: nilai_skewed, dtype: float64

Perhatikan perubahan pada nilai max dan standard deviation setelah handling. Capping menurunkan nilai maksimum secara signifikan sementara median imputation menjaga distribusi tetap mendekati aslinya. Pilih strategi berdasarkan kebutuhan analisis: jika nilai ekstrem dianggap noise, gunakan capping; jika outlier mungkin mengandung sinyal, pertimbangkan imputation.

Mengidentifikasi Inkonsistensi dan Duplikasi Data

Inkonsistensi data sering kali lebih sulit dideteksi dibandingkan outlier karena tidak bisa diidentifikasi hanya dengan statistik deskriptif. Masalah ini muncul dalam berbagai bentuk: duplikasi baris, format yang tidak seragam, hingga kontradiksi logis antar kolom.

Duplikasi eksak terjadi ketika baris yang sama persis muncul lebih dari satu kali. Ini bisa disebabkan oleh kegagalan sistem yang menginsert data dua kali atau proses ETL yang tidak memiliki mekanisme deduplikasi. Fungsi drop_duplicates() menangani kasus ini dengan mudah.

Duplikasi parsial lebih kompleks: ID yang sama muncul dengan nilai berbeda di kolom lain. Kita perlu menentukan baris mana yang valid — bisa berdasarkan timestamp terbaru, sumber data yang lebih terpercaya, atau aturan bisnis tertentu.

Inkonsistensi format adalah masalah klasik pada data yang dikumpulkan dari berbagai sumber. Tanggal bisa ditulis dalam format DD/MM/YYYY di satu sistem dan YYYY-MM-DD di sistem lain. Kategori bisa memiliki variasi penulisan seperti "Laki-laki", "Male", "L", atau "Pria" yang semuanya merujuk pada konsep yang sama.

pythonpython
# Dataset sintetis dengan inkonsistensi
data = {
    'id': [1, 2, 2, 3, 4, 5, 6],
    'nama': ['Andi', 'Budi', 'Budi', 'Citra', 'Dewi', 'Eko', 'Fajar'],
    'tanggal_lahir': ['1990-05-12', '1988/11/23', '1988-11-23',
                      '1995-07-30', '1992-01-15', '1993/03/08', '1994-12-20'],
    'jenis_kelamin': ['L', 'Male', 'Pria', 'P', 'Perempuan', 'Laki-laki', 'M'],
    'gaji': [5000000, 7500000, 7500000, 6200000, 8100000, 4500000, 9200000],
    'umur': [34, 36, 36, 29, 33, 31, 30]
}
df_raw = pd.DataFrame(data)

# 1. Deteksi duplikasi eksak
exact_dup = df_raw.duplicated().sum()
print(f"Duplikasi eksak: {exact_dup} baris")

# 2. Deteksi duplikasi parsial (ID sama, kolom lain berbeda)
partial_dup = df_raw[df_raw.duplicated(subset=['id'], keep=False)]
print(f"\nDuplikasi parsial (ID sama):")
print(partial_dup)

# 3. Deteksi inkonsistensi format tanggal
def is_iso_date(val):
    try:
        pd.to_datetime(val, format='%Y-%m-%d')
        return True
    except:
        return False

inconsistent_dates = df_raw[~df_raw['tanggal_lahir'].apply(is_iso_date)]
print(f"\nTanggal dengan format tidak standar: {len(inconsistent_dates)} baris")

# 4. Standarisasi kategori jenis kelamin
gender_map = {
    'L': 'Laki-laki', 'Male': 'Laki-laki', 'M': 'Laki-laki', 'Pria': 'Laki-laki',
    'P': 'Perempuan', 'Female': 'Perempuan', 'W': 'Perempuan'
}
df_raw['jenis_kelamin_clean'] = df_raw['jenis_kelamin'].map(gender_map)
print(f"\nKategori unik sebelum standarisasi: {df_raw['jenis_kelamin'].unique()}")
print(f"Kategori unik setelah standarisasi: {df_raw['jenis_kelamin_clean'].unique()}")

Output:

text
Duplikasi eksak: 0 baris

Duplikasi parsial (ID sama):
   id  nama tanggal_lahir jenis_kelamin     gaji  umur
1   2  Budi    1988/11/23          Male  7500000    36
2   2  Budi    1988-11-23          Pria  7500000    36

Tanggal dengan format tidak standar: 2 baris

Kategori unik sebelum standarisasi: ['L' 'Male' 'Pria' 'P' 'Perempuan' 'Laki-laki' 'M']
Kategori unik setelah standarisasi: ['Laki-laki' 'Perempuan' nan]

Hasil dari kode di atas memberi kita gambaran lengkap tentang kondisi data: berapa banyak duplikasi yang perlu dibersihkan, format mana yang perlu distandarisasi, dan kolom mana yang memerlukan pemetaan kategori. Informasi ini menjadi dasar untuk membangun pipeline pembersihan yang sistematis.

Membangun Pipeline Pembersihan Data yang Reusable

Membersihkan data secara manual setiap kali menerima dataset baru adalah pekerjaan yang repetitif dan rentan kesalahan. Solusi yang lebih baik adalah membangun sebuah pipeline yang menggabungkan semua teknik deteksi dan handling ke dalam satu fungsi yang reusable.

Diagram alur proses data science

Gambar: Diagram alur proses data science yang menunjukkan tahapan dari pengumpulan data, pembersihan (data cleaning), analisis eksplorasi, hingga menghasilkan data product — Sumber: [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Data_visualization_process_v1.png) — Lisensi: CC BY-SA 3.0

Pipeline yang baik memiliki struktur: deteksi → report → handling → validasi ulang. Setiap tahap menghasilkan log sehingga kita tahu persis apa yang berubah dan mengapa. Ini penting untuk audit trail, terutama ketika data digunakan untuk keputusan bisnis.

pythonpython
import pandas as pd
import numpy as np
from datetime import datetime

def clean_dataset(df, outlier_method='cap', id_column=None):
    """
    Pipeline pembersihan data: deteksi outlier, duplikasi, dan inkonsistensi.
    Mengembalikan DataFrame bersih dan report perubahan.
    """
    report = {'initial_rows': len(df), 'actions': []}
    df_clean = df.copy()

    # Step 1: Handle duplikasi eksak
    dup_count = df_clean.duplicated().sum()
    if dup_count > 0:
        df_clean = df_clean.drop_duplicates()
        report['actions'].append(f'Menghapus {dup_count} baris duplikat eksak')

    # Step 2: Handle duplikasi parsial (keep first occurrence)
    if id_column and id_column in df_clean.columns:
        dup_partial = df_clean.duplicated(subset=[id_column], keep=False).sum()
        if dup_partial > 0:
            df_clean = df_clean.drop_duplicates(subset=[id_column], keep='first')
            report['actions'].append(
                f'Menghapus duplikasi parsial pada kolom "{id_column}", menyimpan first occurrence'
            )

    # Step 3: Handle outlier pada semua kolom numerik
    numeric_cols = df_clean.select_dtypes(include=[np.number]).columns
    for col in numeric_cols:
        Q1 = df_clean[col].quantile(0.25)
        Q3 = df_clean[col].quantile(0.75)
        IQR = Q3 - Q1
        lower = Q1 - 1.5 * IQR
        upper = Q3 + 1.5 * IQR

        outlier_mask = (df_clean[col] < lower) | (df_clean[col] > upper)
        n_outliers = outlier_mask.sum()

        if n_outliers > 0:
            if outlier_method == 'cap':
                df_clean[col] = df_clean[col].clip(lower, upper)
                report['actions'].append(
                    f'Kolom "{col}": {n_outliers} outlier di-capping (batas: {lower:.2f} - {upper:.2f})'
                )
            elif outlier_method == 'trim':
                df_clean = df_clean[~outlier_mask]
                report['actions'].append(
                    f'Kolom "{col}": {n_outliers} outlier dihapus'
                )

    report['final_rows'] = len(df_clean)
    report['rows_removed'] = report['initial_rows'] - report['final_rows']

    return df_clean, report

# Demonstrasi pipeline
np.random.seed(123)
demo_data = pd.DataFrame({
    'id': [1, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    'score': [85, 85, 92, 78, 88, 200, 91, 76, 500, 83],
    'age': [25, 25, 30, 22, 28, 35, 27, 24, -5, 31]
})

df_result, cleaning_report = clean_dataset(demo_data, id_column='id')

print("=== Report Pembersihan Data ===")
for action in cleaning_report['actions']:
    print(f"  - {action}")
print(f"\nBaris awal: {cleaning_report['initial_rows']}")
print(f"Baris akhir: {cleaning_report['final_rows']}")
print(f"Baris dihapus: {cleaning_report['rows_removed']}")

Output:

text
=== Report Pembersihan Data ===
  - Menghapus 1 baris duplikat eksak
  - Kolom "score": 2 outlier di-capping (batas: 69.50 - 105.50)
  - Kolom "age": 1 outlier di-capping (batas: 15.00 - 39.00)

Baris awal: 10
Baris akhir: 9
Baris dihapus: 1

Pipeline ini menerima DataFrame mentah dan mengembalikan DataFrame bersih beserta report yang mendokumentasikan setiap perubahan. Kita bisa langsung mengintegrasikan fungsi clean_dataset() ke dalam workflow analisis harian sehingga setiap dataset yang masuk otomatis melalui proses validasi yang konsisten.

Kemampuan membersihkan data secara sistematis adalah fondasi dari setiap proyek data science yang kredibel. Di Rumah Coding, kami mengajarkan teknik-teknik data preprocessing ini dalam program Data Science yang mencakup seluruh pipeline: dari data wrangling, exploratory analysis, hingga model deployment. Pelajari langsung dengan dataset real-world dan bangun portofolio analisis data yang siap industri.

Course Terkait

E-commerce Sales Dashboard
Premium Course Data Science

Data Science with Python

Master the art of data analysis, visualization, and predictive modeling.

Capstone Project

E-commerce Sales Dashboard

  • Data Cleaning Pipeline
  • Interactive Charts
  • Sales Forecasting Model
7 Weeks Beginner
Lihat Detail Course
GreenGuard: Intelligent Plant Disease Diagnosis Web App
Premium Course Machine Learning

Deep Learning Bootcamp

A beginner-friendly, highly interactive bootcamp designed to take you from foundational concepts to deploying real-world Artificial Intelligence applications. Through a completely project-based approach, you will master the core of Deep Learning, Artificial Neural Networks, and Computer Vision using Python and TensorFlow, ultimately building a professional-grade AI web application for your portfolio.

Capstone Project

GreenGuard: Intelligent Plant Disease Diagnosis Web App

  • Interactive Image Upload UI: A clean, user-friendly interface built with Streamlit that supports drag-and-drop image uploads directly from a computer or mobile phone.
  • Real-Time AI Inference: Utilizes a lightweight, optimized CNN model (like MobileNetV2) to process the image and return a diagnosis in seconds without heavy server load.
  • Confidence Scoring Dashboard: Visually displays the model's prediction probability (e.g., "95% confident this is Tomato Late Blight") using interactive progress bars or charts.
7 Weeks Intermediate
Lihat Detail Course
Domain-Specific AI Knowledge Assistant
Premium Course Machine Learning

LLM Bootcamp

This project-based bootcamp is designed for beginners to dive practically into the world of Large Language Models (LLMs). Through hands-on building, you will learn how to interact with top-tier AI APIs, master prompt engineering, orchestrate complex workflows using LangChain, and implement Retrieval-Augmented Generation (RAG) to query your own documents. By the end of this course, you will have the skills to build, test, and deploy a fully functional, custom AI web application.

Capstone Project

Domain-Specific AI Knowledge Assistant

  • Dynamic Document Processing: A sidebar interface allowing users to upload new PDF or TXT files, which the app automatically chunks, embeds, and stores in the vector database.
  • Context-Aware Chat UI: A modern chat interface built with Streamlit that maintains conversation history, allowing users to ask follow-up questions naturally.
  • Strict Guardrails (Anti-Hallucination): System instructions designed so the AI politely declines to answer questions that fall outside the context of the uploaded documents.
7 Weeks Beginner
Lihat Detail Course

Artikel Terkait