Agregasi pada chapter sebelumnya memperkaya tabel kita dengan banyak kolom baru — dan di situlah persoalan baru bermula: semakin banyak fitur, semakin besar peluang dua di antaranya mengukur hal yang sama. Jumlah cangkir yang terjual dan pendapatan harian, misalnya, hampir selalu bergerak seiring karena pendapatan memang berasal dari cangkir. Dua kolom yang bergerak bersama seperti itu tidak membawa informasi baru, tetapi menimbulkan multikolinearitas — kondisi yang membuat koefisien model linear sulit ditafsirkan dan mudah menyesatkan. Pada chapter ini kita belajar menemukan pasangan fitur berkorelasi kuat lewat matriks korelasi, lalu merawatnya dengan dua strategi: membuang salah satu anggota pasangan atau menggabungkan keduanya menjadi rasio.
Ketika Dua Fitur Mengukur Hal yang Sama #
Multikolinearitas terjadi ketika dua fitur atau lebih berkorelasi kuat satu sama lain, sehingga model tidak sanggup memisahkan kontribusi masing-masing fitur. Ibarat dua orang bercerita dengan kalimat yang hampir sama, pendengar tahu informasinya penting, tetapi tidak bisa menilai siapa yang sebenarnya menyampaikannya — dan batas ibaratnya ada di situ: komputer tidak menimbang cerita, ia hanya menerima angka. Model linear menghadapi dilema yang persis sama: ketika cangkir dan pendapatan_ribu naik bersamaan, kenaikan penjualan makanan bisa diatribusikan ke salah satunya dengan hasil prediksi yang sama baiknya. Prediksi tetap aman; yang rusak adalah tafsiran koefisiennya.
Kita pakai data dua belas cabang Toko Kopi Kita. Tiga kolom menjadi fitur — jumlah cangkir terjual per hari, pendapatan harian dalam ribuan rupiah, dan jumlah kursi — sedangkan porsi makanan terjual menjadi target yang ingin kita jelaskan. Sebelum menyentuh model, kita periksa dulu korelasi antar kolomnya:
import pandas as pd
cabang = pd.DataFrame({
"cangkir": [120, 95, 150, 80, 200, 175, 110, 140, 90, 160, 130, 105],
"pendapatan_ribu": [2824, 2228, 3551, 1872, 4713, 4051, 2593, 3332, 2106, 3759, 3053, 2459],
"kursi": [23, 22, 20, 18, 29, 35, 21, 18, 22, 24, 26, 23],
"makanan_porsi": [49, 38, 56, 32, 79, 79, 45, 47, 39, 62, 54, 43],
})
print(cabang.head(5).to_string(index=False))
print()
print(cabang.corr().round(2))Output yang Dihasilkan:
cangkir pendapatan_ribu kursi makanan_porsi
120 2824 23 49
95 2228 22 38
150 3551 20 56
80 1872 18 32
200 4713 29 79
cangkir pendapatan_ribu kursi makanan_porsi
cangkir 1.00 1.00 0.65 0.96
pendapatan_ribu 1.00 1.00 0.62 0.95
kursi 0.65 0.62 1.00 0.82
makanan_porsi 0.96 0.95 0.82 1.00Mari kita bedah baris demi baris:
- Baris 3-8 (
cabang = pd.DataFrame(...)): dua belas cabang dengan tiga fitur dan satu target. Perhatikan kolompendapatan_ribu: nilainya selalu bermain di sekitar 23 sampai 24 kalicangkir— sekadar harga secangkir kopi dikalikan jumlahnya — sehingga kedua kolom nyaris saling menyalin. - Baris 10 (
cabang.corr().round(2)):corr()menghitung korelasi Pearson untuk setiap pasangan kolom numerik, danround(2)menjaga tabelnya mudah dibaca. - Pembacaan matriks:
cangkirdanpendapatan_ribuberkorelasi 1,00 — tumpang tindih nyaris sempurna. Sebaliknya,kursihanya berkorelasi 0,65 dan 0,62 dengan keduanya, jadi masih membawa informasi tersendiri. Barismakanan_porsimemang berkorelasi tinggi dengan hampir semua fitur, dan itu bukan masalah — korelasi dengan target justru yang kita cari.
Menyaring Pasangan dengan Ambang 0,9 #
Matriks korelasi empat kolom masih mudah dibaca mata; matriks empat puluh kolom jelas tidak. Kita membutuhkan cara terprogram: susun semua pasangan menjadi tabel panjang, pastikan tiap pasangan hanya muncul sekali, lalu saring yang korelasinya melewati ambang. Ambang 0,9 adalah titik awal yang lazim — di atasnya, dua fitur praktis saling menyalin dan salah satu layak dikeluarkan.
import numpy as np
import pandas as pd
cabang = pd.DataFrame({
"cangkir": [120, 95, 150, 80, 200, 175, 110, 140, 90, 160, 130, 105],
"pendapatan_ribu": [2824, 2228, 3551, 1872, 4713, 4051, 2593, 3332, 2106, 3759, 3053, 2459],
"kursi": [23, 22, 20, 18, 29, 35, 21, 18, 22, 24, 26, 23],
"makanan_porsi": [49, 38, 56, 32, 79, 79, 45, 47, 39, 62, 54, 43],
})
matriks_fitur = cabang[["cangkir", "pendapatan_ribu", "kursi"]].corr()
segitiga_atas = np.triu(np.ones(matriks_fitur.shape), k=1).astype(bool)
pasangan = matriks_fitur.where(segitiga_atas).stack().reset_index()
pasangan.columns = ["fitur_1", "fitur_2", "korelasi"]
print(pasangan.to_string(index=False))
print()
ambang = 0.9
print(pasangan[pasangan["korelasi"].abs() > ambang].to_string(index=False))Output yang Dihasilkan:
fitur_1 fitur_2 korelasi
cangkir pendapatan_ribu 0.999589
cangkir kursi 0.645169
pendapatan_ribu kursi 0.624760
fitur_1 fitur_2 korelasi
cangkir pendapatan_ribu 0.999589Pekerjaan tiap barisnya:
- Baris 5 (
cabang[[...]].corr()): matriks korelasi dihitung hanya dari tiga fitur — target sengaja dikeluarkan karena ambang berlaku untuk korelasi antar fitur, bukan korelasi dengan target. - Baris 6 (
np.triu(np.ones(...), k=1)):np.triumenandai segitiga atas matriks;k=1membuang diagonalnya sehingga pasangan A-B terhitung satu kali, tanpa kembarannya B-A dan tanpa pasangan A-A. - Baris 7-9 (
where(...).stack().reset_index()):wheremengosongkan sel di luar segitiga atas,stack()melipat matriks menjadi tabel panjang tiga kolom, danreset_index()merapikan nama kolomnya. - Baris 12-13 (penyaringan
abs() > ambang): hanyacangkir–pendapatan_ribuyang lolos ambang, dengan r = 0.999589. Dua pasangan lain aman karena berada di bawah 0,7.
Multikolinearitas Mengaburkan Koefisien #
Apa sebenarnya bahayanya jika kedua fitur tetap kita masukkan? Untuk menjawabnya, kita latih dua model regresi linear dengan target makanan_porsi: model pertama memakai semua fitur, model kedua membuang pendapatan_ribu. Bila multikolinearitas tidak berefek, koefisien kedua model seharusnya tidak jauh berbeda.
import pandas as pd
from sklearn.linear_model import LinearRegression
cabang = pd.DataFrame({
"cangkir": [120, 95, 150, 80, 200, 175, 110, 140, 90, 160, 130, 105],
"pendapatan_ribu": [2824, 2228, 3551, 1872, 4713, 4051, 2593, 3332, 2106, 3759, 3053, 2459],
"kursi": [23, 22, 20, 18, 29, 35, 21, 18, 22, 24, 26, 23],
"makanan_porsi": [49, 38, 56, 32, 79, 79, 45, 47, 39, 62, 54, 43],
})
X = cabang[["cangkir", "pendapatan_ribu", "kursi"]]
y = cabang["makanan_porsi"]
model_penuh = LinearRegression().fit(X, y)
model_ringkas = LinearRegression().fit(X[["cangkir", "kursi"]], y)
koef_penuh = pd.Series(model_penuh.coef_, index=X.columns, name="semua_fitur")
koef_ringkas = pd.Series(model_ringkas.coef_, index=["cangkir", "kursi"],
name="tanpa_pendapatan")
print(pd.concat([koef_penuh, koef_ringkas], axis=1).round(3))
print()
print("R2 semua fitur :", round(model_penuh.score(X, y), 3))
print("R2 tanpa pendapatan:", round(model_ringkas.score(X[["cangkir", "kursi"]], y), 3))Output yang Dihasilkan:
semua_fitur tanpa_pendapatan
cangkir 1.703 0.302
pendapatan_ribu -0.058 NaN
kursi 0.746 1.107
R2 semua fitur : 0.994
R2 tanpa pendapatan: 0.993Mari kita baca hasilnya:
- Baris 6-7 (
X,y):Xmemuat tiga fitur danyadalah target. Konvensi huruf besar-kecil ini mengikuti kebiasaan scikit-learn. - Baris 9-10 (dua
LinearRegression().fit(...)): model penuh belajar dari tiga fitur; model ringkas hanya daricangkirdankursi. - Baris 12-16 (tabel koefisien): koefisien kedua model disusun berdampingan lewat
pd.concat; selNaNmenandakan fitur yang tidak dipakai oleh model kedua. - Pembacaan koefisien: koefisien
cangkirmelompat dari 1.703 menjadi 0.302 hanya karena satu kolom dibuang. Lebih mencolok lagi, koefisienpendapatan_ribubernilai -0.058 padahal korelasinya dengan target +0,95 — model penuh seolah berkata bahwa menambah pendapatan justru menurunkan penjualan makanan, tafsiran yang jelas keliru. Padahal R2 kedua model nyaris sama: masalahnya bukan kemampuan prediksi, melainkan interpretasi.
Rasio: Menyimpan Informasi tanpa Menyalin #
Membuang salah satu anggota pasangan adalah obat paling sederhana, tetapi bukan satu-satunya. Dua fitur yang berkorelasi kadang bisa digabungkan menjadi satu fitur baru yang memuat informasi berbeda: pendapatan dibagi jumlah cangkir menghasilkan harga rata-rata per cangkir — ukuran strategi harga yang nyaris bebas dari ramai-sepi cabang.
import pandas as pd
cabang = pd.DataFrame({
"cangkir": [120, 95, 150, 80, 200, 175, 110, 140, 90, 160, 130, 105],
"pendapatan_ribu": [2824, 2228, 3551, 1872, 4713, 4051, 2593, 3332, 2106, 3759, 3053, 2459],
"kursi": [23, 22, 20, 18, 29, 35, 21, 18, 22, 24, 26, 23],
"makanan_porsi": [49, 38, 56, 32, 79, 79, 45, 47, 39, 62, 54, 43],
})
cabang["harga_rata"] = (cabang["pendapatan_ribu"] / cabang["cangkir"]).round(2)
print(cabang[["cangkir", "pendapatan_ribu", "harga_rata"]].head(4).to_string(index=False))
print()
print(cabang[["cangkir", "harga_rata", "kursi"]].corr().round(2))Output yang Dihasilkan:
cangkir pendapatan_ribu harga_rata
120 2824 23.53
95 2228 23.45
150 3551 23.67
80 1872 23.40
cangkir harga_rata kursi
cangkir 1.00 0.09 0.65
harga_rata 0.09 1.00 -0.64
kursi 0.65 -0.64 1.00Yang terjadi pada kode di atas:
- Baris 5 (
cabang["harga_rata"] = ...): pembagian dua kolom dijalankan baris demi baris, menghasilkan harga rata-rata tiap cabang;round(2)menjaga tampilannya rapi. - Baris 8-9: matriks korelasi dihitung ulang dengan kehadiran kolom baru.
- Pembacaan hasil: korelasi
harga_ratadengancangkirhanya 0,09. Dari pasangan yang tadinya nyaris kembar, kita kini memegang dua informasi mandiri: lalu lintas (cangkir) dan harga (harga_rata). Kedua kolom lama pun bisa digantikan olehcangkir+harga_ratatanpa kehilangan apa pun.
Dua strategi di atas bukan pilihan satu-satunya. Tabel berikut merangkum empat jalan yang umum ditempuh:
| Strategi | Cara kerja | Kapan tepat |
|---|---|---|
| Buang salah satu | drop satu kolom dari pasangan terkuat | kedua fitur mengukur konstruk yang sama |
| Bentuk rasio | bagi atau gabungkan menjadi fitur baru | kombinasinya punya makna bisnis, misal harga |
| Reduksi dimensi | padatkan banyak fitur menjadi komponen | banyak fitur saling tumpang tindih sekaligus |
| Biarkan saja | tidak diubah | fokus prediksi dengan model pohon, tanpa perlu menafsirkan koefisien |
Jalan ketiga, reduksi dimensi, kita bahas tuntas pada chapter khusus di ujung section ini. Untuk sekarang, dua strategi pertama sudah cukup untuk merapikan kebanyakan tabel.
Rangkuman #
- Multikolinearitas muncul ketika fitur saling berkorelasi kuat; prediksi biasanya tetap baik, tetapi koefisien model linear menjadi tidak stabil dan mudah menyesatkan.
df.corr()memberi matriks korelasi; pasangan dengan |r| di atas ambang (misalnya 0,9) ditemukan terprogram lewatnp.triu+stack(), dengan target dikeluarkan dari matriks.- Pada contoh kita, koefisien
cangkirbergeser dari 1.703 ke 0.302 dan koefisienpendapatan_ribuberubah menjadi negatif padahal korelasinya dengan target positif — bukti nyata koefisien tak bisa dipercaya saat fitur saling menyalin. - Dua perawatan utama: buang salah satu anggota pasangan, atau bentuk rasio (
pendapatan / cangkir) yang menghasilkan informasi baru nyaris bebas dari fitur aslinya.
Masalah pasangan berkorelasi sesungguhnya adalah kasus khusus dari pertanyaan yang lebih luas: dari sekian banyak kolom, mana yang benar-benar layak masuk model? Pertanyaan itu kita jawab dengan feature selection pada chapter berikutnya.