Model machine learning berhitung dengan angka, sedangkan dunia nyata penuh teks: nama menu, ukuran gelas, kota asal pelanggan. Encoding adalah jembatan di antara keduanya — mengubah kolom kategorikal menjadi representasi numerik yang bisa dibaca model. Pilihan encodingnya tidak bebas: kategori yang punya urutan alami (kecil-sedang-besar) dan kategori yang setara (Bandung-Surabaya-Medan) menuntut perlakuan berbeda, dan pilihan yang keliru bisa menanamkan urutan palsu ke dalam model. Pada chapter ini kita bahas tiga pendekatan utama beserta jebakan yang paling sering menjumpai kita saat data baru datang.

Ordinal Encoding dan Frequency Encoding #

Mari mulai dari kasus paling bersahabat: kategori yang urutannya jelas. Ukuran gelas “kecil”, “sedang”, “besar” membawa urutan bermakna — sedang lebih besar dari kecil — sehingga kita berhak menggantinya dengan 0, 1, 2. Untuk kategori tanpa urutan seperti nama menu, kita bisa memakai frequency encoding: setiap kategori diganti proporsi kemunculannya di data:

python
import pandas as pd

pesanan = pd.DataFrame({
    "menu": ["kopi susu", "matcha", "teh tawar", "kopi susu",
             "kopi hitam", "kopi susu", "teh tawar", "matcha"],
    "ukuran": ["kecil", "sedang", "besar", "sedang",
               "kecil", "besar", "sedang", "kecil"],
})

# Ordinal: urutan bermakna, kita tetapkan angkanya sendiri.
peta_ukuran = {"kecil": 0, "sedang": 1, "besar": 2}
pesanan["ukuran_skor"] = pesanan["ukuran"].map(peta_ukuran)

# Frequency: kategori diganti proporsi kemunculannya di data.
frekuensi_menu = pesanan["menu"].value_counts() / len(pesanan)
pesanan["menu_freq"] = pesanan["menu"].map(frekuensi_menu)

print(pesanan)

Output yang Dihasilkan:

text
         menu  ukuran  ukuran_skor  menu_freq
0   kopi susu   kecil            0      0.375
1      matcha  sedang            1      0.250
2   teh tawar   besar            2      0.250
3   kopi susu  sedang            1      0.375
4  kopi hitam   kecil            0      0.125
5   kopi susu   besar            2      0.375
6   teh tawar  sedang            1      0.250
7      matcha   kecil            0      0.250

Mari kita bedah kode tersebut:

  1. Baris 11 (peta_ukuran = {...}): kamus urutan dibuat eksplisit. Angka 0-1-2 kita tetapkan sendiri karena urutan itulah maknanya; model belajar bahwa naik satu skor berarti naik satu tingkat ukuran.
  2. Baris 12 (map(peta_ukuran)): mengganti setiap nilai teks dengan angka dari kamus. Satu baris, tanpa perulangan.
  3. Baris 15 (value_counts() / len(...)): menghitung frekuensi tiap menu lalu membaginya dengan total baris — “kopi susu” muncul 3 dari 8 pesanan (0,375), “kopi hitam” sekali (0,125).
  4. Baris 16 (map(frekuensi_menu)): frekuensi tersebut dipetakan balik ke setiap baris. Menu populer kini tampil dengan angka lebih besar — informasi yang berguna saat kepopuleran memang relevan dengan target.

Frequency encoding hemat kolom: berapapun banyak kategorinya, hasilnya tetap satu kolom. Harganya, informasi identitas menu tertentu berubah menjadi informasi kepopuleran — dua menu yang sama-sama jarang menjadi tidak terbedakan.

One-Hot Encoding untuk Kategori Setara #

Sekarang kasus yang mustahil diurutkan: kota. Menulis Bandung = 0, Surabaya = 1, Medan = 2 akan menanamkan klaim palsu bahwa Medan “dua tingkat lebih tinggi” dari Bandung. One-hot encoding menghindarinya dengan memberi setiap kategori kolomnya sendiri: nilai 1 pada kolom kota terkait, 0 pada sisanya:

python
import pandas as pd

pelanggan = pd.DataFrame({
    "nama": ["Budi", "Siti", "Dewi", "Rani", "Agus"],
    "kota": ["Bandung", "Surabaya", "Bandung", "Medan", "Surabaya"],
})

one_hot = pd.get_dummies(pelanggan["kota"], prefix="kota").astype(int)
hasil = pd.concat([pelanggan, one_hot], axis=1)
print(hasil)
print()
print("versi drop_first (kolom Bandung dijadikan acuan):")
print(pd.get_dummies(pelanggan["kota"], prefix="kota", drop_first=True).astype(int))

Output yang Dihasilkan:

text
   nama      kota  kota_Bandung  kota_Medan  kota_Surabaya
0  Budi   Bandung             1           0              0
1  Siti  Surabaya             0           0              1
2  Dewi   Bandung             1           0              0
3  Rani     Medan             0           1              0
4  Agus  Surabaya             0           0              1

versi drop_first (kolom Bandung dijadikan acuan):
   kota_Medan  kota_Surabaya
0           0              0
1           0              1
2           0              0
3           1              0
4           0              1

Tiga hal terjadi di balik layar:

  1. Baris 8 (pd.get_dummies(...)): kolom kota diurai menjadi tiga kolom baru — satu per kategori — dengan awalan kota_. astype(int) mengubah keluaran boolean pandas menjadi angka 0/1 agar rapi dibaca. Tiga kategori menghasilkan tiga kolom, sepuluh kategori menghasilkan sepuluh; di situlah letak biayanya.
  2. Baris 9 (pd.concat(..., axis=1)): kolom-kolom one-hot ditempelkan di samping tabel asli sehingga nama, kota, dan representasi numeriknya berdampingan.
  3. Baris 13 (drop_first=True): membuang kategori pertama (Bandung) karena statusnya bisa dibaca dari kolom lain — bila Medan 0 dan Surabaya 0, jelas pelanggan dari Bandung. Baris Budi (0, 0) dibaca “bukan Medan, bukan Surabaya, berarti Bandung”.

Versi drop_first menghemat satu kolom dan mencegah ketergantungan sempurna antar kolom — masalah yang kita bahas tuntas pada chapter Menangani Fitur Berkorelasi .

Jebakan Kategori Tak Dikenal pada Data Baru #

Masalah muncul ketika model dipakai pada data baru. Data latih mengenal Bandung, Surabaya, Medan — lalu datang pelanggan dari Yogyakarta. pd.get_dummies memproses data yang diberikan saja, sehingga kolom yang dihasilkan tidak lagi sama dengan data latih:

python
import pandas as pd

peta_ukuran = {"kecil": 0, "sedang": 1, "besar": 2}

latih = pd.DataFrame({"kota": ["Bandung", "Surabaya", "Medan", "Bandung"]})
kolom_latih = pd.get_dummies(latih["kota"], prefix="kota").columns

baru = pd.DataFrame({"kota": ["Bandung", "Yogyakarta"], "ukuran": ["kecil", "jumbo"]})

print("kolom hasil one-hot data baru (tanpa penyesuaian):")
print(pd.get_dummies(baru["kota"], prefix="kota").astype(int))
print()

aman = pd.get_dummies(baru["kota"], prefix="kota").reindex(
    columns=kolom_latih, fill_value=0
).astype(int)
print("setelah reindex ke kolom data latih:")
print(aman)
print()

print("map pada ukuran tak dikenal:", baru["ukuran"].map(peta_ukuran).tolist())

Output yang Dihasilkan:

text
kolom hasil one-hot data baru (tanpa penyesuaian):
   kota_Bandung  kota_Yogyakarta
0             1                0
1             0                1

setelah reindex ke kolom data latih:
   kota_Bandung  kota_Medan  kota_Surabaya
0             1           0              0
1             0           0              0

map pada ukuran tak dikenal: [0.0, nan]

Bacaannya begini:

  1. Baris 6 (kolom_latih = ...columns): daftar kolom one-hot data latih disimpan sebagai acuan. Inilah “kosakatan resmi” yang dipelajari saat pelatihan.
  2. Baris 10-11: tanpa penyesuaian, get_dummies pada data baru menghasilkan kota_Bandung dan kota_Yogyakarta — strukturnya bentrok dengan data latih yang memuat kota_Medan dan kota_Surabaya. Model akan menolak masukan ini.
  3. Baris 14-16 (reindex(columns=kolom_latih, fill_value=0)): hasil one-hot dipaksa mengikuti struktur data latih. Kolom yang hilang diisi 0, kolom asing otomatis dibuang — Yogyakarta direpresentasikan sebagai “bukan satu kota pun yang dikenal”.
  4. Baris 21 (map pada “jumbo”): encoding ordinal juga berlubang — map mengembalikan nan untuk ukuran yang tidak ada di kamus. Sebelum dipakai, nilai nan harus ditangani: diisi kategori terdekat, nilai default, atau barisnya diperiksa manual.

Kapan Memakai Encoding yang Mana #

SituasiEncoding yang tepatAlasannya
Kategori berurutan (kecil-sedang-besar)Ordinal map manualurutan bermakna dan ingin dipertahankan sebagai satu kolom
Kategori setara, jumlahnya sedikitOne-hot get_dummiestiap kategori berdiri sendiri, tanpa urutan palsu
Kategori setara, jumlahnya banyakdrop_first atau frequencymencegah jumlah kolom membengkak dan ketergantungan antar kolom
Kategori sangat banyak (ribu-an)Frequency atau target groupingone-hot menjadi tidak praktis

Rangkuman #

  • Ordinal encoding (map dengan kamus manual) untuk kategori berurutan; angkanya menempelkan urutan yang memang bermakna.
  • One-hot encoding (pd.get_dummies) untuk kategori setara; setiap kategori mendapat kolomnya sendiri, dan drop_first membuang satu kolom yang berlebihan.
  • Frequency encoding mengganti kategori dengan proporsi kemunculannya — hemat kolom, tetapi identitas kategori menyusut menjadi kepopuleran.
  • Data baru bisa membawa kategori tak dikenal: selaraskan kolom dengan reindex untuk one-hot dan tangani nan dari map untuk ordinal, selalu memakai struktur data latih sebagai acuan.

Dengan kategori yang kini berwujud angka, tabel kita hampir siap untuk model — tinggal satu masalah: kolom-kolom itu masih berdiri dengan satuan yang berbeda-beda. Suhu berkisar 20-an, pendapatan ribuan. Kita atur dengan penskalaan, dimulai dari normalisasi pada chapter berikutnya.

Referensi #

  1. pandas — User Guide
  2. scikit-learn — Preprocessing Data