Algoritma machine learning tidak melihat dunia seperti manusia. Model tidak tahu bahwa “Bandung” adalah nama kota atau bahwa pendapatan diukur dalam jutaan rupiah; model hanya menerima angka dan menghitung jarak di antaranya. Feature engineering adalah seni menerjemahkan data mentah menjadi kolom-kolom yang membuat pola mudah ditemukan algoritma. Pada chapter ini kita berempat teknik inti: scaling, encoding kategori, penciptaan fitur baru, dan seleksi fitur.
Scaling dengan StandardScaler dan MinMaxScaler #
Banyak algoritma sensitif terhadap besaran angka. Scaling menyamakan rentang fitur agar tidak ada kolom yang mendominasi murni karena satuannya besar. Dua transformer yang paling sering dipakai:
StandardScalermenggeser data agar memiliki rata-rata 0 dan simpangan baku 1 (z-score).MinMaxScalermenarik data ke rentang [0, 1] berdasarkan nilai minimum dan maksimum.
import numpy as np
from sklearn.preprocessing import MinMaxScaler, StandardScaler
pendapatan = np.array([[4.2], [5.0], [6.5], [8.0], [9.5]])
standard = StandardScaler().fit_transform(pendapatan)
minmax = MinMaxScaler().fit_transform(pendapatan)
print("Asli :", pendapatan.ravel())
print("Standard :", standard.ravel().round(3))
print("MinMax :", minmax.ravel().round(3))Output yang Dihasilkan:
Asli : [4.2 5. 6.5 8. 9.5]
Standard : [-1.262 -0.848 -0.072 0.703 1.479]
MinMax : [0. 0.151 0.434 0.717 1. ]Kedua transformer mempertahankan urutan dan bentuk sebaran data; keduanya hanya mengganti skala sumbunya. Pilih StandardScaler ketika data mendekati sebaran normal, dan MinMaxScaler ketika kita ingin rentang output yang pasti [0, 1]. Keduanya berbahaya di hadapan pencilan ekstrem, karena nilai minimum, maksimum, dan rata-rata bisa tertarik oleh satu baris data tak wajar.
Kenapa Scaling Penting untuk k-NN #
Model k-Nearest Neighbors menghitung jarak Euclidean antar baris data. Ketika satu fitur bernilai puluhan dan fitur lain bernilai satuan, jarak dikuasai fitur berskala besar. Mari kita ukur jarak dua pelanggan dengan fitur usia (25 dan 52 tahun) dan pendapatan_juta (4.2 dan 9.5):
fitur = np.array([
[25, 4.2],
[52, 9.5],
])
fitur_skala = MinMaxScaler().fit_transform(fitur)
jarak_mentah = np.linalg.norm(fitur[1] - fitur[0])
jarak_skala = np.linalg.norm(fitur_skala[1] - fitur_skala[0])
print(f"Jarak sebelum scaling: {jarak_mentah:.2f}")
print(f"Jarak setelah scaling: {jarak_skala:.2f}")Output yang Dihasilkan:
Jarak sebelum scaling: 27.52
Jarak setelah scaling: 1.41Sebelum scaling, selisih usia 27 tahun menyumbang hampir seluruh jarak 27.52; selisih pendapatan 5.3 juta nyaris tidak terasa. Setelah scaling, kedua fitur berkontribusi seimbang pada jarak 1.41. Tanpa scaling, k-NN, K-Means, dan PCA secara efektif mengabaikan fitur berskala kecil. Algoritma berbasis pohon seperti decision tree dan random forest adalah pengecualian: algoritma tersebut membelah berdasarkan ambang per fitur sehingga scaling tidak mengubah hasilnya.
One-Hot Encoding untuk Fitur Kategori #
Model linear membutuhkan input numerik; string “Bandung” tidak bisa dikalikan koefisien. Solusinya, setiap nilai kategori dipecah menjadi kolom biner sendiri, teknik yang disebut one-hot encoding:
import pandas as pd
df = pd.DataFrame({
"kota": ["Bandung", "Jakarta", "Surabaya", "Bandung", "Jakarta"],
"usia": [25, 32, 47, 52, 38],
})
df_dummies = pd.get_dummies(df, columns=["kota"], dtype=int)
print(df_dummies.to_string(index=False))Output yang Dihasilkan:
usia kota_Bandung kota_Jakarta kota_Surabaya
25 1 0 0
32 0 1 0
47 0 0 1
52 1 0 0
38 0 1 0Kolom kota tiga nilai berubah menjadi tiga kolom biner: kota_Bandung, kota_Jakarta, dan kota_Surabaya. Fungsi pd.get_dummies() memudahkan eksplorasi, tetapi untuk workflow training, class OneHotEncoder dari scikit-learn lebih aman karena encoder menyimpan daftar kategori yang pernah dilihat saat fit():
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(sparse_output=False)
matriks = encoder.fit_transform(df[["kota"]])
print(matriks)
print(encoder.get_feature_names_out())Output yang Dihasilkan:
[[1. 0. 0.]
[0. 1. 0.]
[0. 0. 1.]
[1. 0. 0.]
[0. 1. 0.]]
['kota_Bandung' 'kota_Jakarta' 'kota_Surabaya']Saat data baru memuat kota keempat yang belum pernah dilihat, OneHotEncoder dapat memperlakukannya sebagai kolom nol (parameter handle_unknown="ignore") alih-alih gagal total seperti pd.get_dummies() yang bisa menghasilkan kolom berbeda antara data latih dan data prediksi.
Membuat Fitur Baru dari Fitur Lama #
Seringkali informasi paling prediktif justru fitur turunan yang kita rancang sendiri. Kolom tanggal misalnya, tidak bisa dipakai model secara langsung, tetapi turunannya sangat informatif:
df_pesanan = pd.DataFrame({
"tanggal": pd.to_datetime(["2025-06-01", "2025-06-15", "2025-07-20", "2025-12-25"]),
"total_belanja": [120000, 350000, 200000, 900000],
"jumlah_item": [3, 10, 5, 25],
})
df_pesanan["hari_dalam_minggu"] = df_pesanan["tanggal"].dt.dayofweek
df_pesanan["bulan"] = df_pesanan["tanggal"].dt.month
df_pesanan["harga_per_item"] = df_pesanan["total_belanja"] / df_pesanan["jumlah_item"]
print(df_pesanan.to_string(index=False))Output yang Dihasilkan:
tanggal total_belanja jumlah_item hari_dalam_minggu bulan harga_per_item
2025-06-01 120000 3 6 6 40000.0
2025-06-15 350000 10 6 6 35000.0
2025-07-20 200000 5 6 7 40000.0
2025-12-25 900000 25 3 12 36000.0Method .dt.dayofweek mengekstrak hari (0 = Senin, 6 = Minggu) dan .dt.month mengekstrak bulan, sehingga model dapat belajar pola musiman. Rasio harga_per_item menangkap segmen belanja yang tidak terlihat dari kedua kolom aslinya. Prinsipnya: fitur baru yang baik adalah fitur yang membuat pola menjadi lebih dekat dengan bentuk linear atau lebih pendek jarak antar kelompok sejenis.
Seleksi Fitur dan Prinsip Garbage In Garbage Out #
Tidak semua kolom membantu. Fitur dengan variansi nol (nilainya sama di semua baris) jelas tidak membedakan apa pun, dan fitur yang berkorelasi sempurna hanya menambah dimensi tanpa informasi baru:
from sklearn.feature_selection import VarianceThreshold
X_sintetis = np.array([
[0, 2.0, 100],
[1, 4.0, 101],
[0, 6.0, 100],
[1, 8.0, 101],
])
pemilih = VarianceThreshold(threshold=0.1)
print(pemilih.fit_transform(X_sintetis))Output yang Dihasilkan:
[[ 0. 2. 100.]
[ 1. 4. 101.]
[ 0. 6. 100.]
[ 1. 8. 101.]]Class VarianceThreshold membuang kolom yang variansinya di bawah ambang 0.1. Pada contoh di atas, seluruh kolom lolos karena variansi minimal ketiganya masih di atas ambang; coba ganti kolom pertama menjadi [0, 0, 0, 0] dan kolom tersebut akan otomatis dibuang. Selain variansi, korelasi antar fitur juga layak diperiksa. Matriks korelasi dataset iris memperlihatkan petal length (cm) dan petal width (cm) berkorelasi 0.96:
from sklearn.datasets import load_iris
iris = load_iris()
df_iris = pd.DataFrame(iris.data, columns=iris.feature_names)
print(df_iris.corr().round(2).to_string())Output yang Dihasilkan:
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
sepal length (cm) 1.00 -0.12 0.87 0.82
sepal width (cm) -0.12 1.00 -0.43 -0.37
petal length (cm) 0.87 -0.43 1.00 0.96
petal width (cm) 0.82 -0.37 0.96 1.00Sebagai rangkuman, berikut cara menangani tiap jenis fitur:
| Jenis fitur | Masalah umum | Penanganan umum |
|---|---|---|
| Numerik rentang lebar | Mendominasi jarak | StandardScaler, MinMaxScaler |
| Kategori nominal | Tidak bisa dihitung model | One-hot encoding |
| Tanggal | Model tidak bisa membaca kalender | Ekstraksi hari, bulan, rasio |
| Fitur duplikat/redundan | Korelasi tinggi, dimensi membengkak | Seleksi fitur, cek korelasi |
Prinsip penutup yang mengikat semuanya: garbage in, garbage out. Model tidak memperbaiki fitur yang salah; model menggeneralisasi kualitas fitur yang diberikan. Satu jam merapikan fitur sering menaikkan skor lebih besar daripada sehari mencoba-coba algoritma baru.