Notebook Bab 4 - Representasi Fitur Kategorikal

Open In Colab

Notebook Bab 4 ini punya dua bagian. Bagian Demo tinggal Anda jalankan lalu amati keluarannya; bagian Mini Project berisi soal dan data yang Anda kerjakan sendiri.

Fitur kategorikal harus diubah menjadi angka. Pilihan encoding menentukan hasil, terutama pada kategori berkardinalitas tinggi.

Persiapan

import numpy as np
import pandas as pd
from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, TargetEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score

RANDOM_STATE = 42
rng = np.random.default_rng(RANDOM_STATE)
print('Setup selesai.')
Setup selesai.

Section 1 - Demo: Perbandingan Encoding pada Kategori Berkardinalitas Tinggi

Data: kolom kota dengan 500 nilai

Kardinalitas tinggi dengan sedikit sampel per kota (sekitar 6). Tiap kota punya efek kuat terhadap target, jadi identitas kota membawa sinyal.

n = 3000
kota_ids = [f'kota_{i}' for i in range(500)]
efek = dict(zip(kota_ids, rng.normal(0, 2.5, len(kota_ids))))
kota = rng.choice(kota_ids, n)
x_num = rng.normal(0, 1, n)

logit = np.array([efek[k] for k in kota]) + 0.3 * x_num
y = (rng.random(n) < 1 / (1 + np.exp(-logit))).astype(int)
df = pd.DataFrame({'kota': kota, 'x_num': x_num})
print('Data:', df.shape, '| kardinalitas kota:', df['kota'].nunique())
df.head()
Data: (3000, 2) | kardinalitas kota: 498
kota x_num
0 kota_207 -1.893171
1 kota_186 0.217273
2 kota_354 1.575083
3 kota_76 0.488669
4 kota_119 0.170625

Tiga strategi encoding

Semua encoder dibungkus pipeline. TargetEncoder scikit-learn aman dari leakage karena melakukan cross-fitting internal saat fit.

def eval_enc(enc):
    ct = ColumnTransformer([('kota', enc, ['kota'])], remainder='passthrough')
    pipe = Pipeline([('enc', ct), ('lr', LogisticRegression(max_iter=1000))])
    return cross_val_score(pipe, df, y, cv=5, scoring='accuracy').mean()

skor = {
    'One-Hot': eval_enc(OneHotEncoder(handle_unknown='ignore')),
    'Ordinal': eval_enc(OrdinalEncoder(handle_unknown='use_encoded_value', unknown_value=-1)),
    'Target': eval_enc(TargetEncoder(random_state=RANDOM_STATE)),
}
for k, v in skor.items():
    print(f'{k:9s} akurasi CV = {v:.3f}')
One-Hot   akurasi CV = 0.780
Ordinal   akurasi CV = 0.541
Target    akurasi CV = 0.781

🔎 Amati. Dengan akurasi yang setara, target encoding memakai satu kolom sedangkan one-hot memakai ~498 kolom. Pada kardinalitas tinggi itu penghematan dimensi dan memori yang besar sekaligus lebih skalabel. One-hot masih kompetitif di sini karena regresi logistik ter-regularisasi, tetapi ordinal jelas terburuk karena memaksakan urutan angka palsu pada kategori nominal yang tidak berurutan.

Section 2 - Mini Project

Soal

Anda diberi data dengan kolom kategorikal berkardinalitas tinggi (merek_produk) dan satu kolom numerik (harga). Targetnya terjual (1/0).

Tugas:

  1. Bandingkan minimal dua strategi encoding pada merek_produk (misalnya one-hot dan target encoding), semuanya di dalam pipeline.
  2. Tangani kategori yang belum pernah muncul saat inferensi (unseen category).
  3. Laporkan akurasi CV tiap strategi.

Luaran: kode pipeline, tabel akurasi, dan 2-3 kalimat kesimpulan pilihan encoding.

Kriteria penilaian: (a) encoder di dalam pipeline (tidak di-fit pada seluruh data); (b) penanganan unseen category eksplisit; (c) perbandingan adil (model sama).

# DATA AWAL (jangan diubah) - merek berkardinalitas tinggi.
merek_ids = [f'merek_{i}' for i in range(300)]
efek_merek = dict(zip(merek_ids, rng.normal(0, 2.2, len(merek_ids))))
merek = rng.choice(merek_ids, 3000)
harga = rng.gamma(2.0, 50000, 3000)
logit = np.array([efek_merek[m] for m in merek]) - 0.000004 * harga
terjual = (rng.random(3000) < 1 / (1 + np.exp(-logit))).astype(int)
produk = pd.DataFrame({'merek_produk': merek, 'harga': harga, 'terjual': terjual})
print('Data:', produk.shape, '| kardinalitas merek:', produk['merek_produk'].nunique())
produk.head()
Data: (3000, 3) | kardinalitas merek: 300
merek_produk harga terjual
0 merek_206 445441.984515 0
1 merek_279 52423.000250 1
2 merek_292 139674.416744 0
3 merek_247 206692.312882 1
4 merek_89 77615.890626 1
# Kerjakan di sini.
# Petunjuk: ColumnTransformer + TargetEncoder / OneHotEncoder(handle_unknown='ignore').