Setelah konstruksi fitur, jumlah kolom biasanya bertambah, dan tidak semuanya berguna: sebagian miskin sinyal, sebagian saling menyalin informasi. Feature selection memangkas kolom-kolom tersebut sebelum model dilatih. Chapter ini membahas keluarga pertama dari tiga keluarga metode: filter, yang menilai fitur langsung dari statistik data tanpa melibatkan model apa pun. Kita akan memakai satu dataset kampanye pemasaran sintetis — 120 pelanggan, 8 fitur kandidat, target biner “merespons kampanye atau tidak” — dan membiarkan metode filter membuktikan dirinya menemukan tiga fitur yang benar-benar memuat sinyal.

Mengapa Memilih Subset Fitur #

Empat alasan praktis menjawab pertanyaan ini. Pertama, training menjadi lebih cepat dan murah: biaya melatih banyak model tumbuh mengikuti jumlah fitur, dan seleksi dini memotong biaya itu untuk seluruh eksperimen berikutnya. Kedua, risiko overfitting turun: pada sampel terbatas, dimensi fitur yang besar memberi model ruang lebih luas untuk menghafal derau — gejala yang dibahas pada Chapter Overfitting dan Underfitting. Ketiga, model lebih mudah ditafsirkan: koefisien tiga fitur bisa dibaca dan dijelaskan kepada pemangku kepentingan; tiga ratus tidak. Keempat, biaya pengumpulan data turun: fitur yang terbukti tidak terpilih tidak perlu dihitung atau dibeli datanya di sistem produksi.

Tiga Kelompok Kriteria Filter #

Metode filter murah karena setiap fitur dinilai sekali lewat perhitungan statistik, hasilnya bisa dipakai untuk model apa pun, dan tidak ada latihan model yang berulang. Kriteria penilaiannya terbagi tiga kelompok: varians tiap fitur, korelasi antar fitur, dan uji statistik antara fitur dan label.

Menyaring dengan Ambang Varians #

Fitur yang nilainya nyaris tidak pernah berubah tidak punya daya pembeda: kolom yang 99% isinya sama tidak bisa memisahkan pelanggan yang merespons dari yang tidak. VarianceThreshold(threshold=0.01) membuang semua kolom dengan varians di bawah ambang tersebut. Perhatikan bahwa varians bukan ukuran informativitas — pada data contoh kita nanti, fitur informatif riwayat_pembelian justru punya varians terkecil — melainkan penyaring awal kolom mati.

Menyiang Redundansi dengan Korelasi #

Dua fitur yang berkorelasi tinggi membawa informasi hampir sama; mempertahankan keduanya menambah dimensi tanpa menambah sinyal, dan membuat koefisien model tidak stabil. Praktik lazimnya: bila korelasi Pearson |r| melewati sekitar 0,9, buang salah satu dari pasangan itu. Kandidat yang dipertahankan biasanya yang paling murah dikumpulkan atau paling mudah dijelaskan — pertimbangan operasional, bukan statistik.

Menguji Hubungan Fitur dengan Label #

Kelompok ketiga paling penting: mengukur seberapa kuat tiap fitur berhubungan dengan label. Tiga uji yang paling sering dipakai punya asumsi berbeda-beda:

MetodeAsumsi pada dataMenangkap
chi2Nilai non-negatif (hitungan)Ketergantungan fitur-label untuk data hitungan seperti frekuensi kata atau jumlah klik
f_classifNumerik, sebaran mendekati normalPerbedaan rata-rata antar kelas (hubungan linier)
mutual_info_classifNumerik maupun kategorikal, bebas bentuk sebaranHubungan apa pun dengan label, termasuk non-linier

Chi-squared wajar untuk matriks hitungan kata; ANOVA F murah dan cepat; mutual information paling bebas asumsi dan sanggup menemukan fitur yang berhubungan melengkung dengan label — dengan tarif komputasi yang sedikit lebih tinggi.

SelectKBest: Membungkus Skor menjadi Selektor #

Uji statistik menghasilkan skor per fitur; SelectKBest mengubah skor itu menjadi selektor konkret dengan mempertahankan k fitur berskor tertinggi. Pilihan k bisa diganti pendekatan persentil lewat SelectPercentile (misalnya menyisakan 50% teratas) ketika jumlah fiturnya ratusan dan nilai k yang pas belum diketahui. Skor mentahnya tetap tersedia di atribut scores_ untuk dibaca sebelum memutuskan. Satu detail teknis: mutual_info_classif memakai keacakan internal, sehingga saat dibungkus sebagai score_func nilainya perlu dibekukan dengan partial(mutual_info_classif, random_state=42) agar hasil seleksi deterministik.

Contoh: Dataset Kampanye 120 Pelanggan #

Dataset contoh dibangun make_classification dengan delapan fitur, tiga di antaranya informatif dan lima sisanya derau murni. Parameter shuffle=False dipilih agar kolom informatif pasti berada di indeks 0–2 — kebenaran yang kita ketahui, tetapi belum diketahui metode seleksinya — sehingga keberhasilan penemuan bisa diverifikasi jujur. Nama fitur diberi rasa kampanye pemasaran (kunjungan_mingguan, durasi_kunjungan, riwayat_pembelian, usia, kode_area, skor_kredit, jumlah_perangkat, tayangan_iklan) walau nilainya sintetis.

python
# Seleksi fitur keluarga filter: varians, mutual information, dan korelasi.
# Dataset sintetis kampanye pemasaran: 120 pelanggan, 8 fitur, 3 di antaranya
# sengaja dibuat informatif oleh generator (kolom indeks 0-2).

from functools import partial

import numpy as np
from sklearn.datasets import make_classification
from sklearn.feature_selection import SelectKBest, VarianceThreshold, mutual_info_classif

X, y = make_classification(
    n_samples=120,
    n_features=8,
    n_informative=3,
    n_redundant=0,  # tanpa kolom redundan bawaan; redundansi didemokan manual
    n_clusters_per_class=1,
    class_sep=1.5,
    shuffle=False,  # agar kolom informatif pasti berada di indeks 0-2
    random_state=42,
)

nama_fitur = [
    "kunjungan_mingguan",  # indeks 0: informatif
    "durasi_kunjungan",    # indeks 1: informatif
    "riwayat_pembelian",   # indeks 2: informatif
    "usia",                # indeks 3: derau
    "kode_area",           # indeks 4: derau
    "skor_kredit",         # indeks 5: derau
    "jumlah_perangkat",    # indeks 6: derau
    "tayangan_iklan",      # indeks 7: derau
]
informatif_sejati = ["kunjungan_mingguan", "durasi_kunjungan", "riwayat_pembelian"]

# --- (a) Varians tiap fitur, lalu VarianceThreshold pada kolom nyaris konstan ---
print("(a) Varians tiap fitur:")
for nama, varians in zip(nama_fitur, X.var(axis=0)):
    print(f"  {nama:<20}{varians:6.3f}")

rng = np.random.default_rng(42)
kolom_konstan = 5.0 + rng.normal(0.0, 0.01, size=120)  # nilai nyaris tak berubah
X_dengan_konstan = np.column_stack([X, kolom_konstan])
nama_lengkap = nama_fitur + ["fitur_konstan"]

penyaring_var = VarianceThreshold(threshold=0.01)
X_bersih = penyaring_var.fit_transform(X_dengan_konstan)
dihapus = [n for n, s in zip(nama_lengkap, penyaring_var.get_support()) if not s]
print(f"\nVarianceThreshold(0.01): {X_dengan_konstan.shape[1]} kolom -> {X_bersih.shape[1]} kolom")
print(f"Kolom yang dibuang: {', '.join(dihapus)}")

# --- (b) Skor mutual information tiap fitur terhadap label ---
skor_mi = mutual_info_classif(X, y, random_state=42)
urutan = np.argsort(skor_mi)[::-1]
print("\n(b) Skor mutual information (diurutkan menurun):")
for indeks in urutan:
    print(f"  {nama_fitur[indeks]:<20}{skor_mi[indeks]:6.3f}")
tiga_teratas = sorted(nama_fitur[i] for i in urutan[:3])
print(f"Tiga skor tertinggi : {', '.join(tiga_teratas)}")
print(f"Informatif sejati   : {', '.join(sorted(informatif_sejati))}")
if tiga_teratas == sorted(informatif_sejati):
    print("Kesimpulan: tiga peringkat teratas cocok seluruhnya dengan informatif sejati.")

# --- (c) SelectKBest membungkus skor mutual information ---
# random_state di-bekukan lewat partial agar skor deterministik saat dipanggil ulang.
pemilih = SelectKBest(score_func=partial(mutual_info_classif, random_state=42), k=3)
pemilih.fit(X, y)
terpilih = [n for n, s in zip(nama_fitur, pemilih.get_support()) if s]
print(f"\n(c) Fitur terpilih SelectKBest(k=3): {', '.join(terpilih)}")

# --- (d) Redundansi: dua fitur berkorelasi tinggi ---
pendapatan_juta = rng.normal(12.0, 2.5, size=120)
pengeluaran_juta = 0.8 * pendapatan_juta + rng.normal(0.0, 0.6, size=120)
matriks_r = np.corrcoef(pendapatan_juta, pengeluaran_juta)
print("\n(d) Matriks korelasi dua fitur kandidat:")
print(f"  {'':<14}{'pendapatan':>10}{'pengeluaran':>13}")
for nama_baris, baris in zip(["pendapatan", "pengeluaran"], matriks_r):
    print(f"  {nama_baris:<14}{baris[0]:>10.3f}{baris[1]:>13.3f}")
r = matriks_r[0, 1]
print(f"Korelasi r = {r:.3f}; |r| > 0.90 -> salah satu kolom dibuang (redundan)")

Output yang Dihasilkan:

text
(a) Varians tiap fitur:
  kunjungan_mingguan   3.998
  durasi_kunjungan     1.385
  riwayat_pembelian    0.737
  usia                 1.021
  kode_area            0.932
  skor_kredit          1.035
  jumlah_perangkat     0.994
  tayangan_iklan       0.934

VarianceThreshold(0.01): 9 kolom -> 8 kolom
Kolom yang dibuang: fitur_konstan

(b) Skor mutual information (diurutkan menurun):
  kunjungan_mingguan   0.438
  riwayat_pembelian    0.281
  durasi_kunjungan     0.079
  kode_area            0.057
  skor_kredit          0.052
  tayangan_iklan       0.038
  jumlah_perangkat     0.037
  usia                 0.012
Tiga skor tertinggi : durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian
Informatif sejati   : durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian
Kesimpulan: tiga peringkat teratas cocok seluruhnya dengan informatif sejati.

(c) Fitur terpilih SelectKBest(k=3): kunjungan_mingguan, durasi_kunjungan, riwayat_pembelian

(d) Matriks korelasi dua fitur kandidat:
                pendapatan  pengeluaran
  pendapatan         1.000        0.955
  pengeluaran        0.955        1.000
Korelasi r = 0.955; |r| > 0.90 -> salah satu kolom dibuang (redundan)

Mari kita telaah bagian-bagian utama skrip di atas:

  1. Baris 11–20 (X, y = make_classification(...)): dataset 120 baris × 8 kolom dibangun dengan random_state=42; shuffle=False dan n_redundant=0 menjadikan indeks 0–2 satu-satunya pembawa sinyal.
  2. Baris 22–32 (nama_fitur = ...): nama domain dipetakan ke tiap kolom, dan daftar informatif_sejati mencatat kebenaran dari generator — pembanding bagi hasil metode.
  3. Baris 34–48: varians tiap fitur dicetak, lalu VarianceThreshold didemokan dengan satu kolom tambahan fitur_konstan yang nilainya bergoyang sangat kecil di sekitar 5,0.
  4. Baris 50–60 (skor_mi = mutual_info_classif(...)): skor mutual information dihitung dengan random_state=42, diurutkan menurun, lalu dibandingkan dengan daftar informatif sejati.
  5. Baris 62–67 (pemilih = SelectKBest(...)): selektor dengan k=3 di-fit, dan get_support() menandai kolom mana yang lolos.
  6. Baris 69–78: dua fitur buatan — pendapatan dan pengeluaran yang merupakan kombinasi linier plus derau — dihitung korelasinya sebagai contoh redundansi.

Membaca Hasil: Skor Menyingkap Struktur Data #

Bacaan output bagian (a) memberi pelajaran pertama: varians tidak mengenali informativitas. Fitur kunjungan_mingguan memang bervarians terbesar (3,998), tetapi riwayat_pembelian yang informatif justru bervarians terkecil (0,737) — sementara usia yang murni derau bervarians 1,021. VarianceThreshold kemudian menunjukkan fungsi aslinya: dari 9 kolom (8 fitur + fitur_konstan), hanya kolom buatan itu yang dibuang.

Bagian (b) adalah momen pembuktian. Tiga skor mutual information tertinggi jatuh pada kunjungan_mingguan (0,438), riwayat_pembelian (0,281), dan durasi_kunjungan (0,079) — tepat ketiga fitur informatif sejati, tanpa metode ini pernah diberi tahu. Lima fitur derau tertinggal di bawah 0,06. Perhatikan bahwa durasi_kunjungan informatif namun sinyalnya paling tipis (0,079, hanya sedikit di atas derau tertinggi 0,057) — batas antara “informatif lemah” dan “derau” pada data nyata memang tidak selalu lebar, dan itulah mengapa k yang terlalu kecil berisiko memangkas fitur yang masih berguna. Bagian (c) menegaskan hal yang sama dari sisi selektor: SelectKBest mempertahankan tepat ketiga fitur itu.

Bagian (d) memperlihatkan redundansi dalam angka: korelasi pendapatan–pengeluaran mencapai 0,955, jauh di atas ambang praktis 0,9. Salah satu kolom layak dibuang, dan pilihan jatuh pada pendapatan bila pengeluaran lebih murah dikumpulkan langsung dari sistem pembayaran — keputusan operasional di atas keputusan statistik.

Kekuatan filter sekaligus batasnya terlihat jelas: seluruh penilaian dilakukan per fitur secara terpisah, tanpa pernah bertanya bagaimana model justru memakai kombinasi fitur. Chapter berikutnya, Feature Selection dengan RFE, menyerahkan jabatan juri kepada model itu sendiri.

Referensi Resmi #