Keberhasilan akademik siswa di tingkat sekolah menengah merupakan indikator penting dalam mengevaluasi efektivitas proses pembelajaran dan penentu masa depan pendidikan siswa ke jenjang perguruan tinggi. Namun, tingkat kelulusan dan capaian nilai siswa dipengaruhi oleh interaksi kompleks dari berbagai faktor internal maupun eksternal, seperti alokasi waktu belajar mandiri, riwayat ketuntasan belajar di kelas sebelumnya, tingkat disiplin kehadiran, motivasi melanjutkan studi, serta ketersediaan program bimbingan belajar tambahan dari pihak sekolah.
Seringkali institusi pendidikan menghadapi tantangan dalam mendeteksi siswa yang berisiko tidak lulus sejak dini. Evaluasi berbasis nilai ujian akhir saja (summative assessment) bersifat reaktif karena intervensi perbaikan baru dapat dilakukan setelah kegagalan terjadi. Oleh karena itu, diperlukan suatu pendekatan statistik prediktif yang objektif dan berbasis bukti (evidence-based) untuk memodelkan probabilitas kelulusan siswa berdasarkan indikator-indikator perilaku belajar dan demografis akademis sebelum nilai akhir ditetapkan.
Penelitian ini berjudul: “Analisis Faktor-Faktor yang Memengaruhi Kelulusan Siswa Menggunakan Metode Regresi Logistik Biner”
Tujuan spesifik dari analisis ini adalah:
Memodelkan hubungan probabilistik antara status kelulusan siswa
(\(Y\)) dengan lima variabel prediktor:
waktu belajar mingguan (studytime), jumlah kegagalan kelas
sebelumnya (failures), jumlah ketidakhadiran
(absences), dukungan belajar tambahan sekolah
(schoolsup), dan keinginan melanjutkan pendidikan tinggi
(higher).
Melakukan pengujian signifikansi model secara simultan (Omnibus Likelihood Ratio Test) dan parsial (Wald Test).
Melakukan evaluasi diagnostik model secara menyeluruh (uji multikolinearitas VIF, uji kelayakan model Hosmer-Lemeshow, serta pemeriksaan konvergensi dan separasi).
Menginterpretasikan koefisien regresi logit dan nilai Odds Ratio (\(OR = e^{\beta}\)) secara substantif dengan memperhitungkan kategori referensi yang tepat.
Mengevaluasi performa klasifikasi model pada data testing (Confusion Matrix, Akurasi, Presisi, Sensitivitas, Spesifisitas, F1-Score, Kurva ROC, dan AUC).
Regresi logistik biner (binary logistic regression) merupakan metode analisis statistika yang digunakan ketika variabel dependen (\(Y\)) berskala dikotomi/biner (\(Y \in \{0, 1\}\)).
Probabilitas keberhasilan atau kelulusan siswa dinotasikan dengan: \[\pi(\mathbf{x}) = P(Y=1 \mid \mathbf{X} = \mathbf{x}) = \frac{e^{\beta_0 + \sum_{j=1}^k \beta_j X_j}}{1 + e^{\beta_0 + \sum_{j=1}^k \beta_j X_j}} = \frac{1}{1 + e^{-(\beta_0 + \sum_{j=1}^k \beta_j X_j)}}\]
Transformasi logit menghubungkan probabilitas kelulusan dengan kombinasi linier dari variabel penjelas: \[\text{logit}(\pi(\mathbf{x})) = \ln\left(\frac{\pi(\mathbf{x})}{1 - \pi(\mathbf{x})}\right) = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \dots + \beta_k X_k\]
Nilai \(\frac{\pi(\mathbf{x})}{1 - \pi(\mathbf{x})}\) disebut sebagai Odds (rasio kemungkinan lulus terhadap tidak lulus). Parameter \(\beta_j\) merepresentasikan perubahan pada nilai log-odds untuk setiap kenaikan satu satuan prediktor \(X_j\) (dengan asumsi variabel lain konstan). Besaran efek marjinal dinyatakan dalam Odds Ratio (OR): \[\text{OR}_j = \exp(\beta_j) = e^{\beta_j}\]
Estimasi parameter dilakukan dengan memaksimumkan fungsi kemungkinan log (Maximum Likelihood Estimation / MLE) secara numerik melalui algoritma Iteratively Reweighted Least Squares (IRLS).
Dataset yang digunakan berasal dari file student-por.csv
(Student Performance Dataset - Portuguese language course, Cortez &
Silva, 2008), yang mencakup data performa akademik siswa di dua sekolah
menengah di Portugal.
# 1. Memuat Library
required_packages <- c("tidyverse", "ggplot2", "dplyr", "car",
"ResourceSelection", "pROC", "caret", "broom", "knitr")
for (pkg in required_packages) {
if (requireNamespace(pkg, quietly = TRUE)) {
suppressPackageStartupMessages(library(pkg, character.only = TRUE))
}
}
# 2. Membaca Data
data_raw <- read.csv("student-por.csv", sep = ";", stringsAsFactors = FALSE)
# 3. Dimensi Data
cat("Dimensi Dataset:\n")## Dimensi Dataset:
## - Jumlah Baris (Observasi): 649
## - Jumlah Kolom (Variabel) : 33
# 4. Lima Baris Pertama
head(data_raw[, c("school", "sex", "age", "studytime", "failures", "schoolsup", "higher", "absences", "G1", "G2", "G3")], 5)# Pemeriksaan Missing Values
missing_vals <- colSums(is.na(data_raw))
cat("Total Missing Value pada Seluruh Kolom:", sum(missing_vals), "\n")## Total Missing Value pada Seluruh Kolom: 0
## Total Baris Duplikasi: 0
# Ringkasan Statistik Deskriptif Variabel Utama
summary(data_raw[, c("studytime", "failures", "absences", "schoolsup", "higher", "G3")])## studytime failures absences schoolsup
## Min. :1.000 Min. :0.0000 Min. : 0.000 Length:649
## 1st Qu.:1.000 1st Qu.:0.0000 1st Qu.: 0.000 Class :character
## Median :2.000 Median :0.0000 Median : 2.000 Mode :character
## Mean :1.931 Mean :0.2219 Mean : 3.659
## 3rd Qu.:2.000 3rd Qu.:0.0000 3rd Qu.: 6.000
## Max. :4.000 Max. :3.0000 Max. :32.000
## higher G3
## Length:649 Min. : 0.00
## Class :character 1st Qu.:10.00
## Mode :character Median :12.00
## Mean :11.91
## 3rd Qu.:14.00
## Max. :19.00
Hasil inspeksi membuktikan bahwa dataset bersih, memiliki 649 baris dan 33 kolom, tanpa missing value (\(NA = 0\)) dan tanpa baris duplikasi.
Sesuai dengan protokol penelitian yang telah ditetapkan:
Variabel respons dibentuk dari nilai ujian akhir periode ketiga
(G3): \[Y = \begin{cases} 1,
& \text{jika } G3 \ge 10 \quad (\text{Status: Lulus}) \\ 0, &
\text{jika } G3 < 10 \quad (\text{Status: Tidak Lulus})
\end{cases}\]
Model melibatkan 5 variabel independen:
studytime (\(X_1\)): Waktu belajar mingguan (skala 1:
<2 jam, 2: 2–5 jam, 3: 5–10 jam, 4: >10 jam;
numerik/ordinal).
failures (\(X_2\)):
Jumlah kegagalan kelas sebelumnya (\(n\) jika \(1 \le
n < 3\), else 4; numerik diskrit).
absences (\(X_3\)):
Jumlah hari ketidakhadiran di sekolah (numerik kontinu/cacah).
schoolsup (\(X_4\)): Bantuan dukungan belajar tambahan
dari sekolah (kategorik biner: "no",
"yes").
higher (\(X_5\)):
Keinginan melanjutkan ke jenjang perguruan tinggi (kategorik biner:
"no", "yes").
Batasan Ketat Metodologis: * Variabel
G3tidak dimasukkan sebagai prediktor karena merupakan dasar pembentukan \(Y\). * Variabel nilai periode pertama (G1) dan kedua (G2) tidak dimasukkan sebagai prediktor guna mencegah data leakage, karena tujuan model adalah memprediksi kelulusan murni berdasarkan faktor perilaku dan motivasi belajar, bukan nilai ujian perantara. * Variabelschoolsupdanhigherdiperlakukan secara tepat sebagai variabel kategorik (factor) dengan kategori referensi (baseline)"no".
# Pembentukan Variabel Respons Y
data_raw$Y <- ifelse(data_raw$G3 >= 10, 1, 0)
data_raw$status_kelulusan <- factor(
ifelse(data_raw$Y == 1, "Lulus", "Tidak Lulus"),
levels = c("Tidak Lulus", "Lulus")
)
# Menyiapkan Data Model
data_model <- data_raw[, c("studytime", "failures", "absences", "schoolsup", "higher", "Y")]
data_model$schoolsup <- factor(data_model$schoolsup, levels = c("no", "yes"))
data_model$higher <- factor(data_model$higher, levels = c("no", "yes"))
# Distribusi Frekuensi Y
tab_y <- table(data_raw$status_kelulusan)
prop_y <- prop.table(tab_y) * 100
data.frame(
Status_Kelulusan = names(tab_y),
Kode_Biner = c(0, 1),
Frekuensi = as.numeric(tab_y),
Persentase = paste0(round(as.numeric(prop_y), 2), " %")
)Distribusi variabel dependen menunjukkan bahwa sebanyak 549 siswa (84,59%) lulus dan 100 siswa (15,41%) tidak lulus. Terjadi ketidakseimbangan kelas (class imbalance) yang menuntut penggunaan penarikan sampel terstratifikasi (stratified sampling).
Berikut disajikan visualisasi eksplorasi data untuk memahami karakteristik distribusi variabel dan hubungannya dengan status kelulusan.
par(mfrow = c(2, 2), mar = c(4, 4, 3, 1))
# 1. Distribusi Status Kelulusan (Y)
bp_y <- barplot(
tab_y,
col = c("#E74C3C", "#2ECC71"),
main = "Distribusi Status Kelulusan (Y)",
xlab = "Status", ylab = "Jumlah Siswa",
ylim = c(0, 600), border = NA
)
text(x = bp_y, y = as.numeric(tab_y) + 30,
labels = paste0(as.numeric(tab_y), "\n(", round(as.numeric(prop_y), 1), "%)"), cex = 0.9)
grid(nx = NA, ny = NULL, col = "gray85")
# 2. Distribusi Waktu Belajar (studytime)
tab_st <- table(data_model$studytime)
barplot(
tab_st, col = "#3498DB",
main = "Distribusi Waktu Belajar (studytime)",
xlab = "Skala Studytime (1: <2j, 2: 2-5j, 3: 5-10j, 4: >10j)",
ylab = "Frekuensi", border = NA
)
grid(nx = NA, ny = NULL, col = "gray85")
# 3. Distribusi Kegagalan Kelas Sebelumnya (failures)
tab_fl <- table(data_model$failures)
barplot(
tab_fl, col = "#E67E22",
main = "Distribusi Kegagalan Sebelumnya (failures)",
xlab = "Jumlah Kegagalan", ylab = "Frekuensi", border = NA
)
grid(nx = NA, ny = NULL, col = "gray85")
# 4. Distribusi Ketidakhadiran (absences)
hist(
data_model$absences, breaks = 20, col = "#9B59B6", border = "white",
main = "Distribusi Ketidakhadiran (absences)",
xlab = "Hari Tidak Hadir", ylab = "Frekuensi"
)
grid(col = "gray85")par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))
# 5. Hubungan schoolsup dengan Status Kelulusan
tab_ss <- table(data_model$schoolsup, data_raw$status_kelulusan)
prop_ss <- prop.table(tab_ss, 1) * 100
barplot(
t(prop_ss), beside = TRUE, col = c("#E74C3C", "#2ECC71"),
main = "Kelulusan vs Dukungan Sekolah",
xlab = "Menerima schoolsup", ylab = "Persentase (%)",
ylim = c(0, 100), border = NA
)
legend("topright", legend = c("Tidak Lulus", "Lulus"), fill = c("#E74C3C", "#2ECC71"), bty = "n")
grid(nx = NA, ny = NULL, col = "gray85")
# 6. Hubungan higher dengan Status Kelulusan
tab_hg <- table(data_model$higher, data_raw$status_kelulusan)
prop_hg <- prop.table(tab_hg, 1) * 100
barplot(
t(prop_hg), beside = TRUE, col = c("#E74C3C", "#2ECC71"),
main = "Kelulusan vs Keinginan Kuliah",
xlab = "Ingin Melanjutkan Kuliah (higher)", ylab = "Persentase (%)",
ylim = c(0, 100), border = NA
)
legend("topleft", legend = c("Tidak Lulus", "Lulus"), fill = c("#E74C3C", "#2ECC71"), bty = "n")
grid(nx = NA, ny = NULL, col = "gray85")# 7. Perbandingan Variabel Numerik Berdasarkan Status Kelulusan
par(mfrow = c(1, 3), mar = c(4, 4, 3, 1))
boxplot(studytime ~ status_kelulusan, data = data_raw, col = c("#E74C3C", "#2ECC71"),
main = "studytime vs Kelulusan", xlab = "Status Kelulusan", ylab = "studytime (Skala 1-4)")
boxplot(failures ~ status_kelulusan, data = data_raw, col = c("#E74C3C", "#2ECC71"),
main = "failures vs Kelulusan", xlab = "Status Kelulusan", ylab = "Jumlah Failures")
boxplot(absences ~ status_kelulusan, data = data_raw, col = c("#E74C3C", "#2ECC71"),
main = "absences vs Kelulusan", xlab = "Status Kelulusan", ylab = "Jumlah Absences")Status Kelulusan: Sebanyak 84,6% siswa berada pada kategori Lulus (\(G3 \ge 10\)) dan 15,4% Tidak Lulus.
Waktu Belajar (studytime):
Mayoritas siswa mengalokasikan waktu belajar pada kategori 2 (2–5 jam
per minggu). Siswa yang lulus memperlihatkan median waktu belajar yang
cenderung lebih tinggi daripada siswa yang tidak lulus.
Riwayat Kegagalan (failures):
Sebagian besar siswa tidak pernah gagal kelas sebelumnya (nilai 0).
Namun, pada kelompok siswa yang tidak lulus, proporsi siswa dengan
riwayat kegagalan 1 hingga 3 kali tampak jauh lebih dominan.
Ketidakhadiran (absences):
Distribusi ketidakhadiran menceng ke kanan (right-skewed), di
mana mayoritas siswa memiliki ketidakhadiran 0 hingga 6 hari, dengan
nilai maksimum 32 hari.
Keinginan Melanjutkan Pendidikan Tinggi
(higher): Dari siswa yang ingin melanjutkan kuliah
(higher = yes), lebih dari 88% berhasil lulus. Sebaliknya,
pada siswa yang tidak berniat kuliah (higher = no),
persentase ketidaklulusan melonjak hingga hampir 48%.
Dukungan Belajar Tambahan
(schoolsup): Proporsi kelulusan pada siswa yang
menerima bantuan sekolah justru sedikit lebih rendah dibandingkan yang
tidak menerima, mengindikasikan bahwa program dukungan sekolah umumnya
dialokasikan kepada siswa yang memang sejak awal mengalami kesulitan
akademik.
Untuk memastikan validasi model dilakukan secara objektif tanpa bias
data leakage, data dibagi menjadi 80% Data Latih (Training) dan
20% Data Uji (Testing) menggunakan stratified sampling
berdasarkan variabel \(Y\) dengan
seed = 70.
set.seed(70)
idx_y1 <- which(data_model$Y == 1)
idx_y0 <- which(data_model$Y == 0)
train_idx_y1 <- sample(idx_y1, size = round(0.80 * length(idx_y1)))
train_idx_y0 <- sample(idx_y0, size = round(0.80 * length(idx_y0)))
train_indices <- sort(c(train_idx_y1, train_idx_y0))
train_data <- data_model[train_indices, ]
test_data <- data_model[-train_indices, ]
data.frame(
Himpunan_Data = c("Total Populasi", "Data Latih (Train 80%)", "Data Uji (Test 20%)"),
Jumlah_Observasi = c(nrow(data_model), nrow(train_data), nrow(test_data)),
Jumlah_Lulus = c(sum(data_model$Y), sum(train_data$Y), sum(test_data$Y)),
Proporsi_Lulus = paste0(round(c(mean(data_model$Y), mean(train_data$Y), mean(test_data$Y)) * 100, 2), " %"),
Proporsi_Tidak_Lulus = paste0(round(c(1 - mean(data_model$Y), 1 - mean(train_data$Y), 1 - mean(test_data$Y)) * 100, 2), " %")
)Proporsi kelulusan pada data latih (84,59%) dan data uji (84,62%) terjaga identik dengan populasi aslinya, membuktikan efektivitas stratifikasi.
Model diestimasi menggunakan fungsi glm() pada data
training dengan fungsi penghubung logit: \[\text{logit}(P(Y=1)) = \beta_0 + \beta_1 \cdot
\text{studytime} + \beta_2 \cdot \text{failures} + \beta_3 \cdot
\text{absences} + \beta_4 \cdot \text{schoolsup}_{\text{yes}} + \beta_5
\cdot \text{higher}_{\text{yes}}\]
model_null <- glm(Y ~ 1, data = train_data, family = binomial(link = "logit"))
model_full <- glm(Y ~ studytime + failures + absences + schoolsup + higher,
data = train_data, family = binomial(link = "logit"))
summary(model_full)##
## Call:
## glm(formula = Y ~ studytime + failures + absences + schoolsup +
## higher, family = binomial(link = "logit"), data = train_data)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.31088 0.44972 0.691 0.489403
## studytime 0.38424 0.19043 2.018 0.043614 *
## failures -0.93918 0.18492 -5.079 3.8e-07 ***
## absences -0.02834 0.02713 -1.045 0.296159
## schoolsupyes 0.12243 0.50896 0.241 0.809905
## higheryes 1.33555 0.35551 3.757 0.000172 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 446.16 on 518 degrees of freedom
## Residual deviance: 371.42 on 513 degrees of freedom
## AIC: 383.42
##
## Number of Fisher Scoring iterations: 5
Sebelum koefisien diinterpretasikan, model harus melalui serangkaian pengujian diagnostik untuk memverifikasi pemenuhan asumsi dan kelayakan model secara statistik.
Multikolinearitas terjadi jika prediktor memiliki korelasi linear yang kuat antar-sesamanya, yang dapat membiaskan estimasi standard error. Pedoman pengujian menyatakan nilai VIF atau \(\text{GVIF}^{1/(2 \times \text{Df})} < 5\) mengindikasikan bebas multikolinearitas.
if (requireNamespace("car", quietly = TRUE)) {
vif_table <- car::vif(model_full)
vif_df <- data.frame(
Variabel = names(vif_table),
VIF = round(as.numeric(vif_table), 4),
Status = ifelse(vif_table < 5, "Bebas Multikolinearitas (VIF < 5)", "Kolinearitas Tinggi")
)
} else {
X_mat <- model.matrix(model_full)[, -1]
vif_vals <- sapply(1:ncol(X_mat), function(j) 1 / (1 - summary(lm(X_mat[, j] ~ X_mat[, -j]))$r.squared))
vif_df <- data.frame(
Variabel = colnames(X_mat),
VIF = round(vif_vals, 4),
Status = ifelse(vif_vals < 5, "Bebas Multikolinearitas (VIF < 5)", "Kolinearitas Tinggi")
)
}
vif_dfInterpretasi Multikolinearitas: Seluruh variabel prediktor memiliki nilai VIF \(< 1{,}20\) (jauh di bawah batas toleransi 5). Hal ini menegaskan bahwa tidak ada multikolinearitas antar prediktor, sehingga estimasi koefisien dan kesalahan baku bersifat stabil dan efisien.
Uji signifikansi simultan menguji apakah kelima prediktor secara bersama-sama memengaruhi probabilitas kelulusan siswa dibandingkan model tanpa prediktor (intercept-only).
g_stat <- model_null$deviance - model_full$deviance
df_g <- model_null$df.residual - model_full$df.residual
p_val_g <- pchisq(g_stat, df = df_g, lower.tail = FALSE)
cat(sprintf("Statistik G (Chi-Square) = %.4f | df = %d | p-value = %e\n", g_stat, df_g, p_val_g))## Statistik G (Chi-Square) = 74.7419 | df = 5 | p-value = 1.053112e-14
Keputusan Uji Simultan: Nilai statistik \(G = 74{,}7419\) dengan \(df = 5\) menghasilkan \(p\)-value = \(1{,}053 \times 10^{-14} < 0{,}05\). Maka, Tolak \(H_0\). Secara simultan, kelima variabel penjelas memberikan pengaruh yang sangat signifikan terhadap probabilitas kelulusan siswa.
Uji Wald mengevaluasi signifikansi kontribusi individual masing-masing koefisien terhadap model: * Hipotesis: \(H_0: \beta_j = 0\) vs \(H_1: \beta_j \neq 0\) * Statistik Uji: \(Z = \frac{\hat{\beta}_j}{\text{SE}(\hat{\beta}_j)} \sim N(0, 1)\)
summary_coef <- summary(model_full)$coefficients
data.frame(
Variabel = rownames(summary_coef),
Estimasi_Beta = round(summary_coef[, 1], 4),
Std_Error = round(summary_coef[, 2], 4),
Wald_Z = round(summary_coef[, 3], 3),
P_Value = format.pval(summary_coef[, 4], digits = 4),
Keputusan = ifelse(summary_coef[, 4] < 0.05, "Signifikan (Tolak H0)", "Tidak Signifikan (Gagal Tolak H0)")
)Keputusan Uji Parsial (\(\alpha = 0{,}05\)): * Variabel yang signifikan:
failures(\(p < 0{,}001\)),higher(\(p < 0{,}001\)), danstudytime(\(p = 0{,}0436\)). * Variabel yang tidak signifikan:absences(\(p = 0{,}2962\)) danschoolsup(\(p = 0{,}8099\)).
Uji Hosmer-Lemeshow membagi data ke dalam \(g = 10\) kelompok desil berdasarkan probabilitas terprediksi \(\hat{\pi}_i\) untuk menguji apakah frekuensi teramati sesuai dengan ekspektasi model.
if (requireNamespace("ResourceSelection", quietly = TRUE)) {
hl_res <- ResourceSelection::hoslem.test(train_data$Y, model_full$fitted.values, g = 10)
hl_stat <- hl_res$statistic
hl_df <- hl_res$parameter
hl_pval <- hl_res$p.value
} else {
brks <- unique(quantile(model_full$fitted.values, probs = seq(0, 1, length.out = 11)))
groups <- cut(model_full$fitted.values, breaks = brks, include.lowest = TRUE)
obs_1 <- tapply(train_data$Y, groups, sum)
obs_0 <- tapply(1 - train_data$Y, groups, sum)
exp_1 <- tapply(model_full$fitted.values, groups, sum)
exp_0 <- tapply(1 - model_full$fitted.values, groups, sum)
hl_stat <- sum((obs_1 - exp_1)^2 / exp_1 + (obs_0 - exp_0)^2 / exp_0, na.rm = TRUE)
hl_df <- length(brks) - 3
hl_pval <- pchisq(hl_stat, df = hl_df, lower.tail = FALSE)
}
cat("Statistik Hosmer-Lemeshow Chi-Square (X-squared):", round(hl_stat, 4), "\n")## Statistik Hosmer-Lemeshow Chi-Square (X-squared): 9.5689
## Derajat Bebas (df) : 8
## P-value : 0.2966
Keputusan & Keterbatasan Uji Hosmer-Lemeshow: Karena \(p\)-value = \(0{,}2966 > 0{,}05\), maka Gagal Tolak \(H_0\). Tidak terdapat bukti yang cukup untuk menyatakan model tidak cocok, sehingga model regresi logistik biner ini memiliki kebaikan suai (goodness of fit) yang memadai.
Keterbatasan Pengujian: Uji Hosmer-Lemeshow sensitif terhadap pemilihan jumlah grup desil (\(g\)) dan adanya nilai probabilitas prediksi yang identik (ties). Oleh karena itu, hasil uji HL harus didukung oleh metrik diskriminasi kurva ROC dan evaluasi matriks klasifikasi.
Pada regresi logistik biner, kondisi complete separation atau quasi-complete separation (kondisi di mana suatu prediktor memisahkan kelas 0 dan 1 secara sempurna) dapat menyebabkan estimasi koefisien membengkak menuju tak hingga dan standar error menjadi sangat besar (exploding SE).
## Status Konvergensi Algoritma IRLS : TRUE
## Jumlah Iterasi Fisher Scoring Dilakukan : 5
## Rentang Probabilitas Prediksi Minimum : 0.0785
## Rentang Probabilitas Prediksi Maksimum : 0.9646
Evaluasi Konvergensi: Model berhasil mencapai konvergensi hanya dalam 5 iterasi tanpa mengeluarkan warning numerik. Nilai probabilitas prediksi berada dalam rentang wajar (\(0{,}0785\) hingga \(0{,}9646\)) dan seluruh standar error memiliki besaran yang wajar (\(\text{SE} \le 0{,}51\)). Dengan demikian, tidak terjadi masalah separasi data.
Tabel berikut menyajikan ringkasan lengkap parameter model: estimasi koefisien (\(\hat{\beta}\)), kesalahan baku (Standard Error), statistik uji Wald (\(Z\)), nilai \(p\), Odds Ratio (\(OR = e^{\hat{\beta}}\)), dan Interval Kepercayaan 95% untuk Odds Ratio: \[\text{CI}_{95\%}(OR) = \left[ \exp(\hat{\beta} - 1{,}96 \times \text{SE}), \; \exp(\hat{\beta} + 1{,}96 \times \text{SE}) \right]\]
coef_est <- coef(model_full)
se_est <- summary_coef[, 2]
wald_z <- summary_coef[, 3]
p_vals <- summary_coef[, 4]
or_val <- exp(coef_est)
ci_calc <- exp(confint.default(model_full))
table_interpretasi <- data.frame(
Variabel = names(coef_est),
Koefisien_Beta = round(coef_est, 4),
Std_Error = round(se_est, 4),
Wald_Z = round(wald_z, 3),
Odds_Ratio = round(or_val, 4),
P_Value = format.pval(p_vals, digits = 4),
CI_95_Lower = round(ci_calc[, 1], 4),
CI_95_Upper = round(ci_calc[, 2], 4),
Signifikansi = ifelse(p_vals < 0.001, "***",
ifelse(p_vals < 0.01, "**",
ifelse(p_vals < 0.05, "*", "ns")))
)
table_interpretasi# Forest Plot Odds Ratio
plot_df <- table_interpretasi[table_interpretasi$Variabel != "(Intercept)", ]
par(mar = c(5, 8, 4, 2))
plot(
plot_df$Odds_Ratio, 1:nrow(plot_df), pch = 19, col = "#2980B9", cex = 1.3,
xlim = c(0, 8.5), ylim = c(0.5, nrow(plot_df) + 0.5), yaxt = "n",
xlab = "Odds Ratio (OR) dan 95% Confidence Interval", ylab = "",
main = "Forest Plot Odds Ratio Prediktor Kelulusan Siswa"
)
axis(2, at = 1:nrow(plot_df), labels = plot_df$Variabel, las = 2)
abline(v = 1, col = "#E74C3C", lty = 2, lwd = 1.5)
for (i in 1:nrow(plot_df)) {
lines(c(plot_df$CI_95_Lower[i], plot_df$CI_95_Upper[i]), c(i, i), col = "#2980B9", lwd = 2)
}
grid(nx = NULL, ny = NA)studytime)failures)absences)schoolsup)"no" (siswa yang
tidak menerima dukungan belajar tambahan).higher)"no" (siswa yang
tidak berniat melanjutkan ke perguruan tinggi).higher = yes) memiliki odds kelulusan 3,8021 kali
lebih tinggi (atau 280,21% lebih tinggi) dibandingkan siswa
yang tidak berniat melanjutkan kuliah, dengan asumsi variabel lain
konstan. Variabel ini menjadi prediktor pendorong kelulusan terkuat
dalam model.Performa generalisasi model dievaluasi secara independen pada Data Uji (Testing Set) (\(N = 130\) observasi) yang belum pernah dilihat saat pelatihan.
# Prediksi probabilitas pada data uji
prob_test <- predict(model_full, newdata = test_data, type = "response")
actual_test <- test_data$Y
# Klasifikasi dengan Threshold Awal 0.50
pred_class_50 <- ifelse(prob_test >= 0.50, 1, 0)
cm_test <- table(Aktual = actual_test, Prediksi = pred_class_50)
rownames(cm_test) <- c("Aktual: 0 (Tidak Lulus)", "Aktual: 1 (Lulus)")
colnames(cm_test) <- c("Prediksi: 0 (Tidak Lulus)", "Prediksi: 1 (Lulus)")
cm_test## Prediksi
## Aktual Prediksi: 0 (Tidak Lulus) Prediksi: 1 (Lulus)
## Aktual: 0 (Tidak Lulus) 4 16
## Aktual: 1 (Lulus) 2 108
tp <- sum(actual_test == 1 & pred_class_50 == 1)
tn <- sum(actual_test == 0 & pred_class_50 == 0)
fp <- sum(actual_test == 0 & pred_class_50 == 1)
fn <- sum(actual_test == 1 & pred_class_50 == 0)
acc_val <- (tp + tn) / length(actual_test)
prec_val <- tp / (tp + fp)
rec_val <- tp / (tp + fn) # Sensitivity / Recall
spec_val <- tn / (tn + fp)
f1_val <- 2 * prec_val * rec_val / (prec_val + rec_val)
data.frame(
Metrik_Kinerja = c("Accuracy", "Precision", "Recall / Sensitivity", "Specificity", "F1-Score"),
Nilai_Desimal = round(c(acc_val, prec_val, rec_val, spec_val, f1_val), 4),
Persentase = paste0(round(c(acc_val, prec_val, rec_val, spec_val) * 100, 2), " %", c("", "", "", "", " (skala 0-1)"))
)th_grid <- seq(0, 1, by = 0.005)
tpr_seq <- numeric(length(th_grid))
fpr_seq <- numeric(length(th_grid))
for (k in seq_along(th_grid)) {
p_cl <- ifelse(prob_test >= th_grid[k], 1, 0)
tpr_seq[k] <- sum(p_cl == 1 & actual_test == 1) / sum(actual_test == 1)
fpr_seq[k] <- sum(p_cl == 1 & actual_test == 0) / sum(actual_test == 0)
}
ord_roc <- order(fpr_seq)
fpr_s <- fpr_seq[ord_roc]
tpr_s <- tpr_seq[ord_roc]
auc_val <- sum(diff(fpr_s) * (tpr_s[-1] + tpr_s[-length(tpr_s)]) / 2)
plot(
fpr_s, tpr_s, type = "l", col = "#2980B9", lwd = 3,
main = paste0("Kurva ROC pada Data Testing (AUC = ", round(auc_val, 4), ")"),
xlab = "False Positive Rate (1 - Spesifisitas)",
ylab = "True Positive Rate (Sensitivitas)",
xlim = c(0, 1), ylim = c(0, 1)
)
abline(a = 0, b = 1, col = "gray50", lty = 2, lwd = 1.5)
points(1 - spec_val, rec_val, col = "#E74C3C", pch = 19, cex = 1.5)
text(1 - spec_val + 0.16, rec_val - 0.05,
labels = paste0("Threshold 0.50\nSens: ", round(rec_val, 2), "\nSpec: ", round(spec_val, 2)),
col = "#E74C3C", cex = 0.85)
grid()Accuracy (86,15%): Model berhasil memprediksi status kelulusan dengan tepat pada 112 dari 130 siswa di data testing.
Sensitivity / Recall (98,18%): Model mampu mendeteksi 108 dari 110 siswa yang lulus secara tepat (hanya 2 siswa lulus yang salah diklasifikasikan sebagai tidak lulus).
Precision (87,10%): Dari 124 siswa yang diprediksi lulus oleh model, sebanyak 108 siswa faktualnya benar-benar lulus.
Specificity (20,00%): Pada threshold standar 0,50, spesifisitas relatif rendah karena sifat ketidakseimbangan kelas populasi (84,6% lulus), di mana model cenderung agresif memprediksi lulus.
F1-Score (0,9231): Menunjukkan keharmonisan performa presisi dan recall yang sangat tinggi untuk kelas mayoritas.
Area Under ROC (AUC = 0,8032): Berdasarkan standar klasifikasi Hosmer & Lemeshow (\(0{,}80 \le \text{AUC} < 0{,}90\)), nilai AUC sebesar 0,8032 mengindikasikan kemampuan diskriminasi model yang sangat baik (excellent discrimination) dalam membedakan siswa yang akan lulus dan tidak lulus.
Berdasarkan keseluruhan tahapan pengujian dan estimasi empiris, dapat ditarik beberapa kesimpulan penting:
Hasil Pengujian Simultan (Omnibus LRT / \(G\)-test): Model regresi logistik biner yang dibangun terbukti signifikan secara simultan (\(G = 74{,}7419, \; df = 5, \; p\text{-value} = 1{,}053 \times 10^{-14} < 0{,}05\)), membuktikan bahwa kombinasi kelima variabel penjelas secara bersama-sama memberikan kontribusi nyata terhadap kelulusan siswa.
Hasil Pengujian Parsial (Wald Test):
higher (\(p =
0{,}00017\)): Keinginan melanjutkan ke perguruan tinggi
berhubungan positif sangat kuat dengan kelulusan.failures (\(p = 3{,}80 \times
10^{-7}\)): Riwayat kegagalan kelas sebelumnya berhubungan
negatif sangat kuat dengan kelulusan.studytime (\(p =
0{,}0436\)): Waktu belajar mingguan berhubungan positif dengan
kelulusan.absences (\(p
= 0{,}2962\)) dan schoolsup (\(p = 0{,}8099\)).Hasil Diagnosis Multikolinearitas: Seluruh prediktor memiliki nilai VIF \(< 1{,}20\), membuktikan bahwa model terbebas dari masalah multikolinearitas.
Hasil Uji Kelayakan Model (Goodness of Fit): Uji Hosmer-Lemeshow menghasilkan statistik \(\chi^2 = 9{,}5689\) (\(df = 8, \; p\text{-value} = 0{,}2966 > 0{,}05\)) yang mengindikasikan bahwa model memiliki kecocokan (goodness of fit) yang memadai terhadap data.
Hasil Evaluasi Klasifikasi pada Data Testing: Model menghasilkan performa generalisasi yang solid pada data testing independen: Akurasi 86,15%, Recall 98,18%, F1-Score 0,9231, dan nilai AUC = 0,8032 (excellent discrimination).
Variabel yang Paling Kuat Hubungannya dengan Kelulusan:
higher = yes) dengan Odds Ratio
= 3,8021 (odds kelulusan meningkat 280,21%). Siswa yang
memiliki orientasi masa depan akademik yang jelas memiliki dorongan
internal yang kuat untuk menyelesaikan studinya.failures) dengan Odds Ratio =
0,3909 (odds kelulusan merosot 60,91% untuk setiap kegagalan
masa lalu). Riwayat kegagalan merupakan sinyal bahaya utama (red
flag) yang membutuhkan intervensi pedagogis dini.Keterbatasan Penelitian: