Praktikum 9: Pengenalan Machine Learning

1. Peta machine learning

Machine learning mempelajari pola dari data untuk membuat prediksi atau menemukan struktur. Kita akan mencoba lima jenis tugas pada peta berikut.

Peta supervised learning dengan klasifikasi dan regresi serta unsupervised learning dengan clustering, asosiasi, dan reduksi dimensi
Gambar dari materi kuliah K9, slide 13.

Ada contoh jawaban atau belum?

Perbandingan pembelajaran supervised dengan contoh berlabel dan unsupervised yang mencari kelompok dari data tanpa label
Ilustrasi supervised dan unsupervised learning dari slide 14.
Supervised Unsupervised
Contoh jawaban saat belajar Ada target Tidak ada target sebagai acuan
Yang dicari Aturan untuk memprediksi target Kelompok, pola bersama, atau representasi data
Praktik kita Klasifikasi dan regresi Clustering, asosiasi, dan PCA
  • Fitur (X): informasi yang menjadi masukan.
  • Target (Y): jawaban yang ingin diprediksi, berupa kategori atau angka.
  • Statistika dan ML beririsan: regresi dapat dipakai untuk memahami hubungan maupun membuat prediksi.

Coba tentukan: memprediksi nilai ujian dan mengelompokkan kebiasaan belajar termasuk bagian yang mana?

Mulai di R

Seluruh contoh memakai data simulasi atau transaksi buatan. Ganti nim_seed sesuai arahan kelas, lalu jalankan kode berurutan.

nim_seed <- 123

Paket untuk melakukan Knit ke readthedown, jika belum terpasang:

install.packages(c("rmarkdown", "knitr", "rmdformats"))

Pada klasifikasi dan regresi, pembagian datanya adalah:

Data latih: 80% Data uji: 20%
Membentuk model Menilai prediksi pada data yang tidak ikut melatih model

Pemilihan di antara banyak model atau penyetelan parameter memerlukan validasi atau cross-validation pada data latih. Data uji dipakai untuk penilaian akhir.

2. Klasifikasi: risiko nasabah

Ilustrasi kelas risiko nasabah berdasarkan pendapatan dan tabungan dengan dua ambang
Ilustrasi aturan dua ambang pada slide 15. Praktik berikut memakai regresi logistik untuk mempelajari batas dari data.
  • Fitur: pendapatan dan tabungan, dalam juta rupiah.
  • Target: risiko Rendah atau Tinggi.
  • Metode praktik: regresi logistik menghasilkan probabilitas kelas Tinggi.

Sebelum menjalankan kode: hasil yang kita cari berupa kelas atau jumlah uang?

Data dan model

Kita membuat 180 nasabah. Label 1 berarti risiko tinggi dan 0 berarti risiko rendah. plogis() menghasilkan probabilitas, lalu rbinom() membangkitkan labelnya.

set.seed(nim_seed + 1)
n_nasabah <- 180

nasabah <- data.frame(
  pendapatan = round(runif(n_nasabah, 2, 20), 1),
  tabungan = round(runif(n_nasabah, 0, 80), 1)
)

# Mekanisme simulasi: probabilitas risiko tinggi bergantung pada fitur.
peluang_simulasi <- plogis(
  2.2 - 0.17 * nasabah$pendapatan - 0.05 * nasabah$tabungan
)
nasabah$risiko_tinggi <- rbinom(n_nasabah, 1, peluang_simulasi)
nasabah$status <- factor(
  ifelse(nasabah$risiko_tinggi == 1, "Tinggi", "Rendah"),
  levels = c("Rendah", "Tinggi")
)
knitr::kable(head(nasabah[c("pendapatan", "tabungan", "status")], 3),
  col.names = c("Pendapatan (juta Rp)", "Tabungan (juta Rp)", "Risiko"))
Pendapatan (juta Rp) Tabungan (juta Rp) Risiko
3.5 11.7 Rendah
9.4 14.7 Rendah
11.3 53.0 Rendah

Ambil 80% pengamatan untuk melatih model. Sisanya menjadi data uji.

set.seed(nim_seed + 2)
id_latih_nasabah <- sample(
  seq_len(nrow(nasabah)), size = floor(0.8 * nrow(nasabah))
)
latih_nasabah <- nasabah[id_latih_nasabah, ]
uji_nasabah <- nasabah[-id_latih_nasabah, ]
model_risiko <- glm(
  risiko_tinggi ~ pendapatan + tabungan,
  data = latih_nasabah,
  family = binomial()
)

family = binomial() dengan penghubung logit bawaan membentuk regresi logistik. Gunakan type = "response" untuk memperoleh probabilitas.

prob_uji <- predict(
  model_risiko, newdata = uji_nasabah, type = "response"
)

ambang <- 0.5
kelas_uji <- factor(
  ifelse(prob_uji >= ambang, "Tinggi", "Rendah"),
  levels = c("Rendah", "Tinggi")
)
Probabilitas ≥ 0,5: Tinggi
Probabilitas < 0,5: Rendah

Batas keputusan

warna_risiko <- c(Rendah = "#286CAA", Tinggi = "#D65745")
grid_income <- seq(2, 20, length.out = 120)
grid_saving <- seq(0, 80, length.out = 120)
grid_risiko <- expand.grid(
  pendapatan = grid_income, tabungan = grid_saving
)
prob_grid <- matrix(
  predict(model_risiko, newdata = grid_risiko, type = "response"),
  nrow = length(grid_income)
)

plot(latih_nasabah$pendapatan, latih_nasabah$tabungan,
     pch = 16, col = adjustcolor(warna_risiko[latih_nasabah$status], 0.75),
     xlim = c(2, 20), ylim = c(0, 80),
     xlab = "Pendapatan bulanan (juta rupiah)",
     ylab = "Tabungan (juta rupiah)",
     main = "Klasifikasi risiko: data latih dan batas keputusan")
if (min(prob_grid) < ambang && max(prob_grid) > ambang) {
  contour(grid_income, grid_saving, prob_grid,
          levels = ambang, add = TRUE, drawlabels = FALSE,
          lty = 2, lwd = 2, col = "#253342")
}
legend("topright", c("Risiko rendah", "Risiko tinggi", "Batas p = 0,5"),
       col = c(warna_risiko, "#253342"),
       pch = c(16, 16, NA), lty = c(NA, NA, 2), bty = "n", cex = 0.85)

  • Titik: label aktual pada data latih.
  • Garis putus-putus: lokasi probabilitas prediksi 0,5.
  • Dekat garis: probabilitas mendekati 0,5, sehingga pemisahan kelas kurang tegas menurut model.

Prediksinya benar di bagian mana?

matriks_bingung <- table(Aktual = uji_nasabah$status,
                         Prediksi = kelas_uji)
akurasi <- mean(kelas_uji == uji_nasabah$status)

# Pembanding sederhana: selalu memilih kelas mayoritas data latih.
kelas_mayoritas <- names(which.max(table(latih_nasabah$status)))
akurasi_baseline <- mean(uji_nasabah$status == kelas_mayoritas)
local({
  op <- par(mar = c(4, 5, 3, 1)); on.exit(par(op))
  plot.new(); plot.window(xlim = c(0, 2), ylim = c(0, 2), asp = 1)
  for (i in 1:2) for (j in 1:2) {
    isi <- matriks_bingung[i, j]
    warna <- if (i == j) "#D9EBE5" else "#F4DDD7"
    rect(j - 1, 2 - i, j, 3 - i, col = warna,
         border = "white", lwd = 5)
    text(j - 0.5, 2.5 - i, labels = isi, cex = 2, col = "#263B4B")
  }
  axis(1, at = c(0.5, 1.5), labels = c("Rendah", "Tinggi"), tick = FALSE)
  axis(2, at = c(1.5, 0.5), labels = c("Rendah", "Tinggi"),
       tick = FALSE, las = 1)
  mtext("Kelas prediksi", side = 1, line = 2.4)
  mtext("Kelas aktual", side = 2, line = 3.4)
  title("Prediksinya benar di bagian mana?")
})

Baca confusion matrix Hasil pada data uji
Diagonal Prediksi benar
Di luar diagonal Kelas tertukar
Risiko tinggi yang diprediksi rendah 3 dari 4 nasabah
knitr::kable(data.frame(
  Pendekatan = c("Regresi logistik", "Selalu memilih kelas mayoritas latih"),
  Akurasi = 100 * c(akurasi, akurasi_baseline)
), digits = 1, col.names = c("Pendekatan", "Akurasi uji (%)"))
Pendekatan Akurasi uji (%)
Regresi logistik 88.9
Selalu memilih kelas mayoritas latih 88.9

Pembanding selalu memilih kelas mayoritas data latih. Kedua pendekatan memperoleh akurasi yang sama pada pembagian ini.

Diskusi: apakah akurasi yang tinggi menjamin nasabah berisiko tinggi berhasil dikenali?

Satu nasabah baru

nasabah_baru <- data.frame(pendapatan = 8, tabungan = 15)
prob_baru <- predict(model_risiko, nasabah_baru, type = "response")
status_baru <- ifelse(prob_baru >= ambang, "Tinggi", "Rendah")

Pendapatan 8 juta, tabungan 15 juta: probabilitas risiko tinggi 0.673, sehingga kelas prediksinya Tinggi.

3. Regresi: harga mobil bekas

Grafik pada slide kuliah memperlihatkan penurunan harga mobil ketika jarak tempuh bertambah
Hubungan jarak tempuh dan harga pada slide 16.
  • Fitur: jarak tempuh dalam ribu kilometer.
  • Target: harga dalam juta rupiah.
  • Metode praktik: regresi linear sederhana.

Perhatikan sumbu grafik: mengapa masalah ini termasuk regresi?

Data dan garis model

Kita membuat 120 mobil dengan kecenderungan harga menurun ketika jarak tempuh bertambah. Galat membuat harga tersebar di sekitar pola tersebut.

set.seed(nim_seed + 3)
n_mobil <- 120
mobil <- data.frame(
  kilometer_ribu = runif(n_mobil, 5, 160)
)
mobil$harga_juta <- 300 - 1.1 * mobil$kilometer_ribu +
  rnorm(n_mobil, mean = 0, sd = 15)

set.seed(nim_seed + 4)
id_latih_mobil <- sample(seq_len(n_mobil), size = 0.8 * n_mobil)
latih_mobil <- mobil[id_latih_mobil, ]
uji_mobil <- mobil[-id_latih_mobil, ]
knitr::kable(head(mobil, 3), digits = 2,
  col.names = c("Jarak tempuh (ribu km)", "Harga (juta Rp)"))
Jarak tempuh (ribu km) Harga (juta Rp)
104.68 186.33
75.79 224.22
106.39 189.88
model_harga <- lm(harga_juta ~ kilometer_ribu, data = latih_mobil)
coef(model_harga)
##    (Intercept) kilometer_ribu 
##       299.1422        -1.0913
pred_harga <- predict(model_harga, newdata = uji_mobil)

lm() mempelajari garis dari data latih. predict() menerapkan garis itu pada data uji.

plot(latih_mobil$kilometer_ribu, latih_mobil$harga_juta,
     pch = 16, col = adjustcolor("#566B7D", 0.65),
     xlim = range(mobil$kilometer_ribu), ylim = range(mobil$harga_juta),
     xlab = "Jarak tempuh (ribu km)", ylab = "Harga (juta rupiah)",
     main = "Regresi harga mobil bekas")
points(uji_mobil$kilometer_ribu, uji_mobil$harga_juta,
       pch = 17, col = "#B56B28", cex = 1.1)
abline(model_harga, col = "#286CAA", lwd = 2.5)
legend("topright", c("Data latih", "Data uji", "Garis model latih"),
       col = c("#566B7D", "#B56B28", "#286CAA"),
       pch = c(16, 17, NA), lty = c(NA, NA, 1), bty = "n", cex = 0.85)

  • Titik abu-abu: data latih. Segitiga: data uji.
  • Kemiringan: -1.091 juta rupiah per tambahan 1.000 km menurut model.
  • Pola ini berasal dari simulasi. Pada mobil nyata, merek, umur, dan kondisi juga berkaitan dengan harga.

Seberapa jauh prediksi meleset?

MAE = rerata |aktual − prediksi|
RMSE = √rerata (aktual − prediksi)2
mae_harga <- mean(abs(uji_mobil$harga_juta - pred_harga))
rmse_harga <- sqrt(mean((uji_mobil$harga_juta - pred_harga)^2))

pred_baseline_harga <- mean(latih_mobil$harga_juta)
rmse_baseline_harga <- sqrt(mean(
  (uji_mobil$harga_juta - pred_baseline_harga)^2
))
mae_baseline_harga <- mean(abs(
  uji_mobil$harga_juta - pred_baseline_harga
))

knitr::kable(data.frame(
  Pendekatan = c("Regresi linear", "Selalu memprediksi rerata harga latih"),
  MAE = c(mae_harga, mae_baseline_harga),
  RMSE = c(rmse_harga, rmse_baseline_harga)
), digits = 2, col.names = c("Pendekatan", "MAE (juta Rp)", "RMSE (juta Rp)"))
Pendekatan MAE (juta Rp) RMSE (juta Rp)
Regresi linear 12.98 15.95
Selalu memprediksi rerata harga latih 41.45 49.99
  • MAE: rata-rata besar kesalahan absolut.
  • RMSE: lebih peka terhadap kesalahan besar.
  • Keduanya bersatuan juta rupiah dan merupakan ringkasan kesalahan, bukan batas maksimum setiap kesalahan.

Mobil dengan 80.000 km

mobil_baru <- data.frame(kilometer_ribu = 80)
harga_baru <- predict(model_harga, newdata = mobil_baru)
harga_baru
##      1 
## 211.84

Masukkan 80 karena satuan fitur adalah ribu kilometer. Harga prediksinya sekitar Rp 211.84 juta.

Coba: ganti jarak tempuh menjadi 100.000 km. Jelaskan satuan input dan hasilnya.

4. Clustering: segmentasi pelanggan

Ilustrasi kelompok pelanggan pada sumbu umur dan pendapatan
Contoh segmentasi berdasarkan umur dan pendapatan pada slide 17.
  • Tujuan: mencari pelanggan dengan karakteristik serupa.
  • Target segmen: belum tersedia sebagai contoh jawaban.
  • Praktik kita: k-means dengan empat fitur pelanggan.

Empat fitur pelanggan

Data terdiri dari 150 pelanggan simulasi. Label kelompok pembangkit tidak diberikan kepada k-means.

set.seed(nim_seed + 5)
m <- 50
pelanggan <- data.frame(
  pendapatan = c(rnorm(m, 5, 0.9), rnorm(m, 12, 1.8),
                 rnorm(m, 24, 2.5)),
  belanja = c(rnorm(m, 1.2, 0.25), rnorm(m, 3.8, 0.6),
              rnorm(m, 8, 1)),
  frekuensi = c(rpois(m, 2) + 1, rpois(m, 7) + 1,
                rpois(m, 14) + 1),
  durasi_kunjungan = c(rnorm(m, 15, 3), rnorm(m, 35, 5),
                       rnorm(m, 60, 7))
)
pelanggan <- pelanggan[sample(seq_len(nrow(pelanggan))), ]
row.names(pelanggan) <- NULL
fitur_pelanggan <- names(pelanggan)
knitr::kable(head(pelanggan, 3), digits = 2,
  col.names = c("Pendapatan", "Belanja", "Frekuensi", "Durasi kunjungan"))
Pendapatan Belanja Frekuensi Durasi kunjungan
24.29 8.12 17 63.74
14.07 3.12 9 30.02
5.54 1.56 4 19.61
Fitur Satuan
Pendapatan dan belanja Juta rupiah per bulan
Frekuensi Jumlah transaksi per bulan
Durasi kunjungan Menit per kunjungan

Samakan skala, lalu cari kelompok

K-means memakai jarak. Standardisasi membantu agar perbedaan skala fitur tidak mendominasi perhitungan.

Nilai standar = (nilai awal − rerata) / simpangan baku
X_pelanggan <- scale(pelanggan[fitur_pelanggan])
set.seed(nim_seed + 6)
k <- 3
hasil_kmeans <- kmeans(X_pelanggan, centers = k, nstart = 25)
pelanggan$cluster <- factor(hasil_kmeans$cluster)
  • centers = 3: kita menetapkan tiga kelompok untuk demonstrasi.
  • nstart = 25: mencoba beberapa titik awal, lalu memilih hasil dengan jumlah kuadrat jarak dalam kelompok terkecil di antara percobaan itu.
  • K-means bergantian memperbarui anggota dan pusat kelompok.

Sebelum dan sesudah dikelompokkan

profil_cluster <- aggregate(
  pelanggan[fitur_pelanggan],
  by = list(Cluster = pelanggan$cluster), FUN = mean
)
profil_cluster$Jumlah <- as.integer(
  table(pelanggan$cluster)[as.character(profil_cluster$Cluster)]
)
warna_cluster <- c("#286CAA", "#28856A", "#B56B28")
local({
  op <- par(mfrow = c(1, 2), mar = c(4.5, 4.5, 3, 1)); on.exit(par(op))
  plot(pelanggan$pendapatan, pelanggan$belanja,
       pch = 16, col = adjustcolor("#687C88", 0.65),
       xlab = "Pendapatan (juta Rp/bulan)",
       ylab = "Belanja (juta Rp/bulan)", main = "Sebelum clustering")
  plot(pelanggan$pendapatan, pelanggan$belanja,
       pch = 16, col = warna_cluster[pelanggan$cluster],
       xlab = "Pendapatan (juta Rp/bulan)",
       ylab = "Belanja (juta Rp/bulan)", main = "Setelah k-means, k = 3")
  points(profil_cluster$pendapatan, profil_cluster$belanja,
         pch = 4, cex = 1.7, lwd = 2.5, col = "#253342")
  legend("topleft", paste("Cluster", levels(pelanggan$cluster)),
         col = warna_cluster, pch = 16, bty = "n", cex = 0.8)
})

Warna berasal dari empat fitur yang distandardisasi. Dua sumbu ditampilkan agar mudah dilihat. Tanda silang menunjukkan rerata kelompok pada kedua sumbu.

knitr::kable(profil_cluster, digits = 2,
  col.names = c("Cluster", "Pendapatan", "Belanja", "Frekuensi",
                "Durasi kunjungan", "Jumlah"))
Cluster Pendapatan Belanja Frekuensi Durasi kunjungan Jumlah
1 11.93 3.83 7.78 34.69 50
2 24.34 7.94 16.62 58.75 50
3 5.13 1.23 2.86 14.67 50

Profil memakai satuan asli. Nomor cluster hanya penanda dan tidak menunjukkan peringkat. Nilai k = 3 juga belum membuktikan jumlah kelompok terbaik.

Diskusi: beri nama setiap segmen berdasarkan profilnya. Mengapa tugas ini berbeda dari klasifikasi nasabah?

5. Asosiasi: isi keranjang belanja

Foto rak produk toko pada contoh market basket analysis
Contoh konteks market basket analysis pada slide 19.
  • Pertanyaan: barang apa yang sering muncul bersama?
  • Satu pengamatan: satu transaksi yang dapat memuat beberapa barang.
  • Contoh aturan: Roti ⇒ Susu.

Lihat isi setiap transaksi

transaksi <- list(
  T1 = c("Roti", "Susu"),
  T2 = c("Roti", "Susu", "Telur"),
  T3 = c("Roti", "Mentega"),
  T4 = c("Telur", "Beras"),
  T5 = c("Roti", "Susu", "Mentega"),
  T6 = c("Roti", "Susu"),
  T7 = c("Telur", "Beras"),
  T8 = c("Roti", "Telur"),
  T9 = c("Beras", "Kopi"),
  T10 = c("Roti", "Susu", "Telur")
)
barang <- c("Roti", "Susu", "Telur", "Beras", "Mentega", "Kopi")
isi_keranjang <- t(vapply(transaksi, function(x) {
  barang %in% x
}, logical(length(barang))))
colnames(isi_keranjang) <- barang
local({
  op <- par(mar = c(4, 4, 3, 1)); on.exit(par(op))
  plot.new()
  plot.window(xlim = c(0, length(barang)), ylim = c(0, length(transaksi)))
  for (i in seq_along(transaksi)) for (j in seq_along(barang)) {
    rect(j - 1, length(transaksi) - i, j, length(transaksi) - i + 1,
         col = if (isi_keranjang[i, j]) "#386B7D" else "#EDEFEA",
         border = "white", lwd = 3)
  }
  axis(1, at = seq_along(barang) - 0.5, labels = barang, tick = FALSE)
  axis(2, at = length(transaksi) - seq_along(transaksi) + 0.5,
       labels = names(transaksi), tick = FALSE, las = 1)
  title("Kotak gelap: barang ada di keranjang")
})

Satu baris = satu transaksi. Kotak gelap = barang dibeli.

Hitung dulu: berapa transaksi memuat roti, susu, dan keduanya?

Tiga ukuran untuk membaca satu aturan

Ukuran Pertanyaan
Support Berapa bagian dari seluruh transaksi memuat roti dan susu?
Confidence Dari transaksi berisi roti, berapa bagian juga berisi susu?
Lift Seberapa besar confidence dibandingkan proporsi susu pada seluruh transaksi?
ada_roti <- vapply(transaksi, function(x) "Roti" %in% x, logical(1))
ada_susu <- vapply(transaksi, function(x) "Susu" %in% x, logical(1))

n_transaksi <- length(transaksi)
n_roti <- sum(ada_roti)
n_susu <- sum(ada_susu)
n_keduanya <- sum(ada_roti & ada_susu)

support_roti_susu <- n_keduanya / n_transaksi
confidence_roti_susu <- n_keduanya / n_roti
lift_roti_susu <- confidence_roti_susu / (n_susu / n_transaksi)

c(Support = support_roti_susu,
  Confidence = confidence_roti_susu,
  Lift = lift_roti_susu)
##    Support Confidence       Lift 
##    0.50000    0.71429    1.42857
Roti ⇒ Susu Hasil
Support 5/10 = 0.50
Confidence 5/7 ≈ 0.714
Lift Confidence / (5/10) ≈ 1.43

Lift di atas satu menunjukkan kemunculan bersama lebih sering daripada jika kedua kejadian independen pada data ini. Panah aturan tidak menyatakan sebab-akibat atau urutan pembelian.

Bagaimana jika arah aturannya dibalik?

Fungsi berikut membuat perhitungan yang sama dapat digunakan untuk pasangan barang lain.

hitung_aturan <- function(A, B, keranjang = transaksi) {
  ada_A <- vapply(keranjang, function(x) A %in% x, logical(1))
  ada_B <- vapply(keranjang, function(x) B %in% x, logical(1))
  stopifnot(any(ada_A), any(ada_B))

  support <- mean(ada_A & ada_B)
  confidence <- support / mean(ada_A)
  lift <- confidence / mean(ada_B)

  data.frame(
    Aturan = paste(A, "=>", B),
    Support = support, Confidence = confidence, Lift = lift
  )
}

aturan_roti_susu <- hitung_aturan("Roti", "Susu")
aturan_susu_roti <- hitung_aturan("Susu", "Roti")
hasil_aturan <- rbind(aturan_roti_susu, aturan_susu_roti)
knitr::kable(hasil_aturan, digits = 3,
  col.names = c("Aturan", "Support", "Confidence", "Lift"))
Aturan Support Confidence Lift
Roti => Susu 0.5 0.714 1.429
Susu => Roti 0.5 1.000 1.429

Diskusi: support kedua arah sama. Mengapa confidence-nya berbeda? Tunjukkan penyebutnya dari data transaksi.

6. Reduksi dimensi: peta pelanggan dengan PCA

Pelanggan tadi memiliki empat fitur. PCA merangkumnya menjadi koordinat baru agar pola lebih mudah dilihat dalam dua sumbu.

PCA Yang perlu diingat
PC1 Kombinasi fitur yang menangkap variasi terbesar
PC2 Menangkap variasi terbesar berikutnya, dengan arah tegak lurus PC1
Sumbu baru Kombinasi fitur, sehingga tidak langsung bersatuan pendapatan atau belanja

Hitung komponen utama

Gunakan empat fitur asli. Nomor cluster tidak menjadi input PCA.

hasil_pca <- prcomp(
  pelanggan[fitur_pelanggan], center = TRUE, scale. = TRUE
)

proporsi_var <- hasil_pca$sdev^2 / sum(hasil_pca$sdev^2)
tabel_var <- data.frame(
  Komponen = paste0("PC", seq_along(proporsi_var)),
  Variasi_persen = 100 * proporsi_var,
  Kumulatif_persen = 100 * cumsum(proporsi_var)
)
skor_pca <- hasil_pca$x[, 1:2, drop = FALSE]
  • center = TRUE, scale. = TRUE: memusatkan dan menstandardisasi fitur.
  • sdev^2: variasi setiap komponen.
  • hasil_pca$x: koordinat pelanggan pada sumbu baru.

Banyaknya variasi dan peta dua dimensi

local({
  op <- par(mfrow = c(1, 2), mar = c(4.5, 4.5, 3, 1)); on.exit(par(op))
  tinggi <- 100 * proporsi_var
  posisi <- barplot(
    tinggi, names.arg = tabel_var$Komponen,
    col = c("#286CAA", "#28856A", "#BECBD5", "#D6DFE5"), border = NA,
    ylim = c(0, 1.2 * max(tinggi)), ylab = "Variasi (%)",
    xlab = "Komponen utama", main = "Variasi tiap komponen"
  )
  text(posisi, tinggi, sprintf("%.1f%%", tinggi), pos = 3, cex = 0.85)
  plot(skor_pca[, 1], skor_pca[, 2], pch = 16,
       col = warna_cluster[pelanggan$cluster],
       xlab = sprintf("PC1 (%.1f%% variasi)", 100 * proporsi_var[1]),
       ylab = sprintf("PC2 (%.1f%% variasi)", 100 * proporsi_var[2]),
       main = "Peta pelanggan")
  abline(h = 0, v = 0, lty = 3, col = "#CDD4D8")
  legend("topright", paste("Cluster", levels(pelanggan$cluster)),
         col = warna_cluster, pch = 16, bty = "n", cex = 0.8)
})

knitr::kable(tabel_var, digits = 2,
  col.names = c("Komponen", "Variasi (%)", "Kumulatif (%)"))
Komponen Variasi (%) Kumulatif (%)
PC1 92.97 92.97
PC2 3.57 96.54
PC3 2.23 98.77
PC4 1.23 100.00

PC1 + PC2 merangkum 96.54% variasi fitur yang distandardisasi.

  • Warna berasal dari k-means sebelumnya. PCA membentuk sumbu, bukan label cluster.
  • Variasi pada PC3 dan PC4 tidak tampil di peta dua dimensi.
  • Persentase variasi yang dipertahankan berbeda dari akurasi prediksi.

Diskusi: apa yang menjadi lebih mudah dilihat? Mengapa angka PC1 tidak bisa langsung disebut pendapatan dalam juta rupiah?

7. Latihan

  1. Klasifikasi: jelaskan satu jenis kesalahan pada confusion matrix dan hubungkan dengan akurasi model.
  2. Regresi: prediksi harga mobil dengan jarak tempuh 100.000 km. Jelaskan satuan dan makna RMSE-nya.
  3. Clustering: beri nama tiga segmen berdasarkan profil empat fiturnya. Jelaskan alasan standardisasi.
  4. Asosiasi: hitung Telur ⇒ Beras dan Beras ⇒ Telur. Jelaskan support dan confidence kedua arah.
  5. PCA: hitung persentase variasi yang tidak tampil pada PC1–PC2. Jelaskan manfaat dan keterbatasan peta tersebut.

Satu dataset, tiga tujuan: pada data toko, tentukan pendekatan untuk memprediksi jumlah belanja, memprediksi pelanggan kembali atau tidak, dan membentuk segmen pelanggan. Sebutkan target yang diperlukan.