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:

  • StandardScaler menggeser data agar memiliki rata-rata 0 dan simpangan baku 1 (z-score).
  • MinMaxScaler menarik data ke rentang [0, 1] berdasarkan nilai minimum dan maksimum.
python
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:

text
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):

python
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:

text
Jarak sebelum scaling: 27.52
Jarak setelah scaling: 1.41

Sebelum 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:

python
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:

text
 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              0

Kolom 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():

python
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:

text
[[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:

python
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:

text
   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.0

Method .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:

python
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:

text
[[  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:

python
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:

text
                   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.00

Sebagai rangkuman, berikut cara menangani tiap jenis fitur:

Jenis fiturMasalah umumPenanganan umum
Numerik rentang lebarMendominasi jarakStandardScaler, MinMaxScaler
Kategori nominalTidak bisa dihitung modelOne-hot encoding
TanggalModel tidak bisa membaca kalenderEkstraksi hari, bulan, rasio
Fitur duplikat/redundanKorelasi tinggi, dimensi membengkakSeleksi 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.