Model scikit-learn hanya membaca angka, sementara data nyata penuh kolom teks: kota, jenjang pendidikan, jenis kelamin, nama produk. Encoding adalah proses mengubah kolom-kolom tersebut menjadi representasi numerik — dan pilihan metodenya menentukan apakah model membaca informasi yang benar atau justru informasi palsu. Kesalahan paling umum berbunyi “kodekan saja semua kategori dengan 0, 1, 2, 3”: pada kolom kota, angka tersebut menanam urutan yang tidak ada, sehingga model mempelajari bahwa Medan “lebih besar” dari Bandung. Chapter ini memetakan metode encoding ke jenis datanya — OrdinalEncoder untuk kategori berurutan, OneHotEncoder untuk kategori nominal, LabelEncoder khusus target — lalu menutup dengan strategi menghadapi kardinalitas tinggi.
Dua Jenis Data Kategorikal #
Pembedaan pertama yang menentukan segalanya: apakah kategorinya memiliki urutan yang bermakna. Data ordinal memiliki urutan alami — jenjang pendidikan SD, SMA, S1; rating kecil, sedang, besar; kelas tiket ekonomi, bisnis, first. Pada data semacam ini, jarak antar kode boleh dianggap bertingkat, sehingga pemetaan ke 0, 1, 2 justru informatif. Data nominal tidak memiliki urutan apa pun — kota, warna, merek, departemen. Memberi kode angka berurutan pada data nominal sama dengan menanam informasi palsu: model linier akan menghitung “efek rata-rata naik setiap satu tingkat kode”, dan model jarak akan menganggap dua kategori tertentu lebih berdekatan daripada yang lain. Satu kolom kategorikal bisa diurutkan atau tidak, dan tidak ada rumus yang bisa memutuskannya — kembali ke pemahaman domain sebagaimana pada penanganan pencilan.
OrdinalEncoder dengan Daftar Kategori Eksplisit #
OrdinalEncoder menerjemahkan kategori berurutan menjadi angka bertingkat. Argumen categories wajib diisi daftar kategori dalam urutan yang benar — misalnya [["SD", "SMA", "S1"]] menghasilkan pemetaan SD→0, SMA→1, S1→2. Tanpa argumen tersebut, encoder mengurutkan kategori secara alfabetis: S1→0, SD→1, SMA→2 — urutan yang keliru total untuk jenjang pendidikan namun tidak menyampaikan galat apa pun. Menuliskan urutan secara eksplisit juga berfungsi sebagai dokumentasi keputusan domain yang bisa dibaca peneriksa berikutnya.
OneHotEncoder untuk Data Nominal #
OneHotEncoder menghindari urutan palsu dengan cara mengubah satu kolom menjadi beberapa kolom biner: kolom kota dengan empat nilai menjadi empat kolom kota_Bandung, kota_Jakarta, kota_Medan, kota_Surabaya, dan tepat satu kolom bernilai 1 pada setiap baris. Tidak ada jarak antar kategori yang diklaim, sehingga model linier maupun jarak bekerja tanpa asumsi palsu. Dua detail praktis: argumen sparse_output=False menghasilkan matriks padat yang nyaman untuk dataset kecil (representasi sparse jauh lebih hemat memori ketika jumlah kategori ratusan), dan handle_unknown="ignore" membuat data baru dengan kategori yang tidak pernah dilihat saat pelatihan — kota Balikpapan pada contoh kita — dipetakan menjadi baris nol semua, bukan menggagalkan program dengan galat. Nama kolom hasil dapat diambil lewat get_feature_names_out() agar hasil transformasi tetap bisa dilacak sumbernya.
LabelEncoder Hanya untuk Target #
LabelEncoder memetakan kelas target menjadi angka: berhenti→0, bertahan→1. Kesalahan klasiknya adalah memakai transformer ini untuk fitur — padahal LabelEncoder dirancang khusus untuk kolom target y yang berupa satu label per baris. Selain menanam urutan palsu sama seperti kode angka berurutan, LabelEncoder juga tidak punya mekanisme penanganan kategori baru yang aman untuk fitur. Untuk fitur, gunakan OrdinalEncoder (berurutan) atau OneHotEncoder (nominal); simpan LabelEncoder untuk target.
Sebagai pembanding, pandas menyediakan pd.get_dummies() yang menghasilkan one-hot encoding dalam satu baris kode dan menyatu dengan alur DataFrame. Fungsi tersebut praktis untuk eksplorasi, tetapi tidak mengikuti kontrak fit/transform scikit-learn: pemetaannya tidak tersimpan sebagai objek yang bisa diterapkan ulang pada data baru dengan handle_unknown="ignore". Ketika encoding harus masuk ke dalam pipeline produksi, OneHotEncoder adalah pilihan yang tepat.
| Metode | Jenis data | Catatan |
|---|---|---|
OrdinalEncoder(categories=[...]) | Ordinal (berurutan) | Urutan wajib eksplisit; alfabetis bila kosong |
OneHotEncoder | Nominal | handle_unknown="ignore" untuk kategori baru; sparse untuk dimensi besar |
LabelEncoder | Target y saja | Bukan untuk fitur — menanam urutan palsu |
pd.get_dummies | Nominal | Praktis untuk eksplorasi; tidak masuk pipeline fit/transform |
| Frequency encoding | Nominal kardinalitas tinggi | Kategori dipetakan ke proporsi kemunculannya |
Contoh Kode: Empat Metode Encoding #
Contoh berikut memakai delapan pelanggan dengan kolom kota (nominal, empat nilai), pendidikan (ordinal), dan target churn, lalu mendemonstrasikan keempat metode — termasuk nasib pelanggan baru dari kota yang tak pernah dilihat encoder.
# Encoding variabel kategorikal: OrdinalEncoder untuk data berurutan,
# OneHotEncoder untuk data nominal, LabelEncoder khusus kolom target,
# dan frequency encoding untuk kardinalitas tinggi.
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder, OneHotEncoder, OrdinalEncoder
rng = np.random.default_rng(42)
n = 8
kota = rng.choice(["Jakarta", "Bandung", "Surabaya", "Medan"], size=n)
pendidikan = rng.choice(["SD", "SMA", "S1"], size=n)
churn = rng.integers(0, 2, size=n)
df = pd.DataFrame({"kota": kota, "pendidikan": pendidikan, "churn": churn})
print("== Data asal ==")
print(df.to_string())
# --- 1) OrdinalEncoder: urutan eksplisit SD < SMA < S1 ---
encoder_ordinal = OrdinalEncoder(categories=[["SD", "SMA", "S1"]])
kode_pendidikan = encoder_ordinal.fit_transform(df[["pendidikan"]])
df_ordinal = df.assign(pendidikan_kode=kode_pendidikan.ravel().astype(int))
print("\n== 1) OrdinalEncoder dengan urutan eksplisit ==")
print(df_ordinal[["pendidikan", "pendidikan_kode"]].to_string())
# --- 2) OneHotEncoder: kota nominal, kategori tak dikenal diabaikan ---
encoder_onehot = OneHotEncoder(handle_unknown="ignore", sparse_output=False)
matriks_kota = encoder_onehot.fit_transform(df[["kota"]])
nama_kolom = encoder_onehot.get_feature_names_out()
df_onehot = pd.DataFrame(matriks_kota, columns=nama_kolom, index=df.index)
print("\n== 2) OneHotEncoder pada kolom kota ==")
print("kolom hasil :", nama_kolom.tolist())
print(df_onehot.to_string())
pelanggan_baru = pd.DataFrame({"kota": ["Balikpapan"]})
kode_baru = encoder_onehot.transform(pelanggan_baru)
print("\nkota tak dikenal 'Balikpapan' ->", kode_baru.ravel().astype(int).tolist())
# --- 3) LabelEncoder: KHUSUS kolom target ---
status = np.where(df["churn"] == 1, "berhenti", "bertahan")
encoder_label = LabelEncoder()
y = encoder_label.fit_transform(status)
pemetaan_kelas = {
kelas: int(kode)
for kelas, kode in zip(
encoder_label.classes_.tolist(),
range(len(encoder_label.classes_)),
)
}
print("\n== 3) LabelEncoder pada kolom target ==")
print("pemetaan kelas :", pemetaan_kelas)
print("target teks :", status[:4].tolist())
print("target angka :", y[:4].tolist())
# --- 4) Kardinalitas tinggi: frequency encoding ---
# Skenario kardinalitas tinggi: 10 kategori kelurahan pada hanya 30 baris.
nama_kelurahan = [
"Sukajadi", "Cipedes", "Wanasari", "Kaliwungu", "Wonorejo",
"Rancabolang", "Setiamanah", "Mekarsari", "Jatisari", "Indraprasta",
]
df_kelurahan = pd.DataFrame(
{"kelurahan": rng.choice(nama_kelurahan, size=30)}
)
frekuensi = df_kelurahan["kelurahan"].value_counts(normalize=True)
df_kelurahan = df_kelurahan.assign(
kelurahan_frekuensi=df_kelurahan["kelurahan"].map(frekuensi).round(3)
)
print("\n== 4) Frequency encoding untuk kardinalitas tinggi ==")
print("jumlah kategori :", int(frekuensi.size), "pada 30 baris")
print("proporsi tiap kelurahan :")
print(frekuensi.round(3).to_string())
print("8 baris pertama hasil pemetaan :")
print(df_kelurahan.head(8).to_string())Output yang Dihasilkan:
== Data asal ==
kota pendidikan churn
0 Jakarta SD 1
1 Medan SD 0
2 Surabaya SMA 1
3 Bandung S1 0
4 Bandung S1 1
5 Medan S1 0
6 Jakarta S1 0
7 Surabaya S1 1
== 1) OrdinalEncoder dengan urutan eksplisit ==
pendidikan pendidikan_kode
0 SD 0
1 SD 0
2 SMA 1
3 S1 2
4 S1 2
5 S1 2
6 S1 2
7 S1 2
== 2) OneHotEncoder pada kolom kota ==
kolom hasil : ['kota_Bandung', 'kota_Jakarta', 'kota_Medan', 'kota_Surabaya']
kota_Bandung kota_Jakarta kota_Medan kota_Surabaya
0 0.0 1.0 0.0 0.0
1 0.0 0.0 1.0 0.0
2 0.0 0.0 0.0 1.0
3 1.0 0.0 0.0 0.0
4 1.0 0.0 0.0 0.0
5 0.0 0.0 1.0 0.0
6 0.0 1.0 0.0 0.0
7 0.0 0.0 0.0 1.0
kota tak dikenal 'Balikpapan' -> [0, 0, 0, 0]
== 3) LabelEncoder pada kolom target ==
pemetaan kelas : {'berhenti': 0, 'bertahan': 1}
target teks : ['berhenti', 'bertahan', 'berhenti', 'bertahan']
target angka : [0, 1, 0, 1]
== 4) Frequency encoding untuk kardinalitas tinggi ==
jumlah kategori : 10 pada 30 baris
proporsi tiap kelurahan :
kelurahan
Mekarsari 0.167
Wonorejo 0.167
Jatisari 0.167
Setiamanah 0.100
Sukajadi 0.100
Rancabolang 0.067
Wanasari 0.067
Cipedes 0.067
Kaliwungu 0.067
Indraprasta 0.033
8 baris pertama hasil pemetaan :
kelurahan kelurahan_frekuensi
0 Mekarsari 0.167
1 Setiamanah 0.100
2 Wonorejo 0.167
3 Jatisari 0.167
4 Rancabolang 0.067
5 Wonorejo 0.167
6 Wonorejo 0.167
7 Wanasari 0.067Bedah Kode Baris-per-Baris #
- Baris 11–15 (data sintetis): tiga kolom dibangun dari rng berseed —
kotadengan empat nilai nominal,pendidikandengan tiga jenjang, danchurnsebagai target biner. - Baris 20–22 (OrdinalEncoder eksplisit): argumen
categories=[["SD", "SMA", "S1"]]ditulis sebagai daftar-dalam-daftar karena encoder menerima satu daftar per kolom input. Hasilfit_transformberupa array dua dimensi, diratakan denganravel()lalu ditambahkan sebagai kolom baru. - Baris 27–33 (OneHotEncoder):
sparse_output=Falsemenghasilkan matriks padat,handle_unknown="ignore"disiapkan untuk kategori baru, danget_feature_names_out()memberi nama kolom hasil yang tetap menyandang asal-usulnya (kota_...). - Baris 35–37 (kategori tak dikenal): pelanggan baru dari Balikpapan — kota yang tidak ada pada data latih — di-transform tanpa galat dan menghasilkan baris nol semua. Perilaku ini yang membuat pipeline tetap hidup di produksi ketika kategori baru muncul.
- Baris 40–53 (LabelEncoder untuk target): kolom target teks
berhenti/bertahandikodekan menjadi 0/1. Pemetaan kelas dibangun dariclasses_(yang selalu terurut alfabetis) agar makna kode selalu bisa dibaca ulang. - Baris 55–73 (frequency encoding): skenario kardinalitas tinggi dibangun terpisah — sepuluh kelurahan pada tiga puluh baris.
value_counts(normalize=True)menghitung proporsi kemunculan tiap kategori, lalumap()menempelkan proporsi tersebut ke setiap baris sebagai angka fitur.
Membaca Hasil: Empat Perilaku yang Berbeda #
Pada bagian pertama, pendidikan terkode 0, 1, 2 mengikuti urutan yang kita tetapkan — angka yang bisa dibaca model sebagai tingkatan. Bagian kedua menghasilkan empat kolom biner dengan tepat satu nilai 1 per baris; tidak ada urutan yang ditanam. Bagian ketiga memperlihatkan pemetaan target {'berhenti': 0, 'bertahan': 1} — sekadar label, bukan tingkatan. Bagian keempat adalah jawaban atas masalah kardinalitas: sepuluh kelurahan tidak diubah menjadi sepuluh kolom one-hot, melainkan satu kolom numerik berisi proporsi kemunculan (0,167 untuk yang tersering sampai 0,033 untuk yang terjarang). Kategori yang muncul sekali saja pun tetap terwakili tanpa menambah dimensi. Strategi pendampingnya adalah menggabungkan kategori langka — misalnya semua yang di bawah 5% — menjadi kategori lainnya, sehingga one-hot tetap layak dipakai tanpa kolom yang nyaris seluruhnya bernilai nol.
Satu masalah kelas belum kita sentuh: apa yang terjadi bila target tidak seimbang — misalnya hanya 10% pelanggan yang berhenti, sehingga model yang selalu menebak “bertahan” tampak 90% akurat. Chapter berikutnya, Menangani Ketidakseimbangan Kelas, membahas diagnosis dan penanganannya dengan class_weight dan SMOTE.
Referensi Resmi #
- scikit-learn — Encoding categorical features — bagian panduan resmi tentang encoding.
- scikit-learn — OneHotEncoder
— dokumentasi API termasuk
handle_unknown. - scikit-learn — OrdinalEncoder
— dokumentasi argumen
categories. - scikit-learn — LabelEncoder — dokumentasi encoder target.