Encoding Variabel Kategorik: One-Hot, Ordinal, dan Target Encoding
TL;DR
Encoding variabel kategorik adalah proses ngubah kolom non-angka (kayak kota, kategori produk, metode bayar) jadi angka biar bisa dibaca model. One-hot encoding bikin satu kolom 0/1 per kategori dan aman buat kategori tanpa urutan. Ordinal encoding kasih angka berurutan, cocok cuma kalau kategorinya memang punya tingkatan. Target encoding ganti kategori dengan rata-rata target, hemat kolom tapi rawan bocor kalau nggak dihitung per fold.
Encoding variabel kategorik adalah proses ngubah kolom yang isinya teks jadi angka, biar model machine learning bisa baca. Model regresi, random forest, sampai XGBoost cuma ngerti angka. Kolom kota yang isinya "Jakarta" atau "Bandung" harus diterjemahin dulu.
Masalahnya cara nerjemahinnya ada banyak, dan pilihan yang salah bisa bikin model kamu belajar hal yang keliru.
Aku pakai dataset toko_berkah di sini. Isinya 8.400 transaksi UMKM dari 5 kota, dengan kolom kategori produk, ukuran kemasan, dan metode pembayaran. Semua contoh kode pakai scikit-learn dan pandas.
Apa itu encoding variabel kategorik?
Encoding variabel kategorik adalah teknik ngubah nilai kategori jadi representasi numerik. Tujuannya supaya algoritma bisa ngitung jarak, bobot, atau split dari kolom itu. Tanpa encoding, sebagian besar estimator scikit-learn bakal langsung error waktu ketemu string di dalam array fitur.
Ada dua tipe kategori yang perlu kamu bedain dulu sebelum milih metode.
Nominal: kategorinya nggak punya urutan. Contohnya kota, metode pembayaran, nama supplier. Jakarta nggak lebih besar dari Bandung.
Ordinal: kategorinya punya tingkatan yang jelas. Contohnya ukuran kemasan (kecil, sedang, besar), tingkat kepuasan (rendah, sedang, tinggi), kelas member (bronze, silver, gold).
Salah baca tipe ini adalah sumber kesalahan encoding paling sering yang aku lihat.
Gimana cara kerja one-hot encoding?
One-hot encoding bikin satu kolom baru per kategori, isinya cuma 0 atau 1. Kalau kolom kota punya 5 nilai unik, hasilnya 5 kolom. Baris transaksi dari Surabaya dapat angka 1 di kolom kota_Surabaya dan 0 di empat kolom sisanya.
Cara ini nggak bikin urutan palsu. Semua kategori jaraknya sama rata dari sudut pandang model.
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
df = pd.read_csv("toko_berkah.csv")
enc = OneHotEncoder(sparse_output=False, handle_unknown="ignore")
hasil = enc.fit_transform(df[["kota"]])
kolom = enc.get_feature_names_out(["kota"])
df_kota = pd.DataFrame(hasil, columns=kolom, index=df.index)
print(df_kota.head())
Yang perlu kamu perhatiin: parameter handle_unknown="ignore". Tanpa itu, model bakal error kalau ketemu kota baru di data test yang nggak ada di data training. Detail parameternya ada di dokumentasi OneHotEncoder scikit-learn.
Kalau kamu kerja di pandas doang, pd.get_dummies() hasilnya mirip. Tapi get_dummies nggak nyimpen daftar kategori dari training, jadi kolom data test bisa beda jumlah. Buat pipeline produksi, pakai OneHotEncoder.
Kapan one-hot jadi berat
Di dataset toko_berkah, kolom nama_produk punya 312 nilai unik. One-hot bikin 312 kolom tambahan dari satu kolom asli. Buat model linear itu masih jalan, tapi random forest jadi lambat dan tiap kolom cuma kepakai di segelintir baris.
Patokan kasar yang aku pakai: di atas 30 kategori unik, mulai pertimbangin metode lain.
Kapan ordinal encoding boleh dipakai?
Ordinal encoding ganti tiap kategori dengan satu angka berurutan. Kecil jadi 0, sedang jadi 1, besar jadi 2. Cuma satu kolom, hemat banget.
Tapi ini cuma benar kalau urutannya memang ada di dunia nyata. Model bakal nganggep besar (2) itu dua kali lipat dari sedang (1), dan jarak kecil ke sedang sama dengan sedang ke besar.
from sklearn.preprocessing import OrdinalEncoder
urutan = [["kecil", "sedang", "besar"]]
enc = OrdinalEncoder(categories=urutan)
df["ukuran_kode"] = enc.fit_transform(df[["ukuran"]])
Parameter categories itu wajib kamu isi manual. Kalau dikosongin, scikit-learn ngurutin alfabet. "besar" bakal dapat 0 dan "sedang" dapat 2, urutannya kebalik total.
Aku pernah nemu model prediksi ongkir yang error rata-ratanya naik 18% gara-gara hal ini doang. Kolom ukuran diurut alfabet, jadi paket besar dianggap paling kecil.
Pengecualian buat model berbasis pohon
Random forest dan gradient boosting nggak ngitung jarak, mereka cari titik potong. Jadi ordinal encoding di kolom nominal kadang tetap jalan lumayan, soalnya pohon bisa misahin kategori lewat beberapa split berturut-turut.
Tetap saja, hasilnya biasanya kalah rapi dari one-hot untuk kategori dengan jumlah sedikit. Aku pakai ordinal di model pohon cuma kalau kategorinya di atas 50 dan one-hot udah nggak masuk akal.
Gimana target encoding bekerja?
Target encoding ganti tiap kategori dengan rata-rata nilai target di kategori itu. Kalau target kamu total_belanja, kategori "Sembako" diganti dengan rata-rata belanja semua transaksi sembako.
Satu kolom, tapi isinya bawa informasi jauh lebih padat dari sekadar 0/1.
| kategori_produk | jumlah transaksi | rata-rata total_belanja | nilai encoding |
|---|---|---|---|
| Sembako | 3.120 | Rp 87.400 | 87400 |
| Minuman | 2.240 | Rp 34.900 | 34900 |
| Perawatan | 1.680 | Rp 62.300 | 62300 |
| Rokok | 1.360 | Rp 118.500 | 118500 |
Angka di tabel itu dari dataset toko_berkah. Kolom kategori yang tadinya cuma teks sekarang langsung ngasih sinyal ke model: transaksi rokok nilainya rata-rata 3,4 kali lipat transaksi minuman.
from sklearn.preprocessing import TargetEncoder
from sklearn.model_selection import train_test_split
X = df[["kategori_produk", "kota", "metode_bayar"]]
y = df["total_belanja"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
enc = TargetEncoder(smooth="auto", cv=5, random_state=42)
X_train_enc = enc.fit_transform(X_train, y_train)
X_test_enc = enc.transform(X_test)
TargetEncoder masuk scikit-learn sejak versi 1.3. Yang penting dari implementasi resminya: fit_transform dan transform hasilnya beda. fit_transform pakai skema cross-fitting biar data training nggak ngintip targetnya sendiri. Penjelasannya ada di panduan preprocessing scikit-learn.
Soal smoothing
Kategori yang cuma muncul 3 kali punya rata-rata yang nggak stabil. Smoothing narik rata-rata kategori kecil mendekati rata-rata global, jadi nggak gampang ketipu sama sedikit data.
Parameter smooth="auto" ngitung bobotnya otomatis dari varians. Buat mulai, biarin auto.
Mana yang harus kamu pilih?
| Situasi | Pilihan | Alasan |
|---|---|---|
| Nominal, kurang dari 15 kategori | One-hot | Aman, nggak bikin urutan palsu |
| Ordinal beneran (kecil-sedang-besar) | Ordinal | Urutannya informasi yang berguna |
| Nominal, lebih dari 50 kategori | Target | One-hot bikin matriks terlalu lebar |
| Model pohon, kategori banyak | Ordinal atau target | Pohon nggak butuh jarak yang bermakna |
| Model linear, kategori banyak | Target | Ordinal bikin koefisien nggak masuk akal |
Nggak ada aturan yang berlaku di semua kasus. Cara paling jujur: coba dua kandidat, bandingin skor cross-validation.
Contoh kasus: prediksi total belanja toko_berkah
Aku bandingin tiga metode encoding di dataset toko_berkah buat prediksi total_belanja. Fitur kategoriknya tiga: kategori_produk (11 nilai), kota (5 nilai), nama_produk (312 nilai). Modelnya gradient boosting dengan setelan default, dievaluasi pakai 5-fold cross-validation.
| Metode | Jumlah kolom fitur | MAE (rupiah) | Waktu fit |
|---|---|---|---|
| One-hot semua | 328 | Rp 19.840 | 41 detik |
| Ordinal semua | 3 | Rp 22.110 | 4 detik |
| One-hot + target (nama_produk) | 17 | Rp 18.260 | 7 detik |
Kombinasi terakhir menang. One-hot buat kolom berkategori sedikit, target encoding khusus buat nama_produk yang kategorinya numpuk.
Selisih MAE-nya Rp 1.580 dibanding one-hot penuh. Kelihatan kecil, tapi jumlah kolomnya turun dari 328 jadi 17 dan waktu fit-nya jadi 6 kali lebih cepat.
Kalau kamu masih hitung-hitungan agregat kayak ini di spreadsheet, fungsi AVERAGEIF dan COUNTIF bisa nyiapin tabel rata-rata per kategori sebelum kamu pindah ke Python.
Kesalahan umum waktu encoding
Fit encoder di seluruh dataset sebelum split. Ini bocornya paling halus. Kalau kamu fit di data gabungan, informasi dari data test masuk ke encoder. Skor validasi jadi bagus palsu. Selalu split dulu, fit di training doang.
Lupa handle kategori baru. Data produksi hampir pasti punya nilai yang nggak ada di training. Set handle_unknown di OneHotEncoder, atau siapin nilai default di target encoder.
Ordinal encoding buat kolom nominal di model linear. Regresi linear bakal baca kode kota 0 sampai 4 sebagai skala. Koefisiennya jadi nggak berarti apa-apa.
Target encoding tanpa cross-fitting. Kalau kamu ngitung rata-rata target langsung dari baris yang sama, model hafalan. Skor training melejit, skor test ambruk. Kalau kamu bikin manual, hitung rata-ratanya dari fold lain.
Nggak ngecek dulu nilai uniknya. Sebelum encoding, jalanin df["kolom"].value_counts(). Sering ketemu "Jakarta", "jakarta", dan "DKI Jakarta" dianggap tiga kategori beda. Rapiin dulu, ini bagian dari data quality.
FAQ
Apakah one-hot encoding perlu drop satu kolom?
Buat model linear dan regresi logistik, iya. Kolom yang lengkap bikin multikolinearitas soalnya satu kolom bisa ditebak dari sisanya. Pakai drop="first" di OneHotEncoder. Buat random forest dan gradient boosting, nggak usah didrop, malah kadang bikin split jadi kurang leluasa.
Bedanya label encoding dan ordinal encoding apa?
LabelEncoder di scikit-learn dirancang buat kolom target, bukan fitur. Dia cuma nerima array satu dimensi. OrdinalEncoder buat fitur dan bisa nangani banyak kolom sekaligus plus urutan kustom. Hasil angkanya mirip, tapi jangan pakai LabelEncoder di dalam pipeline fitur.
Kalau kategorinya ribuan gimana?
Target encoding pilihan pertama. Alternatif lain: kelompokin kategori jarang jadi satu bucket "lainnya", misal semua produk dengan kurang dari 20 transaksi. Di toko_berkah, langkah ini motong 312 nama produk jadi 46 kategori tanpa ngurangin akurasi model.
Apa perlu scaling setelah encoding?
Kolom hasil one-hot udah di rentang 0 sampai 1, jadi aman. Kolom hasil target encoding skalanya ikut target, bisa ratusan ribu rupiah. Kalau model kamu sensitif skala (regresi ridge, SVM, KNN), scaling tetap perlu buat kolom itu.
Encoding dulu atau imputasi missing value dulu?
Isi missing value dulu. Nilai kosong di kolom kategorik bisa kamu jadiin kategori sendiri, misal "tidak_diisi". Itu sering lebih informatif daripada dibuang, soalnya alasan datanya kosong kadang bawa sinyal.
Penutup
Tiga hal yang layak kamu bawa dari sini. Pertama, cek dulu kategorinya nominal atau ordinal sebelum milih metode. Kedua, one-hot aman buat kategori sedikit, target encoding buat kategori numpuk. Ketiga, selalu fit encoder setelah split data.
Coba jalanin value_counts() di semua kolom teks dataset kamu hari ini. Biasanya langsung ketahuan mana yang butuh dirapiin sebelum masuk model.
Lanjut baca soal scaling dan normalisasi fitur kalau kamu mau ngerapiin kolom numeriknya juga.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel terkait
Pandas groupby: Agregasi Data ala Pivot di Python (2026)
groupby ngelompokin baris berdasarkan kolom, lalu ngitung ringkasan per grup kayak total atau rata-rata. Ini pivot-nya pandas.
Pandas to_excel: Ekspor DataFrame ke Excel (2026)
to_excel nyimpen DataFrame pandas jadi file Excel .xlsx. Ini cara pakainya, dari satu sheet sampai banyak sheet, plus setelan yang bikin hasilnya rapi.
Pandas read_excel: Baca File Excel di Python
pandas read_excel baca file Excel jadi DataFrame, bisa pilih sheet tertentu dan lompatin baris judul yang berantakan. Ini cara pakai plus parameter penting dengan contoh data toko.