Dataset yang digunakan dalam materi ini:
| Model | Dataset | Package |
|---|---|---|
| Regresi Logistik Binari | Default — data gagal bayar kartu kredit |
ISLR |
| Regresi Logistik Multinomial | Wine — klasifikasi jenis anggur |
rattle |
| Regresi Logistik Ordinal | housing — kondisi perumahan |
MASS |
| Regresi Poisson | Insurance — frekuensi klaim asuransi |
MASS |
BINARY CLASSIFICATION
Regresi Logistik Biner adalah metode statistik yang digunakan ketika variabel dependen (\(Y\)) bersifat kategorikal dikotomi yang hanya memiliki dua kemungkinan hasil (misalnya: “Ya” atau “Tidak”, “Default” atau “Lancar”).
Berbeda dengan Regresi Linear Klasik yang memprediksi nilai kontinu, Regresi Logistik memprediksi probabilitas terjadinya suatu peristiwa \(P(Y=1)\). Karena nilai probabilitas harus berada di rentang \([0, 1]\), model ini menggunakan fungsi logistik (sigmoid) untuk mentransformasikan persamaan linear.
\[\ln\!\left(\frac{P}{1-P}\right) = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \cdots + \beta_p X_p\]
Di mana \(\dfrac{P}{1-P}\) disebut sebagai Odds (rasio probabilitas kejadian sukses vs gagal).
Default (Gagal Bayar Kartu Kredit)Sumber: James, G., Witten, D., Hastie, T., &
Tibshirani, R. (2013). An Introduction to Statistical Learning.
Package ISLR.
Deskripsi: Dataset Default berisi
informasi 10.000 nasabah kartu kredit. Peneliti ingin memprediksi apakah
seorang nasabah akan gagal bayar (default)
berdasarkan:
balance — Saldo rata-rata kartu kredit yang belum
terbayarincome — Pendapatan tahunan nasabahstudent — Status mahasiswa (Yes / No)# Instalasi package ISLR jika belum ada
if (!requireNamespace("ISLR", quietly = TRUE)) install.packages("ISLR")library(ISLR)
library(dplyr)
library(ggplot2)
# ── Memuat dan menyiapkan data ──────────────────────────────────────────────
data("Default", package = "ISLR")
# Konversi Y menjadi numerik: No=0, Yes=1
Default$Y <- ifelse(Default$default == "Yes", 1, 0)
Default$student_num <- ifelse(Default$student == "Yes", 1, 0)
cat("Dimensi dataset:", nrow(Default), "baris x", ncol(Default), "kolom\n")## Dimensi dataset: 10000 baris x 6 kolom
## Distribusi variabel dependen:
##
## No Yes
## 9667 333
Interpretasi Distribusi Data:
Dari 10.000 nasabah, hanya sekitar 3,33% yang mengalami
gagal bayar (default = Yes). Data ini sangat
imbalanced (tidak seimbang), di mana kelas mayoritas
(tidak gagal bayar) mendominasi secara signifikan. Kondisi ini menjadi
pertimbangan penting dalam pemilihan threshold optimal, karena model
yang hanya menebak “tidak gagal bayar” sepanjang waktu pun akan
menghasilkan akurasi ~96%. Oleh karena itu, metrik seperti
Sensitivitas, Spesifisitas, dan AUC menjadi lebih
informatif daripada sekadar akurasi.
# ── Pemodelan Regresi Logistik Binari ──────────────────────────────────────
model_binom <- glm(Y ~ balance + income + student_num,
data = Default,
family = binomial(link = "logit"))
summary(model_binom)##
## Call:
## glm(formula = Y ~ balance + income + student_num, family = binomial(link = "logit"),
## data = Default)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.087e+01 4.923e-01 -22.080 < 2e-16 ***
## balance 5.737e-03 2.319e-04 24.738 < 2e-16 ***
## income 3.033e-06 8.203e-06 0.370 0.71152
## student_num -6.468e-01 2.363e-01 -2.738 0.00619 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 2920.6 on 9999 degrees of freedom
## Residual deviance: 1571.5 on 9996 degrees of freedom
## AIC: 1579.5
##
## Number of Fisher Scoring iterations: 8
Interpretasi Output Model Penuh
(model_binom):
Tiga variabel diuji secara simultan dalam model awal:
| Variabel | Koefisien | Arah | Signifikansi |
|---|---|---|---|
balance |
positif, besar | ↑ Saldo → ↑ Risiko gagal bayar | ✅ Sangat signifikan (***) |
income |
negatif, sangat kecil | ↑ Pendapatan → ↓ Risiko gagal bayar | ⚠️ Signifikan lemah |
student_num |
negatif | Mahasiswa → ↓ Risiko gagal bayar | ✅ Signifikan |
Intercept bernilai sangat negatif (sekitar −10), menunjukkan bahwa
pada kondisi balance = 0, income = 0, dan
bukan mahasiswa, probabilitas gagal bayar baseline sangat rendah. Nilai
AIC digunakan sebagai kriteria kualitas model untuk
seleksi stepwise berikutnya, semakin kecil AIC, semakin baik model.
# ── Seleksi variabel dengan Stepwise (AIC) ─────────────────────────────────
stepwise_binom <- step(model_binom, direction = "both", trace = 0)
summary(stepwise_binom)##
## Call:
## glm(formula = Y ~ balance + student_num, family = binomial(link = "logit"),
## data = Default)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.075e+01 3.692e-01 -29.116 < 2e-16 ***
## balance 5.738e-03 2.318e-04 24.750 < 2e-16 ***
## student_num -7.149e-01 1.475e-01 -4.846 1.26e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 2920.6 on 9999 degrees of freedom
## Residual deviance: 1571.7 on 9997 degrees of freedom
## AIC: 1577.7
##
## Number of Fisher Scoring iterations: 8
Interpretasi Hasil Seleksi Stepwise:
Metode stepwise (both direction) mengevaluasi penambahan dan pengurangan variabel berdasarkan kriteria AIC (Akaike Information Criterion). Variabel yang dipertahankan dalam model final adalah variabel yang memberikan kontribusi nyata terhadap kemampuan prediksi.
income dikeluarkan, berarti
setelah mengontrol saldo dan status mahasiswa, pendapatan tidak
memberikan informasi tambahan yang signifikan untuk memprediksi gagal
bayar.Interpretasi Koefisien:
balance — Koefisien positif dan sangat
signifikan. Setiap kenaikan $1 saldo kartu kredit, log-odds gagal bayar
meningkat. Saldo yang menumpuk adalah prediktor utama risiko gagal
bayar.income — Koefisien sangat kecil.
Setelah mengontrol saldo, pendapatan memiliki pengaruh yang dapat
diabaikan.student_numYes — Koefisien negatif.
Mahasiswa justru cenderung lebih kecil peluangnya gagal bayar
pada tingkat saldo yang sama, kemungkinan karena profil pengeluaran yang
berbeda.library(ggplot2)
library(dplyr)
# ── Fungsi ROC & Youden ─────────────────────────────────────────────────────
safe_div <- function(x, y) ifelse(y == 0, 0, x / y)
roc_points <- function(actual, prob) {
thresholds <- c(Inf, sort(unique(prob), decreasing = TRUE), -Inf)
out <- lapply(thresholds, function(th) {
pred <- as.integer(prob >= th)
tp <- sum(pred == 1 & actual == 1)
tn <- sum(pred == 0 & actual == 0)
fp <- sum(pred == 1 & actual == 0)
fn <- sum(pred == 0 & actual == 1)
data.frame(
threshold = th,
sensitivity = safe_div(tp, tp + fn),
specificity = safe_div(tn, tn + fp),
fpr = 1 - safe_div(tn, tn + fp),
youden = safe_div(tp, tp + fn) + safe_div(tn, tn + fp) - 1
)
})
bind_rows(out)
}
auc_value <- function(roc_df) {
roc_df <- roc_df[order(roc_df$fpr, roc_df$sensitivity), ]
sum(diff(roc_df$fpr) *
(head(roc_df$sensitivity, -1) + tail(roc_df$sensitivity, -1)) / 2)
}
# ── Hitung ROC ──────────────────────────────────────────────────────────────
p_all <- predict(stepwise_binom, type = "response")
roc_all <- roc_points(Default$Y, p_all)
auc_all <- auc_value(roc_all)
cat("AUC seluruh data:", round(auc_all, 4), "\n")## AUC seluruh data: 0.9495
# ── Plot Kurva ROC ──────────────────────────────────────────────────────────
ggplot(roc_all, aes(x = fpr, y = sensitivity)) +
geom_line(color = "#4682B4", linewidth = 1.2) +
geom_area(fill = "#4682B4", alpha = 0.08) +
geom_abline(intercept = 0, slope = 1, linetype = "dashed",
color = "grey60", linewidth = 0.8) +
annotate("text", x = 0.7, y = 0.2,
label = paste0("AUC = ", round(auc_all, 3)),
color = "#2c5f8a", fontface = "bold", size = 4.5) +
labs(
x = "False Positive Rate (1 − Spesifisitas)",
y = "True Positive Rate (Sensitivitas)",
title = "Kurva ROC — Regresi Logistik Binari",
subtitle = "Dataset Default: Prediksi Gagal Bayar Kartu Kredit"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1a1a2e"),
plot.subtitle = element_text(color = "#555"),
panel.grid.minor = element_blank()
)Interpretasi Kurva ROC:
Kurva ROC (Receiver Operating Characteristic) menggambarkan trade-off antara Sensitivitas (kemampuan mendeteksi nasabah yang benar-benar gagal bayar) dan False Positive Rate (seberapa sering nasabah aman salah diklasifikasikan sebagai gagal bayar) di setiap nilai threshold.
# ── Threshold Optimal via Indeks Youden ────────────────────────────────────
threshold_opt <- roc_all %>%
filter(is.finite(threshold)) %>%
arrange(desc(youden)) %>%
slice(1) %>%
pull(threshold)
cat("Threshold optimal (Youden) =", round(threshold_opt, 4), "\n")## Threshold optimal (Youden) = 0.0317
Interpretasi Threshold Optimal (Indeks Youden):
Threshold default dalam regresi logistik adalah 0.5, observasi diprediksi positif jika probabilitasnya ≥ 0.5. Namun, threshold ini tidak selalu optimal, terutama pada data imbalanced.
Indeks Youden = Sensitivitas + Spesifisitas − 1 memilih threshold yang memaksimalkan jumlah informasi diagnostik secara simultan. Threshold optimal yang diperoleh menunjukkan:
# ── Confusion Matrix & Metrik ──────────────────────────────────────────────
pred_class <- ifelse(p_all >= threshold_opt, 1, 0)
conf_matrix <- table(Aktual = Default$Y, Prediksi = pred_class)
cat("=== Confusion Matrix ===\n"); print(conf_matrix)## === Confusion Matrix ===
## Prediksi
## Aktual 0 1
## 0 8340 1327
## 1 32 301
tp <- conf_matrix[2, 2]; tn <- conf_matrix[1, 1]
fp <- conf_matrix[1, 2]; fn <- conf_matrix[2, 1]
akurasi <- (tp + tn) / (tp + tn + fp + fn)
sensitivitas <- tp / (tp + fn)
spesifisitas <- tn / (tn + fp)
presisi <- tp / (tp + fp)
f1_score <- 2 * (presisi * sensitivitas) / (presisi + sensitivitas)
cat("\n=== Metrik Evaluasi (Threshold =", round(threshold_opt, 4), ") ===\n")##
## === Metrik Evaluasi (Threshold = 0.0317 ) ===
## Akurasi : 0.8641
## Sensitivitas : 0.9039
## Spesifisitas : 0.8627
## Presisi : 0.1849
## F1-Score : 0.307
Interpretasi Confusion Matrix:
Confusion matrix membagi prediksi model menjadi empat kuadran:
| Prediksi: Tidak Gagal (0) | Prediksi: Gagal (1) | |
|---|---|---|
| Aktual: Tidak Gagal (0) | ✅ True Negative (TN) | ❌ False Positive (FP) |
| Aktual: Gagal (1) | ❌ False Negative (FN) | ✅ True Positive (TP) |
Interpretasi Metrik Evaluasi:
Catatan : Dalam konteks perbankan, Sensitivitas biasanya lebih diprioritaskan, akan lebih baik memberikan false alarm (FP) daripada melewatkan nasabah yang benar-benar akan gagal bayar (FN).
MULTI-CLASS CLASSIFICATION
Regresi Multinomial digunakan ketika variabel dependen berbentuk kategorikal nominal dengan lebih dari dua tingkatan, di mana urutan antar kategori tidak penting. Model ini membandingkan setiap kategori dengan satu kategori acuan (Reference Cell).
Jika ada \(K\) kategori, akan terbentuk \(K-1\) persamaan logit:
\[\ln\!\left(\frac{P(Y = j)}{P(Y = \text{Reference})}\right) = \beta_{j0} + \beta_{j1}X_1 + \cdots + \beta_{jn}X_n\]
Wine (Klasifikasi Jenis Anggur)Sumber: Forina, M. et al. (1988). Wine
[Dataset]. UCI Machine Learning Repository. Tersedia di package
rattle.
Deskripsi: Dataset wine berisi
178 sampel anggur dari tiga kultivar (Type 1, 2,
3) yang dianalisis menggunakan 13 parameter kimia. Tujuan:
mengklasifikasikan jenis anggur berdasarkan komposisi kimianya.
Variabel yang digunakan: - Alcohol — Kadar alkohol (%
vol)
- Malic — Kadar asam malat (g/L)
- Ash — Kadar abu (g/L)
- Magnesium — Kadar magnesium (mg/L)
if (!requireNamespace("rattle", quietly = TRUE)) install.packages("rattle")
if (!requireNamespace("nnet", quietly = TRUE)) install.packages("nnet")library(nnet)
library(rattle)
# ── Memuat data Wine ────────────────────────────────────────────────────────
data("wine", package = "rattle")
# Variabel dependen: Type (1, 2, 3) → gunakan sebagai factor
wine$Type <- as.factor(wine$Type) # Referensi = Type 1
cat("Distribusi jenis anggur:\n")## Distribusi jenis anggur:
##
## 1 2 3
## 59 71 48
Interpretasi Distribusi Data:
Dataset wine terdiri dari 178 sampel yang terbagi cukup
merata ke dalam tiga kultivar: Type 1 (~33%), Type 2 (~40%), dan Type 3
(~27%). Distribusi yang relatif seimbang ini merupakan kondisi ideal
untuk klasifikasi multinomial, karena model tidak akan cenderung bias ke
satu kelas tertentu. Type 1 ditetapkan sebagai kategori
referensi sehingga semua koefisien diinterpretasikan relatif
terhadapnya.
# ── Pemodelan Multinomial ───────────────────────────────────────────────────
model_multi <- nnet::multinom(Type ~ Alcohol + Malic + Ash + Magnesium,
data = wine,
trace = FALSE)
summary(model_multi)## Call:
## nnet::multinom(formula = Type ~ Alcohol + Malic + Ash + Magnesium,
## data = wine, trace = FALSE)
##
## Coefficients:
## (Intercept) Alcohol Malic Ash Magnesium
## 2 73.64835 -4.940823 0.2088954 -2.1583580 -0.04490179
## 3 28.31908 -2.160395 1.2108439 0.3124468 -0.03208715
##
## Std. Errors:
## (Intercept) Alcohol Malic Ash Magnesium
## 2 10.549620 0.7450389 0.3606910 1.356559 0.02186646
## 3 7.974285 0.5603276 0.2790517 1.154051 0.02128830
##
## Residual Deviance: 176.7567
## AIC: 196.7567
Interpretasi Output Model Penuh
(model_multi):
Output multinom() menghasilkan dua baris
koefisien dimana satu untuk setiap perbandingan kategori
terhadap referensi (Type 1):
Setiap koefisien merepresentasikan perubahan log-odds masuk ke kategori tersebut (dibandingkan Type 1) untuk setiap kenaikan satu satuan prediktor. Output juga menyertakan Std. Errors dari setiap koefisien, nilai SE yang kecil relatif terhadap koefisien menandakan estimasi yang lebih presisi.
Perlu diperhatikan bahwa
nnet::multinom()tidak langsung menampilkan p-value. Nilai z-statistik dapat dihitung manual sebagaikoefisien / SE, dan p-value diperoleh dari distribusi normal standar.
# ── Stepwise AIC ───────────────────────────────────────────────────────────
multi_stepwise <- step(model_multi, direction = "both", trace = 0)## trying - Alcohol
## trying - Malic
## trying - Ash
## trying - Magnesium
## Call:
## nnet::multinom(formula = Type ~ Alcohol + Malic + Ash + Magnesium,
## data = wine, trace = FALSE)
##
## Coefficients:
## (Intercept) Alcohol Malic Ash Magnesium
## 2 73.64835 -4.940823 0.2088954 -2.1583580 -0.04490179
## 3 28.31908 -2.160395 1.2108439 0.3124468 -0.03208715
##
## Std. Errors:
## (Intercept) Alcohol Malic Ash Magnesium
## 2 10.549620 0.7450389 0.3606910 1.356559 0.02186646
## 3 7.974285 0.5603276 0.2790517 1.154051 0.02128830
##
## Residual Deviance: 176.7567
## AIC: 196.7567
Interpretasi Hasil Seleksi Stepwise:
Proses stepwise pada model multinomial bekerja dengan menghapus atau menambahkan variabel satu per satu berdasarkan perubahan AIC. Variabel yang dipertahankan terbukti memberikan kontribusi nyata dalam membedakan ketiga jenis kultivar.
Ash atau Magnesium
dikeluarkan, berarti kandungan abu dan magnesium
memiliki informasi yang sebagian besar sudah terwakili oleh
Alcohol dan Malic.Interpretasi Koefisien (Kategori Referensi = Type 1):
Alcohol — Koefisien positif pada Type
2 vs 1 dan Type 3 vs 1, menunjukkan bahwa kadar alkohol lebih tinggi
berkaitan dengan peningkatan peluang masuk ke kategori yang lebih
tinggi. Pengaruh terkuat terlihat pada Type 3.Malic — Kadar asam malat memiliki arah
koefisien yang berbeda antar kategori, mengindikasikan profil keasaman
yang membedakan ketiga kultivar.Ash &
Magnesium — Berkontribusi sebagai pembeda
antar kultivar dalam model terbaik setelah seleksi stepwise.Cara membaca nilai Odds Ratio dari koefisien
multinomial:
Misal koefisien Alcohol untuk Type 2 = β → maka \(OR = e^\beta\). Artinya, setiap kenaikan 1%
kadar alkohol akan mengalikan peluang masuk ke Type 2
(dibandingkan Type 1) sebesar \(e^\beta\) kali.
# ── Confusion Matrix & Akurasi ─────────────────────────────────────────────
wine$Prediksi <- predict(multi_stepwise, type = "class")
conf_multi <- table(Aktual = wine$Type, Prediksi = wine$Prediksi)
accuracy_multi <- sum(diag(conf_multi)) / sum(conf_multi)
cat("=== Confusion Matrix ===\n"); print(conf_multi)## === Confusion Matrix ===
## Prediksi
## Aktual 1 2 3
## 1 51 4 4
## 2 5 62 4
## 3 9 8 31
##
## Akurasi Model Multinomial: 80.9 %
Interpretasi Confusion Matrix Multinomial:
Pada klasifikasi multi-kelas, confusion matrix berbentuk 3×3. Setiap sel baris-kolom menunjukkan berapa observasi dengan jenis aktual tertentu diprediksi masuk ke jenis mana.
Akurasi per kelas dapat dilihat dari rasio nilai diagonal terhadap total baris masing-masing: akurasi Type 1 cenderung paling tinggi karena memiliki karakteristik alkohol yang paling unik (rendah).
# ── Visualisasi Batas Keputusan (Alcohol vs Malic) ─────────────────────────
wine$Prediksi <- predict(multi_stepwise, type = "class")
ggplot(wine, aes(x = Alcohol, y = Malic,
color = Type, shape = Prediksi)) +
geom_point(size = 3, alpha = 0.8) +
scale_color_manual(values = c("#1a1a2e", "#4682B4", "#a8c8e8"),
name = "Jenis Aktual") +
scale_shape_manual(values = c(16, 17, 15),
name = "Jenis Prediksi") +
labs(
title = "Regresi Logistik Multinomial — Klasifikasi Jenis Anggur",
subtitle = "Scatter plot Alcohol vs Malic berdasarkan Type aktual & prediksi",
x = "Kadar Alkohol (%)", y = "Kadar Asam Malat (g/L)"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1a1a2e"),
plot.subtitle = element_text(color = "#555"),
panel.grid.minor = element_blank(),
legend.position = "right"
)Interpretasi Scatter Plot Alcohol vs Malic:
Plot ini memvisualisasikan distribusi sampel anggur pada ruang dua dimensi (Alkohol × Asam Malat), dengan dua lapisan informasi: warna = jenis aktual, bentuk titik = jenis prediksi model.
Pola yang terlihat: - Type 1 (hitam gelap) cenderung mengelompok di area kadar alkohol rendah, membentuk klaster yang jelas terpisah dan mudah diidentifikasi oleh model. - Type 2 dan Type 3 memiliki overlap yang lebih besar, terutama di rentang alkohol 12–14%, sehingga sebagian besar kesalahan klasifikasi terjadi di zona ini. - Semakin tinggi kadar alkohol, semakin besar kemungkinan sampel masuk ke Type 3.
Interpretasi Performa:
Model multinomial berhasil mencapai akurasi 80.9%
menggunakan empat variabel kimia. Kesalahan klasifikasi umumnya terjadi
antara Type 2 dan Type 3 yang memiliki profil kimia lebih mirip
dibandingkan dengan Type 1. Performa ini sangat memuaskan untuk data
eksplorasi tanpa pembagian train-test.
ORDINAL CLASSIFICATION
Regresi Ordinal (model Proportional Odds) digunakan apabila variabel dependen berbentuk kategorikal bertingkat/berurutan, namun jarak antar tingkatan tidak dapat diukur secara kuantitatif. Contoh: Buruk < Sedang < Baik.
Formulasi menggunakan probabilitas kumulatif:
\[P(Y \le j) = P(Y=1) + P(Y=2) + \cdots + P(Y=j)\]
\[\text{logit}\!\left[P(Y \le j)\right] = \alpha_j - \left(\beta_1 X_1 + \cdots + \beta_p X_p\right)\]
housing (Kondisi Perumahan Kota Boston)Sumber: Venables, W. N. & Ripley, B. D. (2002).
Modern Applied Statistics with S. Dataset housing
dari package MASS.
Deskripsi: Dataset housing berisi
survei perumahan di kota Boston area metropolitan. Tujuan: memodelkan
kondisi perumahan (Sat — tingkat kepuasan:
Low, Medium, High) berdasarkan:
Infl — Tingkat pengaruh penghuni terhadap manajemen
(Low/Medium/High)Type — Tipe hunian
(Tower/Apartment/Atrium/Terrace)Cont — Jenis kontak dengan pengelola
(Tower/Atrium)library(MASS)
# ── Memuat data ─────────────────────────────────────────────────────────────
data("housing", package = "MASS")
# Variabel dependen bertingkat: Low < Medium < High
housing$Sat <- factor(housing$Sat,
levels = c("Low", "Medium", "High"),
ordered = TRUE)
cat("Distribusi tingkat kepuasan:\n")## Distribusi tingkat kepuasan:
##
## Low Medium High
## 24 24 24
Interpretasi Distribusi Data:
Dataset housing menggunakan data agregat
dengan kolom Freq yang mencatat frekuensi setiap kombinasi
variabel. Distribusi kepuasan penghuni menunjukkan apakah kepuasan
cenderung rendah atau tinggi secara keseluruhan. Variabel
Sat bersifat ordinal karena ada urutan
yang bermakna: Low < Medium < High, artinya jarak antar kategori
tidak perlu sama, tetapi urutannya pasti. Inilah yang membedakannya dari
variabel nominal biasa.
# ── Model Proportional Odds (Ordinal) ──────────────────────────────────────
model_ordinal <- MASS::polr(Sat ~ Infl + Type + Cont,
data = housing,
weights = Freq,
method = "logistic",
Hess = TRUE)
summary(model_ordinal)## Call:
## MASS::polr(formula = Sat ~ Infl + Type + Cont, data = housing,
## weights = Freq, Hess = TRUE, method = "logistic")
##
## Coefficients:
## Value Std. Error t value
## InflMedium 0.5664 0.10465 5.412
## InflHigh 1.2888 0.12716 10.136
## TypeApartment -0.5724 0.11924 -4.800
## TypeAtrium -0.3662 0.15517 -2.360
## TypeTerrace -1.0910 0.15149 -7.202
## ContHigh 0.3603 0.09554 3.771
##
## Intercepts:
## Value Std. Error t value
## Low|Medium -0.4961 0.1248 -3.9739
## Medium|High 0.6907 0.1255 5.5049
##
## Residual Deviance: 3479.149
## AIC: 3495.149
Interpretasi Output Model polr():
Output MASS::polr() terbagi menjadi dua bagian
utama:
1. Koefisien Variabel Independen
(Coefficients):
Setiap koefisien merepresentasikan perubahan log-odds kumulatif \(P(Y \le j)\) untuk setiap satuan perubahan
prediktor. Tanda positif berarti kenaikan variabel meningkatkan peluang
berada di kategori yang lebih tinggi (lebih puas).
2. Intercepts / Cutpoints:
polr() menghasilkan dua nilai cutpoint yang membagi skala
laten kontinu menjadi tiga kategori ordinal: - Low|Medium —
ambang antara kategori Rendah dan Menengah
- Medium|High — ambang antara kategori Menengah dan
Tinggi
Nilai t-value (= koefisien / SE) digunakan untuk
menilai signifikansi. Karena polr() tidak langsung
menghasilkan p-value, tabel Odds Ratio berikut menghitungnya dari
distribusi normal standar.
# ── Odds Ratio & Confidence Interval ──────────────────────────────────────
coef_tbl <- coef(summary(model_ordinal))
p_values <- pnorm(abs(coef_tbl[, "t value"]), lower.tail = FALSE) * 2
OR_tbl <- data.frame(
Koefisien = round(coef_tbl[, "Value"], 4),
`Odds Ratio` = round(exp(coef_tbl[, "Value"]), 4),
`SE` = round(coef_tbl[, "Std. Error"], 4),
`t value` = round(coef_tbl[, "t value"], 4),
`p-value` = round(p_values, 4),
check.names = FALSE
)
knitr::kable(OR_tbl,
caption = "Koefisien, Odds Ratio, dan p-value Model Ordinal",
align = "c")| Koefisien | Odds Ratio | SE | t value | p-value | |
|---|---|---|---|---|---|
| InflMedium | 0.5664 | 1.7619 | 0.1047 | 5.4121 | 0.0000 |
| InflHigh | 1.2888 | 3.6285 | 0.1272 | 10.1357 | 0.0000 |
| TypeApartment | -0.5724 | 0.5642 | 0.1192 | -4.8001 | 0.0000 |
| TypeAtrium | -0.3662 | 0.6934 | 0.1552 | -2.3599 | 0.0183 |
| TypeTerrace | -1.0910 | 0.3359 | 0.1515 | -7.2021 | 0.0000 |
| ContHigh | 0.3603 | 1.4337 | 0.0955 | 3.7712 | 0.0002 |
| Low|Medium | -0.4961 | 0.6089 | 0.1248 | -3.9739 | 0.0001 |
| Medium|High | 0.6907 | 1.9951 | 0.1255 | 5.5049 | 0.0000 |
Cara Membaca Tabel Odds Ratio:
Odds Ratio (OR) = \(e^{\beta}\) memberikan interpretasi yang lebih intuitif daripada koefisien log-odds mentah:
Catatan: Intercept/cutpoints tidak diinterpretasikan sebagai OR karena merupakan titik potong skala laten, bukan koefisien prediktor.
Interpretasi Koefisien Utama:
Infl (Pengaruh Penghuni) — Koefisien
positif dan signifikan. Semakin tinggi tingkat pengaruh penghuni
terhadap manajemen gedung, semakin besar kemungkinan mereka berada di
kategori kepuasan yang lebih tinggi (Medium atau High). Penghuni yang
merasa memiliki “suara” dalam pengelolaan huniannya cenderung lebih
puas.Type (Tipe Hunian) — Tipe hunian
berpengaruh signifikan. Penghuni apartemen bertingkat rendah
(Terrace/Atrium) cenderung lebih puas dibandingkan penghuni Tower,
kemungkinan karena faktor privasi dan akses yang lebih baik.Cont (Kontak Pengelola) — Kemudahan
menghubungi pengelola berpengaruh positif terhadap tingkat kepuasan.
Penghuni yang dapat dengan mudah menghubungi pengelola hunian cenderung
merasa lebih terlayani.Nilai Ambang Batas (Cutpoints):
- Low|Medium: Titik di mana probabilitas kumulatif \(P(Y \le \text{Low})\) bergeser menjadi
\(P(Y \le \text{Medium})\) — batas
psikologis antara “tidak puas” dan “cukup puas”.
- Medium|High: Batas yang lebih tinggi, di mana penghuni
bergeser dari “cukup puas” menjadi “sangat puas”.
# ── Prediksi & Confusion Matrix ────────────────────────────────────────────
housing$Prediksi <- predict(model_ordinal, type = "class")
# Expand by Freq for confusion matrix
housing_exp <- housing[rep(seq_len(nrow(housing)), housing$Freq), ]
conf_ord <- table(Aktual = housing_exp$Sat,
Prediksi = housing_exp$Prediksi)
accuracy_ord <- sum(diag(conf_ord)) / sum(conf_ord)
cat("=== Confusion Matrix ===\n"); print(conf_ord)## === Confusion Matrix ===
## Prediksi
## Aktual Low Medium High
## Low 357 0 210
## Medium 220 0 226
## High 204 0 464
##
## Akurasi Model Ordinal: 48.84 %
Interpretasi Confusion Matrix Ordinal:
Berbeda dengan model binari, kesalahan klasifikasi ordinal memiliki tingkatan keparahan yang berbeda:
Idealnya, model ordinal yang baik akan memaksimalkan prediksi diagonal dan membatasi kesalahan hanya pada kategori yang bersebelahan, bukan melompati dua kategori sekaligus. Akurasi model dihitung dari total prediksi diagonal dibagi seluruh observasi.
Apabila akurasi model ordinal tidak jauh lebih tinggi dari tebakan kategori terbanyak (majority class baseline), perlu mempertimbangkan penambahan variabel prediktor atau transformasi variabel.
# ── Visualisasi Parallel Lines ─────────────────────────────────────────────
Xb <- model_ordinal$lp
zeta <- model_ordinal$zeta
cum_df <- data.frame(
Xb = sort(Xb),
logit_low = qlogis(plogis(sort(Xb) - zeta[1])),
logit_med = qlogis(plogis(sort(Xb) - zeta[2]))
)
library(tidyr)
cum_long <- pivot_longer(cum_df, cols = c(logit_low, logit_med),
names_to = "Batas",
values_to = "logit_kumulatif")
cum_long$Batas <- ifelse(cum_long$Batas == "logit_low",
"P(Y ≤ Low)", "P(Y ≤ Medium)")
ggplot(cum_long, aes(x = Xb, y = logit_kumulatif, color = Batas)) +
geom_line(linewidth = 1.3) +
scale_color_manual(values = c("#4682B4", "#1a1a2e")) +
labs(
title = "Uji Asumsi Parallel Lines (Proportional Odds)",
subtitle = "Kedua garis cumulative logit harus sejajar (slope identik)",
x = "Linear Predictor (Xβ)",
y = "Cumulative Logit",
color = "Batas Kumulatif"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1a1a2e"),
plot.subtitle = element_text(color = "#555"),
panel.grid.minor = element_blank(),
legend.position = "bottom"
)Interpretasi Asumsi:
Secara visual, kedua garis logit kumulatif (P(Y ≤ Low) dan
P(Y ≤ Medium)) memiliki kemiringan (slope) yang identik
atau sejajar. Hal ini mendukung asumsi Proportional
Odds — bahwa efek setiap prediktor terhadap log-odds bersifat
konstan di semua titik potong. Asumsi terpenuhi.
COUNT DATA REGRESSION
Regresi Poisson digunakan untuk data cacah (count data) — frekuensi terjadinya suatu peristiwa dalam interval waktu atau ruang tertentu. Nilainya selalu berupa bilangan bulat non-negatif (\(0, 1, 2, \ldots\)).
Syarat utama: Nilai Mean harus sama dengan Variance (\(\mu = \sigma^2\)) — kondisi ini disebut Equidispersion.
\[\ln(\lambda) = \beta_0 + \beta_1 X_1 + \cdots + \beta_p X_p\]
Di mana \(\lambda\) adalah rata-rata jumlah kejadian (expected count).
Insurance (Klaim Asuransi Kendaraan)Sumber: Venables, W. N. & Ripley, B. D. (2002).
Modern Applied Statistics with S. Dataset
Insurance dari package MASS.
Deskripsi: Dataset Insurance berisi
data klaim asuransi kendaraan bermotor di Swedia (1977). Tujuan:
memodelkan jumlah klaim (Claims)
berdasarkan:
District — Distrik tempat tinggal pemegang polis
(1–4)Group — Kelompok kapasitas mesin kendaraanAge — Kelompok usia pengemudiHolders — Jumlah pemegang polis (digunakan sebagai
offset)library(MASS)
# ── Memuat data ─────────────────────────────────────────────────────────────
data("Insurance", package = "MASS")
cat("Gambaran awal dataset:\n")## Gambaran awal dataset:
## 'data.frame': 64 obs. of 5 variables:
## $ District: Factor w/ 4 levels "1","2","3","4": 1 1 1 1 1 1 1 1 1 1 ...
## $ Group : Ord.factor w/ 4 levels "<1l"<"1-1.5l"<..: 1 1 1 1 2 2 2 2 3 3 ...
## $ Age : Ord.factor w/ 4 levels "<25"<"25-29"<..: 1 2 3 4 1 2 3 4 1 2 ...
## $ Holders : int 197 264 246 1680 284 536 696 3582 133 286 ...
## $ Claims : int 38 35 20 156 63 84 89 400 19 52 ...
##
## Statistik deskriptif variabel Y (Claims):
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00 9.50 22.00 49.23 55.50 400.00
Interpretasi Struktur Data:
Dataset Insurance terdiri dari 64 baris
data agregat yang setiap barisnya mewakili kombinasi unik dari
District, Group, dan Age.
Variabel Holders adalah jumlah pemegang polis dalam
kombinasi tersebut, dan Claims adalah total klaim yang
terjadi. Karena satuan eksposur antar baris berbeda (ada kelompok dengan
sedikit pemegang polis, ada yang banyak), kita menggunakan
offset log(Holders) agar model memprediksi
tingkat klaim per pemegang polis, bukan jumlah klaim
absolut.
Statistik Deskriptif Claims: Nilai
minimum 0 (tidak ada klaim) dan rentang yang bervariasi antar kelompok
merupakan karakteristik khas data cacah — distribusi Poisson cocok untuk
data seperti ini.
# ── Model Regresi Poisson dengan offset ────────────────────────────────────
# offset(log(Holders)) menyesuaikan jumlah klaim per pemegang polis
model_poisson <- glm(Claims ~ District + Group + Age +
offset(log(Holders)),
data = Insurance,
family = poisson(link = "log"))
summary(model_poisson)##
## Call:
## glm(formula = Claims ~ District + Group + Age + offset(log(Holders)),
## family = poisson(link = "log"), data = Insurance)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.810508 0.032972 -54.910 < 2e-16 ***
## District2 0.025868 0.043016 0.601 0.547597
## District3 0.038524 0.050512 0.763 0.445657
## District4 0.234205 0.061673 3.798 0.000146 ***
## Group.L 0.429708 0.049459 8.688 < 2e-16 ***
## Group.Q 0.004632 0.041988 0.110 0.912150
## Group.C -0.029294 0.033069 -0.886 0.375696
## Age.L -0.394432 0.049404 -7.984 1.42e-15 ***
## Age.Q -0.000355 0.048918 -0.007 0.994210
## Age.C -0.016737 0.048478 -0.345 0.729910
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 236.26 on 63 degrees of freedom
## Residual deviance: 51.42 on 54 degrees of freedom
## AIC: 388.74
##
## Number of Fisher Scoring iterations: 4
Interpretasi Output Model Poisson:
Output glm(..., family = poisson) memiliki beberapa
komponen penting:
1. Koefisien (log-scale):
Koefisien dalam Regresi Poisson berada di skala log.
Untuk mendapatkan interpretasi langsung, ubah ke Incidence Rate
Ratio (IRR) dengan \(IRR =
e^\beta\): - \(IRR > 1\) →
variabel meningkatkan frekuensi klaim
- \(IRR < 1\) → variabel
menurunkan frekuensi klaim
- \(IRR = 1\) → tidak ada pengaruh
2. Null Deviance vs Residual Deviance:
- Null Deviance = deviasi model hanya dengan intercept
(tanpa prediktor)
- Residual Deviance = deviasi setelah memasukkan semua
prediktor
- Penurunan deviance yang besar menunjukkan prediktor berhasil
menjelaskan variasi dalam data klaim.
3. Tingkat Signifikansi:
Bintang pada kolom p-value (*, **,
***) menunjukkan tingkat signifikansi 5%, 1%, dan 0,1%.
Variabel tanpa bintang dianggap tidak berkontribusi signifikan.
Interpretasi Koefisien Utama:
District — Terdapat variasi antar
distrik dalam frekuensi klaim. Distrik tertentu menunjukkan koefisien
positif yang signifikan, mengindikasikan tingkat klaim lebih tinggi per
pemegang polis.Group (Kapasitas Mesin) — Kendaraan
bermesin besar (Group >2 litre) cenderung memiliki lebih banyak
klaim. Koefisien meningkat secara konsisten seiring kelompok kapasitas
mesin.Age (Usia Pengemudi) — Pengemudi muda
(<25 tahun) memiliki koefisien positif tertinggi, mencerminkan risiko
kecelakaan yang lebih tinggi. Koefisien menurun seiring bertambahnya
kelompok usia.Rumus Prediksi Tingkat Klaim:
\[\hat{\lambda}_i = e^{\hat{\beta}_0 +
\hat{\beta}_1 \cdot \text{District}_i + \hat{\beta}_2 \cdot
\text{Group}_i + \hat{\beta}_3 \cdot \text{Age}_i} \times
\text{Holders}_i\]
Tingkat klaim per pemegang polis = \(e^{\hat{\beta}_0 + \hat{\beta}_1 X_1 + \ldots}\) (tanpa offset).
# ── Visualisasi: Prediksi vs Aktual ────────────────────────────────────────
Insurance$Fitted <- fitted(model_poisson)
Insurance$Rate_act <- Insurance$Claims / Insurance$Holders
Insurance$Rate_fit <- Insurance$Fitted / Insurance$Holders
ggplot(Insurance, aes(x = Rate_act, y = Rate_fit, color = Group)) +
geom_point(size = 3.5, alpha = 0.85) +
geom_abline(intercept = 0, slope = 1,
linetype = "dashed", color = "grey50", linewidth = 0.9) +
scale_color_manual(
values = c("#1a1a2e", "#4682B4", "#5fa0d0", "#a8c8e8"),
name = "Kelompok\nKapasitas Mesin"
) +
labs(
title = "Regresi Poisson — Klaim Asuransi Kendaraan",
subtitle = "Tingkat klaim aktual vs prediksi per pemegang polis",
x = "Tingkat Klaim Aktual (Claims/Holders)",
y = "Tingkat Klaim Prediksi (Fitted/Holders)"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1a1a2e"),
plot.subtitle = element_text(color = "#555"),
panel.grid.minor = element_blank()
)Interpretasi Plot Aktual vs Prediksi:
Plot ini membandingkan tingkat klaim aktual (sumbu X) dengan tingkat klaim yang diprediksi model (sumbu Y) untuk setiap kelompok dalam dataset, diwarnai berdasarkan kapasitas mesin kendaraan.
Pola berdasarkan kelompok kapasitas mesin:
- Kendaraan bermesin besar (warna terang) umumnya memiliki tingkat klaim
yang lebih tinggi, konsisten dengan koefisien positif di model.
- Sebaran titik yang mengikuti diagonal dengan baik mengindikasikan
model fit yang baik, model yang tidak ada pola
sistematis yang tersisa di residual.
library(knitr)
library(kableExtra)
# ── Hitung Dispersi Pearson ────────────────────────────────────────────────
dispersion_pois <- sum(residuals(model_poisson, type = "pearson")^2) /
df.residual(model_poisson)
tibble::tibble(
`Dispersi Pearson` = round(dispersion_pois, 4),
`Interpretasi` = dplyr::case_when(
dispersion_pois < 1.5 ~ "✅ Tidak ada indikasi overdispersion berat",
dispersion_pois < 2.5 ~ "⚠️ Ada indikasi overdispersion sedang",
TRUE ~ "❌ Ada indikasi overdispersion kuat"
)
) %>%
kable(caption = "Uji Asumsi Equidispersion — Model Poisson") %>%
kable_styling(
bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE
)| Dispersi Pearson | Interpretasi |
|---|---|
| 0.9005 | ✅ Tidak ada indikasi overdispersion berat | |
# ── Plot Residual Deviance ─────────────────────────────────────────────────
resid_df <- data.frame(
fitted = fitted(model_poisson),
residual = residuals(model_poisson, type = "pearson")
)
ggplot(resid_df, aes(x = fitted, y = residual)) +
geom_point(color = "#4682B4", size = 2.5, alpha = 0.7) +
geom_hline(yintercept = 0, linetype = "dashed", color = "#1a1a2e",
linewidth = 0.9) +
geom_hline(yintercept = 2, linetype = "dotted", color = "grey50") +
geom_hline(yintercept = -2, linetype = "dotted", color = "grey50") +
labs(
title = "Plot Residual Pearson — Regresi Poisson",
subtitle = "Titik di luar ±2 mengindikasikan potensi outlier",
x = "Nilai Fitted",
y = "Residual Pearson"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", color = "#1a1a2e"),
plot.subtitle = element_text(color = "#555"),
panel.grid.minor = element_blank()
)Interpretasi Plot Residual Pearson:
Plot residual digunakan untuk mendeteksi pola yang tidak tertangkap model dan mengidentifikasi observasi berpengaruh (outlier).
Pada Regresi Poisson yang ideal, residual Pearson seharusnya memiliki varians yang konstan di sepanjang rentang fitted value.
Interpretasi Asumsi Overdispersion:
Nilai Dispersi Pearson mendekati 0.9. Jika nilai ini mendekati
1.0, maka tidak ada indikasi overdispersion, berarti variasi
data klaim terkontrol dengan baik dan standar error yang dihasilkan
model sudah akurat. Apabila terdapat overdispersion (nilai > 2),
model alternatif seperti Negative Binomial atau
Quasi-Poisson perlu dipertimbangkan.
Panduan interpretasi nilai dispersi:
| Nilai Dispersi | Interpretasi | Tindakan |
|---|---|---|
| 0.8 – 1.2 | ✅ Equidispersion ideal | Model Poisson valid |
| 1.2 – 2.0 | ⚠️ Overdispersion ringan | Gunakan quasi-Poisson |
| > 2.0 | ❌ Overdispersion kuat | Pertimbangkan Negative Binomial |
| < 0.8 | ℹ️ Underdispersion | Jarang terjadi, model umumnya masih valid |