Mempelajari exploratory data analysis untuk memahami distribusi, outlier, dan missing values, lalu preprocessing yang benar: scaling dengan StandardScaler/MinMaxScaler, encoding One-Hot/Label, serta train/test split tanpa data leakage berbasis scikit-learn Pipeline.

Setelah di episode 3 kita menguasai matematika dasar — vektor, gradien, dan statistik — pada episode ini kita menghadapi realita pahit yang paling sering memakan waktu di proyek ML: data kotor. Di dunia nyata, 70-80% waktu engineer ML habis di tahap eksplorasi dan preprocessing, bukan di model.
Mengapa EDA dan preprocessing penting? Karena model sebaik apa pun tidak bisa menambal data yang rusak. Kalian tidak bisa "memprogram" model untuk paham bahwa kolom umur berisi string, nilai hilang, dan outlier yang menggila. Tugas kalian membuat data bersih dan konsisten sehingga model bisa belajar pola yang benar.
EDA adalah proses memahami data sebelum modeling. Tujuannya menjawab tiga pertanyaan: data apa yang ada? seberapa bersih? bagaimana distribusinya?
import pandas as pd
df = pd.read_csv("customer.csv")
print(df.head())
print(df.info())
print(df.describe())df.head(): sekilas 5 baris pertama.df.info(): tipe data dan jumlah nilai non-null per kolom.df.describe(): statistik deskriptif (mean, std, min, max, kuartil).Dari info() kalian langsung melihat kolom mana yang punya missing values dan kolom yang tipe datanya salah (misalnya umur berupa objek/string).
Visualisasi distribusi setiap kolom numerik dengan histogram, dan deteksi outlier dengan boxplot:
import matplotlib.pyplot as plt
import seaborn as sns
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
sns.histplot(df["pendapatan"], ax=axes[0])
sns.boxplot(df["pendapatan"], ax=axes[1])
plt.show()Outlier adalah nilai yang menyimpang jauh dari mayoritas data. Tidak semua outlier harus dihapus — kadang ia adalah sinyal penting (misalnya transaksi fraud berjumlah sangat besar). Yang penting: kalian tahu keberadaannya dan memutuskan dengan sadar.
Nilai hilang muncul dalam berbagai bentuk: NaN, string kosong, atau 0 yang sebenarnya kosong. Pertama, hitung jumlahnya:
print(df.isnull().sum())
print(df.isnull().mean() * 100)Strategi penanganan, tergantung konteks:
| Strategi | Kapan dipakai |
|---|---|
| Hapus baris | Missing sedikit (di bawah 5%) |
| Hapus kolom | Missing sangat banyak (di atas 70%) |
| Isi median/mean | Kolom numerik dengan distribusi normal |
| Isi modus | Kolom kategorikal |
| Isi 0 atau -1 | Nilai 0 bermakna (misal pendapatan = 0) |
Note
Jangan pernah mengisi missing values dengan statistik dari seluruh data sebelum split — ini bocor. Hitung median dari train set saja, lalu terapkan ke test set. Cara amannya adalah melalui Pipeline yang kita bahas di bawah.
Setelah data dipahami, saatnya menormalkan bentuknya agar model bisa belajar dengan efisien.
Algoritma berbasis jarak (kNN, k-Means) dan gradient-based (regresi, neural network) sangat sensitif terhadap skala. Kolom pendapatan (jutaan) akan mendominasi kolom umur (puluhan). Dua scaler paling umum:
[0, 1]. Bagus untuk data terbatas rentangnya.from sklearn.preprocessing import StandardScaler, MinMaxScaler
X = df[["umur", "pendapatan"]].values
standard = StandardScaler().fit_transform(X)
minmax = MinMaxScaler().fit_transform(X)Model tidak bisa membaca string "Jakarta" — harus diubah menjadi angka. Dua pendekatan dasar:
import pandas as pd
df = pd.get_dummies(df, columns=["kota"], prefix="kota")Satu kolom kota dengan 5 kategori menjadi 5 kolom biner. Detail dan encoding lanjutan (target encoding, frequency) dibahas di episode 15.
Urutan yang benar: lakukan EDA → split → fit preprocessing di train → transform test. Jangan lakukan preprocessing pada seluruh data sebelum split.
from sklearn.model_selection import train_test_split
X = df.drop("churn", axis=1)
y = df["churn"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)stratify=y menjaga proporsi kelas di train dan test sama — penting untuk data yang tidak seimbang (dibahas di episode 6).
Gabungkan semuanya dalam satu Pipeline agar urutan dan statistik preprocessing terkunci rapi:
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.ensemble import RandomForestClassifier
num_cols = ["umur", "pendapatan"]
cat_cols = ["kota"]
preprocessor = ColumnTransformer([
("num", Pipeline([
("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler()),
]), num_cols),
("cat", OneHotEncoder(handle_unknown="ignore"), cat_cols),
])
pipeline = Pipeline([
("prep", preprocessor),
("clf", RandomForestClassifier(random_state=42)),
])
pipeline.fit(X_train, y_train)
print(pipeline.score(X_test, y_test))ColumnTransformer memungkinkan kolom numerik dan kategorikal diperlakukan berbeda dalam satu alur. Seluruh statistik (median, skala, encoder) dihitung dari train set saja, dan saat pipeline.predict(X_test) dipanggil, transformasi yang sama diterapkan otomatis — tanpa risiko leakage.
Pada episode 4 ini, kalian telah menguasai langkah yang paling banyak menyita waktu di proyek ML nyata.
Inti yang harus dibawa pulang:
Pipeline + ColumnTransformer untuk alur yang aman dari data leakage.Di episode 5 selanjutnya kita akan masuk ke supervised learning yang pertama: regression — linear & polynomial regression, regularisasi (Ridge/Lasso/ElasticNet), serta evaluasi dengan MSE, MAE, dan R² melalui studi kasus prediksi harga rumah. Sampai jumpa di episode 5!