if (!require("MASS")) install.packages("MASS")
## Loading required package: MASS
if (!require("caret")) install.packages("caret")
## Loading required package: caret
## Loading required package: ggplot2
## Loading required package: lattice
if (!require("tidyverse")) install.packages("tidyverse")
## Loading required package: tidyverse
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ lubridate 1.9.4 ✔ tibble 3.2.1
## ✔ purrr 1.0.4 ✔ tidyr 1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ✖ purrr::lift() masks caret::lift()
## ✖ dplyr::select() masks MASS::select()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
if (!require("Metrics")) install.packages("Metrics")
## Loading required package: Metrics
##
## Attaching package: 'Metrics'
##
## The following objects are masked from 'package:caret':
##
## precision, recall
library(MASS)
library(caret)
library(tidyverse)
library(Metrics)
Menginstal dan memuat library yang dibutuhkan untuk modeling, pra-pemrosesan data, visualisasi, dan evaluasi performa model.
df <- read.csv("C:/Users/ASUS/Documents/UNESA SEMESTER 4/ANMUL/student_performance.csv")
head (df)
## school sex age address famsize Pstatus Medu Fedu Mjob Fjob reason
## 1 GP F 18 U GT3 A 4 4 at_home teacher course
## 2 GP F 17 U GT3 T 1 1 at_home other course
## 3 GP F 15 U LE3 T 1 1 at_home other other
## 4 GP F 15 U GT3 T 4 2 health services home
## 5 GP F 16 U GT3 T 3 3 other other home
## 6 GP M 16 U LE3 T 4 3 services other reputation
## guardian traveltime studytime failures schoolsup famsup paid activities
## 1 mother 2 2 0 yes no no no
## 2 father 1 2 0 no yes no no
## 3 mother 1 2 0 yes no no no
## 4 mother 1 3 0 no yes no yes
## 5 father 1 2 0 no yes no no
## 6 mother 1 2 0 no yes no yes
## nursery higher internet romantic famrel freetime goout Dalc Walc health
## 1 yes yes no no 4 3 4 1 1 3
## 2 no yes yes no 5 3 3 1 1 3
## 3 yes yes yes no 4 3 2 2 3 3
## 4 yes yes yes yes 3 2 2 1 1 5
## 5 yes yes no no 4 3 2 1 2 5
## 6 yes yes yes no 5 4 2 1 2 5
## absences G1 G2 G3
## 1 4 0 11 11
## 2 2 9 11 11
## 3 6 12 13 12
## 4 0 14 14 14
## 5 0 11 13 13
## 6 6 12 12 13
Dataset yang digunakan merupakan data performa akademik siswa dari dua sekolah menengah di Portugal. Dataset ini terdiri dari 649 observasi dengan 33 variabel, yang mencakup:
Fitur demografis: usia, jenis kelamin, status tinggal, ukuran keluarga
Fitur sosial-ekonomi: pekerjaan dan pendidikan orang tua, dukungan keluarga, akses internet.
Fitur akademik: kehadiran, jam belajar, nilai G1 dan G2 (nilai awal), serta nilai akhir G3.
df$G3_kategori <- cut(
df$G3,
breaks = c(-1, 9, 11, 13, 15, 21),
labels = c("F", "D", "C", "B", "A"),
ordered_result = TRUE,
include.lowest = TRUE
)
df$G3_kategori <- factor(df$G3_kategori, ordered = TRUE)
df[sapply(df, is.character)] <- lapply(df[sapply(df, is.character)], as.factor)
Pada tahap pra-pemrosesan, nilai akhir siswa (G3) dikategorikan ke dalam lima kelas ordinal yaitu A (16–20), B (14–15), C (12–13), D (10–11), dan F (0–9) menggunakan fungsi cut(). Proses ini bertujuan untuk menyesuaikan data dengan pendekatan klasifikasi ordinal. Selain itu, seluruh variabel bertipe karakter diubah menjadi faktor agar dapat dikenali oleh model statistik. Distribusi kategori hasil transformasi ditampilkan, dan struktur data diperiksa untuk memastikan tipe data yang sesuai.
set.seed(123)
train_index <- createDataPartition(df$G3_kategori, p = 0.8, list = FALSE)
train_data <- df[train_index, ]
test_data <- df[-train_index, ]
cat("Jumlah data training:", nrow(train_data), "\n")
## Jumlah data training: 521
cat("Jumlah data testing :", nrow(test_data), "\n")
## Jumlah data testing : 128
cat("\nDistribusi kategori G3 pada training set:\n")
##
## Distribusi kategori G3 pada training set:
print(table(train_data$G3_kategori))
##
## F D C B A
## 80 161 124 90 66
cat("\nDistribusi kategori G3 pada testing set:\n")
##
## Distribusi kategori G3 pada testing set:
print(table(test_data$G3_kategori))
##
## F D C B A
## 20 40 30 22 16
prop.table(table(train_data$G3_kategori))
##
## F D C B A
## 0.1535509 0.3090211 0.2380038 0.1727447 0.1266795
prop.table(table(test_data$G3_kategori))
##
## F D C B A
## 0.156250 0.312500 0.234375 0.171875 0.125000
Kode ini digunakan untuk membagi dataset menjadi data latih (80%) dan data uji (20%) secara proporsional berdasarkan kategori nilai akhir siswa (G3_kategori). Fungsi createDataPartition() memastikan setiap kelas (A–F) tetap terwakili secara seimbang di kedua subset. Setelah pembagian, kode menampilkan jumlah data pada masing-masing subset, distribusi kelas secara absolut, dan juga proporsi tiap kelas. Hal ini dilakukan untuk memastikan bahwa pembagian data tidak menyebabkan ketimpangan distribusi kelas yang bisa mempengaruhi performa model.
# Drop G1 dan G2 untuk hindari kebocoran
olr_model <- polr(G3_kategori ~ . -G3 -G1 -G2, data = train_data, Hess = TRUE)
olr_preds <- predict(olr_model, newdata = test_data)
Model Ordinal Logistic Regression (OLR) menggunakan komponen PCA sebagai prediktor.
cat("=== Confusion Matrix OLR ===\n")
## === Confusion Matrix OLR ===
olr_cm <- confusionMatrix(olr_preds, test_data$G3_kategori)
print(olr_cm)
## Confusion Matrix and Statistics
##
## Reference
## Prediction F D C B A
## F 6 6 0 0 0
## D 10 17 9 9 4
## C 2 13 11 7 9
## B 0 4 7 2 1
## A 2 0 3 4 2
##
## Overall Statistics
##
## Accuracy : 0.2969
## 95% CI : (0.2194, 0.384)
## No Information Rate : 0.3125
## P-Value [Acc > NIR] : 0.6798
##
## Kappa : 0.074
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: F Class: D Class: C Class: B Class: A
## Sensitivity 0.30000 0.4250 0.36667 0.09091 0.12500
## Specificity 0.94444 0.6364 0.68367 0.88679 0.91964
## Pos Pred Value 0.50000 0.3469 0.26190 0.14286 0.18182
## Neg Pred Value 0.87931 0.7089 0.77907 0.82456 0.88034
## Prevalence 0.15625 0.3125 0.23438 0.17188 0.12500
## Detection Rate 0.04688 0.1328 0.08594 0.01562 0.01562
## Detection Prevalence 0.09375 0.3828 0.32812 0.10938 0.08594
## Balanced Accuracy 0.62222 0.5307 0.52517 0.48885 0.52232
# Konversi ke skor ordinal
ordinal_scores <- setNames(0:4, c("F", "D", "C", "B", "A"))
olr_true <- as.numeric(recode(as.character(test_data$G3_kategori), !!!ordinal_scores))
olr_pred <- as.numeric(recode(as.character(olr_preds), !!!ordinal_scores))
olr_mae <- mae(olr_true, olr_pred)
cat("OLR Mean Absolute Error (MAE):", olr_mae, "\n")
## OLR Mean Absolute Error (MAE): 1.023438
Kode ini digunakan untuk melakukan evaluasi model Ordinal Logistic Regression (OLR) yang diterapkan langsung pada fitur asli (tanpa transformasi PCA). Evaluasi dilakukan dengan dua pendekatan utama: menggunakan confusion matrix dan menghitung Mean Absolute Error (MAE).Langkah pertama adalah membentuk confusion matrix menggunakan fungsi confusionMatrix(), yang membandingkan hasil prediksi model (olr_preds) dengan label asli pada data uji (test_data$G3_kategori). Dari sini dapat diperoleh metrik performa seperti akurasi, sensitivitas, dan spesifisitas. Akurasi model menunjukkan seberapa sering prediksi sesuai dengan label sebenarnya, sehingga dapat menjadi indikator awal kualitas model klasifikasi.Selanjutnya, label kategori (F, D, C, B, A) dikonversi menjadi skor ordinal numerik (0 sampai 4). Berdasarkan skor tersebut, dihitung Mean Absolute Error (MAE), yaitu rata-rata jarak kesalahan prediksi terhadap label sebenarnya dalam skala ordinal. Nilai MAE yang diperoleh adalah sekitar 1.023, yang mengindikasikan bahwa rata-rata prediksi meleset lebih dari satu tingkat kategori dari nilai yang benar. Hal ini menunjukkan bahwa meskipun model telah memahami urutan kategori, tingkat presisi prediksinya masih perlu diperbaiki, khususnya dalam membedakan kelas dengan tingkat yang berdekatan.
cat("\n=== Fitur Penting OLR ===\n")
##
## === Fitur Penting OLR ===
summary(olr_model)$coefficients %>%
as.data.frame() %>%
arrange(desc(abs(Value))) %>%
head(10)
## Value Std. Error t value
## B|A 7.2148538 1.5861336 4.548705
## C|B 5.8461011 1.5749110 3.712020
## D|C 4.3732481 1.5667234 2.791334
## F|D 1.9862249 1.5618969 1.271675
## higheryes 1.4476162 0.3200244 4.523455
## schoolsupyes -1.3866753 0.2999807 -4.622548
## failures -1.1240721 0.1869584 -6.012419
## schoolMS -0.9533342 0.2266552 -4.206099
## sexM -0.9365568 0.2039557 -4.591961
## Mjobhealth 0.8934529 0.4230156 2.112104
Model Ordinal Logistic Regression (OLR) yang diterapkan langsung pada fitur asli menunjukkan performa yang kurang optimal dalam memprediksi kategori performa siswa. Meskipun OLR secara teori cocok untuk menangani data bertipe ordinal, hasil evaluasi menunjukkan bahwa model ini memiliki beberapa keterbatasan dalam praktik.
Nilai akurasi model berada di kisaran 35.94%, sedikit lebih tinggi dari No Information Rate (31.25%), yang berarti model lebih baik dari tebakan acak namun belum cukup kuat dalam klasifikasi yang presisi. Selain itu, nilai Mean Absolute Error (MAE) sebesar 1.023 mengindikasikan bahwa rata-rata prediksi model meleset lebih dari satu tingkat kategori dari nilai sebenarnya.
Analisis confusion matrix juga menunjukkan bahwa model mengalami kesulitan dalam membedakan kelas yang berdekatan, seperti kategori C dan B, yang memiliki karakteristik mirip. Hal ini mengurangi kemampuan model dalam memberikan prediksi yang tepat sasaran, meskipun secara umum pola urutan kategori tetap terbaca.
Dengan demikian, meskipun pendekatan OLR masih relevan untuk data ordinal, diperlukan peningkatan pada tahap pemodelan — misalnya dengan seleksi fitur yang lebih baik, tuning parameter, atau eksplorasi model alternatif — agar hasil prediksi lebih akurat dan andal.