Tutorial 14: Masterclass Rekayasa Fitur (Feature Engineering)
Daftar Isi
Pendahuluan
Rekayasa fitur secara luas dianggap sebagai aktivitas paling berdampak dalam machine learning terapan. Meskipun arsitektur model dan tuning hyperparameter mendapat perhatian signifikan, kualitas dan daya ekspresif fitur input Anda-lah yang pada akhirnya menentukan performa model. Fitur yang direkayasa dengan baik dapat membuat model linear sederhana mengungguli jaringan neural dalam yang fiturnya buruk.
Masterclass ini mencakup spektrum lengkap teknik rekayasa fitur: dari transformasi numerik fundamental dan strategi encoding kategorikal hingga ekstraksi datetime tingkat lanjut, vektorisasi teks, dan pembuatan fitur otomatis. Setiap teknik didemonstrasikan dengan kode Python siap produksi dan panduan praktis tentang kapan dan bagaimana menerapkannya.
Prasyarat
- Python 3.9 atau lebih tinggi
- Pemahaman solid tentang pandas dan NumPy
- Pengetahuan dasar machine learning (klasifikasi, regresi)
- Instal paket yang diperlukan:
pip install pandas numpy scikit-learn categoryencoders featuretools scipy nltk sentence-transformers
Mengapa Rekayasa Fitur Penting
Bayangkan sebuah dataset dengan kolom tanggallahir. Memberikan string tanggal mentah ke model tidak ada artinya. Namun merekayasa fitur seperti usia, bulanlahir, lahirdiakhirpekan, atau kohortgenerasi dari satu kolom tersebut menciptakan sinyal yang sangat informatif. Inilah esensi rekayasa fitur: mengubah data mentah menjadi representasi yang menangkap pola-pola mendasar yang perlu dipelajari model.
Prinsip-prinsip utama:
- Pengetahuan domain adalah aset terbesar Anda. Memahami konteks bisnis membantu Anda membuat fitur yang tidak bisa ditemukan oleh alat otomatis mana pun.
- Kesederhanaan menang. Beberapa fitur yang dipilih dengan baik sering mengungguli ratusan fitur yang berisik.
- Hindari kebocoran data. Jangan pernah menggunakan informasi dari masa depan atau variabel target selama pembuatan fitur.
Transformasi Fitur Numerik
Penskalaan dan Normalisasi
Banyak algoritma (SVM, KNN, neural networks, regresi terregularisasi) sensitif terhadap skala fitur. Penskalaan memastikan semua fitur berkontribusi secara setara.
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
Data sampel
df = pd.DataFrame({
'pendapatan': [30000, 45000, 120000, 55000, 250000, 80000],
'usia': [22, 35, 45, 28, 60, 40],
'jumlahtransaksi': [5, 120, 450, 30, 800, 200],
})
StandardScaler: rata-rata nol, varians satuan (terbaik untuk data terdistribusi normal)
standardscaler = StandardScaler()
df['pendapatanstandard'] = standardscaler.fittransform(df[['pendapatan']])
MinMaxScaler: skala ke rentang [0, 1] (terbaik saat membutuhkan nilai terbatas)
minmaxscaler = MinMaxScaler()
df['pendapatanminmax'] = minmaxscaler.fittransform(df[['pendapatan']])
RobustScaler: menggunakan median dan IQR (terbaik untuk data dengan outlier)
robustscaler = RobustScaler()
df['pendapatanrobust'] = robustscaler.fittransform(df[['pendapatan']])
print(df[['pendapatan', 'pendapatanstandard', 'pendapatanminmax', 'pendapatanrobust']])
Transformasi Log dan Pangkat
Distribusi miring umum ditemukan dalam data dunia nyata (pendapatan, harga, jumlah hitungan). Transformasi log dan pangkat mengurangi kemencengan, membuat data lebih terdistribusi normal.
from sklearn.preprocessing import PowerTransformer
Transformasi log (untuk nilai positif dengan kemencengan kanan)
df['pendapatanlog'] = np.log1p(df['pendapatan']) # log1p menangani nilai nol
Transformasi akar kuadrat (lebih ringan dari log)
df['transaksisqrt'] = np.sqrt(df['jumlahtransaksi'])
Transformasi Box-Cox (menemukan parameter pangkat optimal secara otomatis)
ptboxcox = PowerTransformer(method='box-cox') # Membutuhkan data positif ketat
df['pendapatanboxcox'] = ptboxcox.fittransform(df[['pendapatan']])
Transformasi Yeo-Johnson (menangani nilai nol dan negatif)
ptyeojohnson = PowerTransformer(method='yeo-johnson')
df['pendapatanyeojohnson'] = ptyeojohnson.fittransform(df[['pendapatan']])
Binning dan Diskretisasi
Mengubah variabel kontinu menjadi bin diskrit dapat menangkap hubungan non-linear dan mengurangi dampak outlier.
from sklearn.preprocessing import KBinsDiscretizer
Binning dengan lebar sama
df['usiabin'] = pd.cut(df['usia'], bins=4, labels=['muda', 'dewasa', 'senior', 'lansia'])
Binning berbasis kuantil (frekuensi sama di setiap bin)
df['pendapatankuantil'] = pd.qcut(df['pendapatan'], q=4, labels=['Q1', 'Q2', 'Q3', 'Q4'])
KBinsDiscretizer dengan strategi berbeda
kbd = KBinsDiscretizer(nbins=5, encode='ordinal', strategy='kmeans')
df['pendapatankmeansbin'] = kbd.fittransform(df[['pendapatan']])
Bin kustom berdasarkan domain
def kategorikanpendapatan(pendapatan):
if pendapatan < 40000:
return 'rendah'
elif pendapatan < 80000:
return 'menengah'
elif pendapatan < 150000:
return 'tinggi'
else:
return 'sangattinggi'
df['kategoripendapatan'] = df['pendapatan'].apply(kategorikanpendapatan)
Fitur Interaksi dan Polinomial
Menggabungkan fitur dapat mengungkap hubungan yang tidak bisa diekspresikan oleh fitur individual.
from sklearn.preprocessing import PolynomialFeatures
Fitur interaksi manual
df['pendapatanpertransaksi'] = df['pendapatan'] / (df['jumlahtransaksi'] + 1)
df['interaksiusiapendapatan'] = df['usia'] df['pendapatan']
Fitur polinomial (derajat 2)
poly = PolynomialFeatures(degree=2, includebias=False, interactiononly=False)
kolomnumerik = ['pendapatan', 'usia', 'jumlahtransaksi']
fiturpoly = poly.fittransform(df[kolomnumerik])
namapoly = poly.getfeaturenamesout(kolomnumerik)
dfpoly = pd.DataFrame(fiturpoly, columns=namapoly)
print(f"Fitur asli: {len(kolomnumerik)}, Fitur polinomial: {len(namapoly)}")
Encoding Fitur Kategorikal
One-Hot Encoding
Cocok untuk kategori nominal dengan kardinalitas rendah.
from sklearn.preprocessing import OneHotEncoder
dfcat = pd.DataFrame({
'warna': ['merah', 'biru', 'hijau', 'merah', 'biru', 'hijau'],
'ukuran': ['S', 'M', 'L', 'M', 'L', 'S'],
'target': [1, 0, 1, 0, 1, 0],
})
pandas getdummies (cepat dan sederhana)
dfencoded = pd.getdummies(dfcat, columns=['warna', 'ukuran'], dropfirst=True)
scikit-learn OneHotEncoder (lebih baik untuk pipeline)
ohe = OneHotEncoder(sparseoutput=False, drop='first', handleunknown='ignore')
encoded = ohe.fittransform(dfcat[['warna', 'ukuran']])
encodeddf = pd.DataFrame(encoded, columns=ohe.getfeaturenamesout())
Target Encoding
Memetakan kategori ke rata-rata variabel target. Sangat kuat untuk fitur kardinalitas tinggi tetapi memerlukan penanganan hati-hati untuk menghindari overfitting.
import categoryencoders as ce
df
target = pd.DataFrame({
'kota': ['Jakarta', 'Surabaya', 'Bandung', 'Jakarta', 'Surabaya', 'Bandung', 'Jakarta', 'Surabaya'],
'target': [1, 0, 1, 1, 0, 0, 1, 1],
})
Target encoding dengan smoothing untuk mencegah overfitting
targetencoder = ce.TargetEncoder(cols=['kota'], smoothing=1.0)
dftarget['kotatargetencoded'] = targetencoder.fittransform(
dftarget['kota'], dftarget['target']
)
Implementasi manual dengan k-fold untuk mencegah kebocoran
from sklearn.modelselection import KFold
def targetencodekfold(df, kolom, target, nsplits=5, smoothing=10):
"""Target encoding dengan validasi silang k-fold untuk mencegah kebocoran."""
ratarataglobal = df[target].mean()
encoded = pd.Series(index=df.index, dtype=float)
kf = KFold(nsplits=nsplits, shuffle=True, randomstate=42)
for trainidx, validx in kf.split(df):
train = df.iloc[trainidx]
stats = train.groupby(kolom)[target].agg(['mean', 'count'])
smoothed = (stats['count'] stats['mean'] + smoothing ratarataglobal) / (
stats['count'] + smoothing
)
encoded.iloc[validx] = df.iloc[validx][kolom].map(smoothed).fillna(ratarataglobal)
return encoded
Frequency dan Count Encoding
Mengganti kategori dengan frekuensi kemunculannya. Sederhana namun efektif.
def frequencyencode(df, kolom):
"""Mengganti nilai kategori dengan frekuensinya dalam dataset."""
peta
frekuensi = df[kolom].valuecounts(normalize=True).todict()
return df[kolom].map(petafrekuensi)
def countencode(df, kolom):
"""Mengganti nilai kategori dengan jumlah kemunculannya dalam dataset."""
petajumlah = df[kolom].valuecounts().todict()
return df[kolom].map(petajumlah)
dfcat['warnafreq'] = frequencyencode(dfcat, 'warna')
dfcat['warnacount'] = countencode(dfcat, 'warna')
Ordinal dan Binary Encoding
import categoryencoders as ce
Ordinal encoding (untuk kategori yang memiliki urutan alami)
peta
ordinal = {'S': 1, 'M': 2, 'L': 3, 'XL': 4}
dfcat['ukuranordinal'] = dfcat['ukuran'].map(petaordinal)
Binary encoding (efisien untuk kardinalitas tinggi)
binaryencoder = ce.BinaryEncoder(cols=['warna'])
dfbinary = binaryencoder.fittransform(dfcat[['warna']])
print(dfbinary)
Rekayasa Fitur Datetime
Kolom datetime mengandung sinyal temporal yang kaya. Mengekstraknya dengan benar sangat penting untuk model time-series dan berbasis event.
import pandas as pd
import numpy as np
Buat data datetime sampel
dfwaktu = pd.DataFrame({
'waktuevent': pd.daterange('2025-01-01', periods=1000, freq='H'),
'userid': np.random.randint(1, 50, 1000),
'jumlah': np.random.uniform(10, 500, 1000),
})
Komponen temporal dasar
dfwaktu['jam'] = dfwaktu['waktuevent'].dt.hour
dfwaktu['hariminggu'] = dfwaktu['waktuevent'].dt.dayofweek # 0=Senin
dfwaktu['haribulan'] = dfwaktu['waktuevent'].dt.day
dfwaktu['bulan'] = dfwaktu['waktuevent'].dt.month
dfwaktu['kuartal'] = dfwaktu['waktuevent'].dt.quarter
dfwaktu['tahun'] = dfwaktu['waktuevent'].dt.year
dfwaktu['minggutahun'] = dfwaktu['waktuevent'].dt.isocalendar().week.astype(int)
Flag boolean
dfwaktu['adalahakhirpekan'] = dfwaktu['hariminggu'].isin([5, 6]).astype(int)
dfwaktu['adalahawalbulan'] = dfwaktu['waktuevent'].dt.ismonthstart.astype(int)
dfwaktu['adalahakhirbulan'] = dfwaktu['waktuevent'].dt.ismonthend.astype(int)
Encoding siklikal (penting agar model memahami bahwa jam 23 dekat dengan jam 0)
dfwaktu['jamsin'] = np.sin(2 np.pi dfwaktu['jam'] / 24)
dfwaktu['jamcos'] = np.cos(2 np.pi dfwaktu['jam'] / 24)
dfwaktu['harisin'] = np.sin(2 np.pi dfwaktu['hariminggu'] / 7)
dfwaktu['haricos'] = np.cos(2 np.pi dfwaktu['hariminggu'] / 7)
dfwaktu['bulansin'] = np.sin(2 np.pi dfwaktu['bulan'] / 12)
dfwaktu['bulancos'] = np.cos(2 np.pi * dfwaktu['bulan'] / 12)
Agregasi berbasis waktu per pengguna
dfwaktu = dfwaktu.sortvalues(['userid', 'waktuevent'])
dfwaktu['waktusejakeventterakhir'] = (
dfwaktu.groupby('userid')['waktuevent'].diff().dt.totalseconds()
)
dfwaktu['rataratabergulir24j'] = (
dfwaktu.groupby('userid')['jumlah']
.transform(lambda x: x.rolling(window=24, minperiods=1).mean())
)
Bagian hari
def dapatkanbagianhari(jam):
if 5 <= jam < 12:
return 'pagi'
elif 12 <= jam < 17:
return 'siang'
elif 17 <= jam < 21:
return 'sore'
else:
return 'malam'
dfwaktu['bagianhari'] = dfwaktu['jam'].apply(dapatkanbagianhari)
Rekayasa Fitur Teks
Vektorisasi TF-IDF
from sklearn.featureextraction.text import TfidfVectorizer
dokumen = [
"machine learning adalah bagian dari kecerdasan buatan",
"deep learning menggunakan jaringan neural dengan banyak lapisan",
"pemrosesan bahasa alami menangani data teks",
"computer vision menganalisis gambar dan video",
"reinforcement learning melatih agen melalui reward",
]
TF-IDF dasar
tfidf = TfidfVectorizer(
max
features=100,
mindf=1,
maxdf=0.95,
ngramrange=(1, 2), # Unigram dan bigram
sublineartf=True, # Terapkan normalisasi log
)
matrikstfidf = tfidf.fittransform(dokumen)
namafitur = tfidf.getfeaturenamesout()
print(f"Fitur TF-IDF: {matrikstfidf.shape[1]}")
Fitur Statistik Teks
Fitur sederhana tetapi sangat efektif yang diekstrak dari teks mentah:
import re
def ekstrakfiturteks(teks):
"""Mengekstrak fitur statistik dari teks."""
kata = teks.split()
kalimat = teks.split('.')
return {
'jumlahkarakter': len(teks),
'jumlahkata': len(kata),
'jumlahkalimat': len([s for s in kalimat if s.strip()]),
'rataratapanjangkata': np.mean([len(w) for w in kata]) if kata else 0,
'makspanjangkata': max([len(w) for w in kata]) if kata else 0,
'rasiokataunik': len(set(kata)) / len(kata) if kata else 0,
'rasiohurufbesar': sum(1 for c in teks if c.isupper()) / len(teks) if teks else 0,
'rasiodigit': sum(1 for c in teks if c.isdigit()) / len(teks) if teks else 0,
'jumlahtandabaca': len(re.findall(r'[^\w\s]', teks)),
}
Terapkan ke DataFrame
dfteks = pd.DataFrame({'teks': dokumen})
fiturteks = dfteks['teks'].apply(lambda x: pd.Series(ekstrakfiturteks(x)))
dfteks = pd.concat([dfteks, fiturteks], axis=1)
Embedding Kalimat
Untuk tugas yang memerlukan pemahaman semantik, embedding padat mengungguli TF-IDF sparse:
from sentencetransformers import SentenceTransformer
Muat model sentence transformer yang sudah dilatih
model = SentenceTransformer('all-MiniLM-L6-v2')
Hasilkan embedding (vektor 384 dimensi)
embeddings = model.encode(dokumen, show
progressbar=True)
print(f"Bentuk embedding: {embeddings.shape}") # (5, 384)
Opsional: kurangi dimensi dengan PCA
from sklearn.decomposition import PCA
pca = PCA(n
components=50, randomstate=42)
embedding
tereduksi = pca.fittransform(embeddings)
print(f"Varians yang dijelaskan: {pca.explained
varianceratio.sum():.2%}")
Teknik Seleksi Fitur
Setelah merekayasa banyak fitur, memilih fitur yang paling relevan mengurangi overfitting, meningkatkan interpretabilitas, dan mempercepat pelatihan.
Metode Filter
from sklearn.featureselection import mutualinfoclassif, fclassif, SelectKBest
from sklearn.datasets import makeclassification
Hasilkan dataset sintetis
X, y = makeclassification(nsamples=1000, nfeatures=50, ninformative=10,
nredundant=10, randomstate=42)
namafitur = [f'fitur{i}' for i in range(50)]
Mutual Information (bekerja untuk hubungan non-linear)
skormi = mutualinfoclassif(X, y, randomstate=42)
peringkatmi = pd.Series(skormi, index=namafitur).sortvalues(ascending=False)
print("10 fitur teratas berdasarkan Mutual Information:")
print(peringkatmi.head(10))
ANOVA F-test (terbaik untuk hubungan linear)
selector = SelectKBest(scorefunc=fclassif, k=10)
Xterpilih = selector.fittransform(X, y)
Penghapusan Berbasis Korelasi
def hapusfiturberkorelasi(df, ambangbatas=0.90):
"""Menghapus fitur yang sangat berkorelasi satu sama lain."""
matriks
korelasi = df.corr().abs()
segitigaatas = matrikskorelasi.where(
np.triu(np.ones(matrikskorelasi.shape), k=1).astype(bool)
)
kolomhapus = [col for col in segitigaatas.columns
if any(segitigaatas[col] > ambangbatas)]
print(f"Menghapus {len(kolomhapus)} fitur berkorelasi: {kolomhapus}")
return df.drop(columns=kolomhapus)
Seleksi Berbasis Model (Feature Importance)
from sklearn.ensemble import RandomForestClassifier
from sklearn.featureselection import SelectFromModel
Latih Random Forest dan gunakan feature importance
rf = RandomForestClassifier(nestimators=100, randomstate=42)
rf.fit(X, y)
kepentingan = pd.Series(rf.featureimportances, index=namafitur)
kepentingan = kepentingan.sortvalues(ascending=False)
print("10 fitur teratas berdasarkan Random Forest importance:")
print(kepentingan.head(10))
Seleksi otomatis berdasarkan ambang batas importance
selector = SelectFromModel(rf, threshold='median')
Xterpilih = selector.fittransform(X, y)
print(f"Memilih {Xterpilih.shape[1]} fitur dari {X.shape[1]}")
Rekayasa Fitur Otomatis dengan Featuretools
Featuretools mengotomatisasi pembuatan fitur dari dataset relasional menggunakan Deep Feature Synthesis (DFS).
import featuretools as ft
Buat entitas sampel (tabel)
pelanggan = pd.DataFrame({
'idpelanggan': range(1, 6),
'tanggaldaftar': pd.daterange('2024-01-01', periods=5, freq='30D'),
'negara': ['ID', 'SG', 'MY', 'ID', 'TH'],
})
transaksi = pd.DataFrame({
'idtransaksi': range(1, 21),
'idpelanggan': np.random.choice(range(1, 6), 20),
'jumlah': np.random.uniform(10, 500, 20).round(2),
'kategori': np.random.choice(['makanan', 'elektronik', 'pakaian', 'travel'], 20),
'tanggaltransaksi': pd.daterange('2024-06-01', periods=20, freq='3D'),
})
Buat EntitySet
es = ft.EntitySet(id='ecommerce')
es = es.adddataframe(
dataframename='pelanggan',
dataframe=pelanggan,
index='idpelanggan',
timeindex='tanggaldaftar',
)
es = es.adddataframe(
dataframename='transaksi',
dataframe=transaksi,
index='idtransaksi',
timeindex='tanggaltransaksi',
)
es = es.addrelationship('pelanggan', 'idpelanggan', 'transaksi', 'idpelanggan')
Jalankan Deep Feature Synthesis
matriksfitur, definisifitur = ft.dfs(
entityset=es,
targetdataframename='pelanggan',
aggprimitives=['mean', 'sum', 'max', 'min', 'count', 'std', 'numunique'],
transprimitives=['month', 'weekday', 'year'],
maxdepth=2,
)
print(f"Menghasilkan {len(definisifitur)} fitur secara otomatis")
print(matriksfitur.columns.tolist())
Membangun Pipeline Fitur Lengkap
Gabungkan semua teknik ke dalam pipeline sklearn yang dapat digunakan kembali:
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder, FunctionTransformer
Definisikan kelompok kolom
fiturnumerik = ['pendapatan', 'usia', 'jumlahtransaksi']
fiturkategorikal = ['kota', 'tipeperangkat']
Pipeline numerik
pipelinenumerik = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('logtransform', FunctionTransformer(np.log1p, validate=True)),
('scaler', StandardScaler()),
])
Pipeline kategorikal
pipelinekategorikal = Pipeline([
('imputer', SimpleImputer(strategy='constant', fillvalue='tidakdiketahui')),
('encoder', OneHotEncoder(handleunknown='ignore', sparseoutput=False)),
])
Preprocessor gabungan
preprocessor = ColumnTransformer([
('numerik', pipelinenumerik, fiturnumerik),
('kategorikal', pipelinekategorikal, fiturkategorikal),
])
Pipeline lengkap dengan model
from sklearn.ensemble import GradientBoostingClassifier
pipelinelengkap = Pipeline([
('preprocessor', preprocessor),
('classifier', GradientBoostingClassifier(nestimators=100, randomstate=42)),
])
Praktik Terbaik
Kesimpulan
Rekayasa fitur tetap menjadi pengungkit paling efektif untuk meningkatkan performa model machine learning. Dalam masterclass ini, Anda telah mempelajari:
- Transformasi numerik: penskalaan, transformasi log/pangkat, binning, fitur polinomial
- Encoding kategorikal: one-hot, target, frequency, binary, dan ordinal encoding
- Ekstraksi fitur datetime: encoding siklikal, agregasi temporal, flag bagian hari
- Fitur teks: TF-IDF, fitur statistik, dan embedding kalimat padat
- Seleksi fitur: metode filter, analisis korelasi, dan importance berbasis model
- Rekayasa fitur otomatis dengan Featuretools
Pelajaran utamanya adalah bahwa rekayasa fitur bukan aktivitas satu kali. Ini adalah proses iteratif yang didorong oleh pengetahuan domain, eksperimentasi, dan evaluasi berkelanjutan. Kuasai teknik-teknik ini, dan Anda akan secara konsisten membangun model yang mengungguli model yang hanya mengandalkan data mentah.