Persiapan Data

Load Library

pkgs <- c("tidyverse", "car", "MVN", "ggpubr", "rstatix", "nortest", "moments",
          "knitr", "kableExtra", "psych", "heplots", "effectsize", "emmeans", "magrittr")
invisible(lapply(pkgs, function(p)
  if (!requireNamespace(p, quietly = TRUE)) install.packages(p)))
invisible(lapply(pkgs, library, character.only = TRUE))

Import Data

data <- read.csv("student_performance.csv", sep = ",")
str(data)
## 'data.frame':    14003 obs. of  16 variables:
##  $ StudyHours          : int  19 19 19 19 19 19 19 19 19 19 ...
##  $ Attendance          : int  64 64 64 64 64 64 64 64 64 64 ...
##  $ Resources           : int  1 1 1 1 1 1 0 0 0 1 ...
##  $ Extracurricular     : int  0 0 0 1 1 1 1 1 1 1 ...
##  $ Motivation          : int  0 0 0 0 0 0 0 0 0 1 ...
##  $ Internet            : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Gender              : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ Age                 : int  19 23 28 19 23 28 19 23 28 19 ...
##  $ LearningStyle       : int  2 3 1 2 3 1 2 3 1 2 ...
##  $ OnlineCourses       : int  8 16 19 8 16 19 8 16 19 8 ...
##  $ Discussions         : int  1 0 0 1 0 0 1 0 0 1 ...
##  $ AssignmentCompletion: int  59 90 67 59 90 67 59 90 67 59 ...
##  $ ExamScore           : int  40 66 99 40 66 99 40 66 99 40 ...
##  $ EduTech             : int  0 0 1 0 0 1 0 0 1 0 ...
##  $ StressLevel         : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ FinalGrade          : int  3 2 0 3 2 0 3 2 0 3 ...

Data Cleaning

Cek Missing Value

colSums(is.na(data))
##           StudyHours           Attendance            Resources 
##                    0                    0                    0 
##      Extracurricular           Motivation             Internet 
##                    0                    0                    0 
##               Gender                  Age        LearningStyle 
##                    0                    0                    0 
##        OnlineCourses          Discussions AssignmentCompletion 
##                    0                    0                    0 
##            ExamScore              EduTech          StressLevel 
##                    0                    0                    0 
##           FinalGrade 
##                    0

Transformasi Variabel

# Semua kolom kategorikal berisi angka (0,1,2,...) — mapping ke label
# Gender       : 0=Female, 1=Male
# LearningStyle: 0=Visual, 1=Auditory, 2=Reading, 3=Kinesthetic
# Extracurricular/Internet/EduTech: 0=No, 1=Yes
# Motivation/StressLevel: 0=Low, 1=Medium, 2=High
# FinalGrade   : 0=A, 1=B, 2=C, 3=D
# Resources    : 0=Low, 1=Medium, 2=High

data$Gender          <- factor(data$Gender,          levels=0:1,   labels=c("Female","Male"))
data$LearningStyle   <- factor(data$LearningStyle,   levels=0:3,   labels=c("Visual","Auditory","Reading","Kinesthetic"))
data$Extracurricular <- factor(data$Extracurricular, levels=0:1,   labels=c("No","Yes"))
data$Internet        <- factor(data$Internet,        levels=0:1,   labels=c("No","Yes"))
data$EduTech         <- factor(data$EduTech,         levels=0:1,   labels=c("No","Yes"))
data$Motivation      <- factor(data$Motivation,      levels=0:2,   labels=c("Low","Medium","High"))
data$StressLevel     <- factor(data$StressLevel,     levels=0:2,   labels=c("Low","Medium","High"))
data$FinalGrade      <- factor(data$FinalGrade,      levels=0:3,   labels=c("A","B","C","D"))

data$ExamScore      <- as.numeric(data$ExamScore)
data$FinalGrade_num <- as.numeric(data$FinalGrade)   # A=1, B=2, C=3, D=4

# Matriks DV dan daftar faktor/kovariat
dv_mat  <- cbind(FinalGrade = data$FinalGrade_num, ExamScore = data$ExamScore)
factors <- c("Gender", "LearningStyle", "Extracurricular",
             "Internet", "EduTech", "Motivation", "StressLevel")
covars  <- c("StudyHours", "Attendance", "AssignmentCompletion",
             "OnlineCourses", "Discussions", "Age")
covs    <- c("StudyHours", "Attendance", "AssignmentCompletion")

# Helper
sig <- function(p) ifelse(p < 0.05, "Signifikan *", "Tidak Signifikan")
kb  <- function(df, cap, col = "Keputusan", bg = "#d4edda") {
  kable(df, caption = cap) %>%
    kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                  full_width = FALSE) %>%
    row_spec(which(df[[col]] == "Signifikan *"), bold = TRUE, background = bg)
}

Karakteristik Data

Statistika Deskriptif

var_numerik  <- c("StudyHours", "Attendance", "Age", "OnlineCourses",
                  "Discussions", "AssignmentCompletion", "ExamScore")
var_kategorik <- c("Resources", "Extracurricular", "Motivation", "Internet",
                   "Gender", "LearningStyle", "EduTech",
                   "StressLevel", "FinalGrade")

data_deskriptif <- describe(data[var_numerik])
print(data_deskriptif)
##                      vars     n  mean    sd median trimmed   mad min max range
## StudyHours              1 14003 19.99  5.89     20   19.94  5.93   5  44    39
## Attendance              2 14003 80.19 11.47     80   80.22 14.83  60 100    40
## Age                     3 14003 23.53  3.51     24   23.53  4.45  18  29    11
## OnlineCourses           4 14003  9.89  6.11     10    9.88  7.41   0  20    20
## Discussions             5 14003  0.61  0.49      1    0.63  0.00   0   1     1
## AssignmentCompletion    6 14003 74.50 14.63     74   74.40 19.27  50 100    50
## ExamScore               7 14003 70.35 17.69     70   70.41 22.24  40 100    60
##                       skew kurtosis   se
## StudyHours            0.10    -0.20 0.05
## Attendance           -0.01    -1.18 0.10
## Age                  -0.02    -1.26 0.03
## OnlineCourses         0.02    -1.23 0.05
## Discussions          -0.43    -1.81 0.00
## AssignmentCompletion  0.05    -1.18 0.12
## ExamScore            -0.02    -1.21 0.15
for (kol in intersect(var_kategorik, colnames(data))) {
  cat("\n---", kol, "---\n")
  frekuensi  <- table(data[[kol]])
  persentase <- round(prop.table(frekuensi) * 100, 2)
  print(cbind(Frekuensi = frekuensi, Persentase = persentase))
}
## 
## --- Resources ---
##   Frekuensi Persentase
## 0      2750      19.64
## 1      7041      50.28
## 2      4212      30.08
## 
## --- Extracurricular ---
##     Frekuensi Persentase
## No       5683      40.58
## Yes      8320      59.42
## 
## --- Motivation ---
##        Frekuensi Persentase
## Low         4112      29.37
## Medium      7098      50.69
## High        2793      19.95
## 
## --- Internet ---
##     Frekuensi Persentase
## No       1043       7.45
## Yes     12960      92.55
## 
## --- Gender ---
##        Frekuensi Persentase
## Female      6274       44.8
## Male        7729       55.2
## 
## --- LearningStyle ---
##             Frekuensi Persentase
## Visual           3376      24.11
## Auditory         3580      25.57
## Reading          3500      24.99
## Kinesthetic      3547      25.33
## 
## --- EduTech ---
##     Frekuensi Persentase
## No       4074      29.09
## Yes      9929      70.91
## 
## --- StressLevel ---
##        Frekuensi Persentase
## Low         2836      20.25
## Medium      4069      29.06
## High        7098      50.69
## 
## --- FinalGrade ---
##   Frekuensi Persentase
## A      3832      27.37
## B      3310      23.64
## C      3618      25.84
## D      3243      23.16

Visualisasi Data

par(mfrow = c(4, 4), mar = c(4, 4, 2, 1))

for (i in 1:ncol(data)) {
  nama_col <- colnames(data)[i]
  if (nama_col %in% var_kategorik) {
    counts <- table(data[[i]])
    barplot(counts, main = nama_col, col = "lightcoral",
            border = "white", cex.main = 0.8)
  } else if (is.numeric(data[[i]])) {
    hist(data[[i]], main = nama_col, col = "lightblue",
         border = "white", xlab = "", cex.main = 0.8)
  }
}

par(mfrow = c(1, 1))


Uji Asumsi MANOVA

DV: FinalGrade + ExamScore | Faktor: Gender

A1 — Normalitas Multivariat

# Sample 2000 baris agar mvn() tidak kehabisan RAM
set.seed(42)
idx_mvn <- sample(nrow(data), 2000)
dv_sample_manova <- data.frame(
  FinalGrade = as.double(data$FinalGrade_num[idx_mvn]),
  ExamScore  = as.double(data$ExamScore[idx_mvn])
)

# Deteksi argumen yang didukung versi MVN yang terinstal
mvn_args <- names(formals(MVN::mvn))
use_new_api <- "mvnTest" %in% mvn_args

mvn_res <- if (use_new_api) {
  MVN::mvn(data = dv_sample_manova, mvnTest = "mardia", univariateTest = "SW")
} else {
  MVN::mvn(dv_sample_manova)
}

cat("=== Normalitas Multivariat — sampel n=2.000 ===
")
## === Normalitas Multivariat — sampel n=2.000 ===
for (slot in intersect(names(mvn_res),
                        c("multivariateNormality","Mardia","mardia","HZ","hz"))) {
  print(kable(mvn_res[[slot]]) |>
    kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE))
}

cat("
=== Normalitas Univariat ===
")
## 
## === Normalitas Univariat ===
for (slot in intersect(names(mvn_res),
                        c("univariateNormality","univariate"))) {
  print(kable(mvn_res[[slot]]) |>
    kable_styling(bootstrap_options = c("striped","hover"), full_width = FALSE))
}

# Q-Q Plot
par(mfrow = c(1, 2))
qqnorm(data$FinalGrade_num, main = "Q-Q: FinalGrade",
       col = "steelblue", pch = 16, cex = 0.3)
qqline(data$FinalGrade_num, col = "tomato", lwd = 2)
qqnorm(data$ExamScore, main = "Q-Q: ExamScore",
       col = "steelblue", pch = 16, cex = 0.3)
qqline(data$ExamScore, col = "tomato", lwd = 2)

par(mfrow = c(1, 1))

# Shapiro-Wilk per grup
set.seed(42)
df_s <- sample_n(data, 2000)
lapply(c("FinalGrade_num", "ExamScore"), function(v)
  df_s |> group_by(Gender) |>
    summarise(W = round(shapiro.test(.data[[v]])$statistic, 4),
              p = round(shapiro.test(.data[[v]])$p.value, 4), .groups = "drop") |>
    mutate(Variabel = v)
) |> bind_rows() |>
  kable(caption = "Shapiro-Wilk per Grup Gender (sampel n=2.000)") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Shapiro-Wilk per Grup Gender (sampel n=2.000)
Gender W p Variabel
Female 0.8567 0 FinalGrade_num
Male 0.8601 0 FinalGrade_num
Female 0.9569 0 ExamScore
Male 0.9582 0 ExamScore

A2 — Box’s M & Levene

print(box_m(data[, c("FinalGrade_num", "ExamScore")], data$Gender))
## # A tibble: 1 × 4
##   statistic p.value parameter method                                            
##       <dbl>   <dbl>     <dbl> <chr>                                             
## 1      5.53   0.137         3 Box's M-test for Homogeneity of Covariance Matric…
map_df(c("FinalGrade_num", "ExamScore"), function(v) {
  lv <- leveneTest(as.formula(paste(v, "~ Gender")), data = data)
  data.frame(DV = v, F = round(lv$`F value`[1], 4), p = round(lv$`Pr(>F)`[1], 4),
             Keputusan = ifelse(lv$`Pr(>F)`[1] > 0.05, "Terpenuhi", "Tidak Terpenuhi"))
}) |> kable(caption = "Levene's Test — Homogenitas Varians") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Levene’s Test — Homogenitas Varians
DV F p Keputusan
FinalGrade_num 3.3975 0.0653 Terpenuhi
ExamScore 1.6018 0.2057 Terpenuhi

A3 — Korelasi DV & Outlier Mahalanobis

ct <- cor.test(data$FinalGrade_num, data$ExamScore)
data.frame(r = round(ct$estimate, 4), p = formatC(ct$p.value, format = "e", digits = 3),
           Interpretasi = ifelse(abs(ct$estimate) > 0.2 & abs(ct$estimate) < 0.9,
                                 "Moderat ✔", "Periksa")) |>
  kable(caption = "Korelasi Pearson: FinalGrade ~ ExamScore") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Korelasi Pearson: FinalGrade ~ ExamScore
r p Interpretasi
cor -0.9683 0.000e+00 Periksa
D2       <- mahalanobis(dv_mat, colMeans(dv_mat), cov(dv_mat))
chi_crit <- qchisq(0.999, df = 2)
data.frame(
  Keterangan = c("Chi-sq kritis (p=.001, df=2)", "Outlier terdeteksi", "Persentase"),
  Nilai      = c(round(chi_crit, 4), sum(D2 > chi_crit),
                 paste0(round(mean(D2 > chi_crit) * 100, 2), "%"))
) |> kable(caption = "Outlier Multivariat (Mahalanobis)") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Outlier Multivariat (Mahalanobis)
Keterangan Nilai
Chi-sq kritis (p=.001, df=2) 13.8155
Outlier terdeteksi 0
Persentase 0%

Uji Asumsi MANCOVA

DV: FinalGrade + ExamScore | Faktor: Gender | Kovariat: StudyHours, Attendance, AssignmentCompletion, OnlineCourses, Discussions, Age

B1 — Normalitas Multivariat

set.seed(42)
dv_sample <- data.frame(
  ExamScore  = as.double(data$ExamScore[1:5000]),
  FinalGrade = as.double(data$FinalGrade_num[1:5000])
)

mvn_res2 <- mvn(data = dv_sample)

cat("=== Normalitas Multivariat (Henze-Zirkler) ===\n")
## === Normalitas Multivariat (Henze-Zirkler) ===
print(mvn_res2$multivariateNormality)
## NULL
cat("\n=== Normalitas Univariat (Anderson-Darling) ===\n")
## 
## === Normalitas Univariat (Anderson-Darling) ===
print(mvn_res2$univariateNormality)
## NULL
par(mfrow = c(1, 2))
qqnorm(data$ExamScore, main = "Q-Q: ExamScore",
       col = "steelblue", pch = 16, cex = 0.4)
qqline(data$ExamScore, col = "tomato", lwd = 2)
qqnorm(data$FinalGrade_num, main = "Q-Q: FinalGrade",
       col = "steelblue", pch = 16, cex = 0.4)
qqline(data$FinalGrade_num, col = "tomato", lwd = 2)

par(mfrow = c(1, 1))

B2 — Normalitas per Grup (Shapiro-Wilk)

set.seed(42)
data_s <- sample_n(data, 5000)

lapply(c("ExamScore", "FinalGrade_num"), function(v)
  data_s |> group_by(Gender) |>
    summarise(W = round(shapiro.test(.data[[v]])$statistic, 4),
              p = round(shapiro.test(.data[[v]])$p.value, 4), .groups = "drop") |>
    mutate(Variabel = v)
) |> bind_rows() |>
  kable(caption = "Shapiro-Wilk per Grup Gender (n=5000)") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Shapiro-Wilk per Grup Gender (n=5000)
Gender W p Variabel
Female 0.9564 0 ExamScore
Male 0.9521 0 ExamScore
Female 0.8565 0 FinalGrade_num
Male 0.8542 0 FinalGrade_num

B3 — Box’s M (Homogenitas Matriks Kovarians)

boxM_res <- heplots::boxM(dv_mat, data$Gender)
print(boxM_res)
## 
##  Box's M-test for Homogeneity of Covariance Matrices 
## 
## data:  dv_mat by data$Gender 
## Chi-Sq (approx.) = 5.5313, df = 3, p-value = 0.1368
map_df(c("ExamScore", "FinalGrade_num"), function(v) {
  lv <- leveneTest(as.formula(paste(v, "~ Gender")), data = data)
  data.frame(DV = v, F_stat = round(lv$`F value`[1], 4),
             p = round(lv$`Pr(>F)`[1], 4),
             Keputusan = ifelse(lv$`Pr(>F)`[1] > 0.05, "Terpenuhi", "Tidak Terpenuhi"))
}) |>
  kable(caption = "Levene Test — Homogenitas Varians per DV") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Levene Test — Homogenitas Varians per DV
DV F_stat p Keputusan
ExamScore 1.6018 0.2057 Terpenuhi
FinalGrade_num 3.3975 0.0653 Terpenuhi

B4 — Linearitas Kovariat

map_df(covars, function(cv)
  data.frame(Covariate    = cv,
             r_ExamScore  = round(cor(data[[cv]], data$ExamScore), 4),
             r_FinalGrade = round(cor(data[[cv]], data$FinalGrade_num), 4))
) |>
  kable(caption = "Korelasi Pearson: Kovariat vs DV") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Korelasi Pearson: Kovariat vs DV
Covariate r_ExamScore r_FinalGrade
StudyHours 0.0045 -0.0096
Attendance -0.0147 0.0109
AssignmentCompletion 0.0253 -0.0301
OnlineCourses 0.0210 -0.0210
Discussions -0.0288 0.0397
Age -0.0072 0.0029
p1 <- ggplot(data, aes(x = StudyHours, y = ExamScore)) +
  geom_point(alpha = 0.2, color = "steelblue") +
  geom_smooth(method = "lm", color = "tomato", se = TRUE) +
  labs(title = "StudyHours vs ExamScore") + theme_minimal()

p2 <- ggplot(data, aes(x = StudyHours, y = FinalGrade_num)) +
  geom_point(alpha = 0.2, color = "seagreen") +
  geom_smooth(method = "lm", color = "tomato", se = TRUE) +
  labs(title = "StudyHours vs FinalGrade") + theme_minimal()

ggarrange(p1, p2, ncol = 2)

B5 — Homogenitas Slope Regresi

expand.grid(dv = c("ExamScore", "FinalGrade_num"), cov = covars,
            stringsAsFactors = FALSE) |>
  pmap_df(function(dv, cov) {
    av <- anova(lm(as.formula(paste(dv, "~ Gender *", cov)), data = data))
    p  <- av[grep(":", rownames(av)), "Pr(>F)"]
    data.frame(DV = dv, Covariate = cov,
               p_interaksi = round(p, 4),
               Keputusan   = ifelse(p > 0.05, "Homogen ✔", "Tidak Homogen ✘"))
  }) |>
  kable(caption = "Homogenitas Slope: Gender × Kovariat") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Homogenitas Slope: Gender × Kovariat
DV Covariate p_interaksi Keputusan
ExamScore StudyHours 0.0307 Tidak Homogen ✘
FinalGrade_num StudyHours 0.0926 Homogen ✔
ExamScore Attendance 0.9384 Homogen ✔
FinalGrade_num Attendance 0.7427 Homogen ✔
ExamScore AssignmentCompletion 0.5780 Homogen ✔
FinalGrade_num AssignmentCompletion 0.8214 Homogen ✔
ExamScore OnlineCourses 0.8258 Homogen ✔
FinalGrade_num OnlineCourses 0.5902 Homogen ✔
ExamScore Discussions 0.0190 Tidak Homogen ✘
FinalGrade_num Discussions 0.0005 Tidak Homogen ✘
ExamScore Age 0.0003 Tidak Homogen ✘
FinalGrade_num Age 0.0005 Tidak Homogen ✘

B6 — Independensi Kovariat dari Faktor

map_df(covars, function(cv) {
  p <- summary(aov(as.formula(paste(cv, "~ Gender")), data = data))[[1]][["Pr(>F)"]][1]
  data.frame(Covariate = cv, p = round(p, 4),
             Keputusan = ifelse(p > 0.05, "Independen ✔", "Berkorelasi dgn Grup ✘"))
}) |>
  kable(caption = "Independensi Kovariat dari Gender") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Independensi Kovariat dari Gender
Covariate p Keputusan
StudyHours 0.0905 Independen ✔
Attendance 0.0009 Berkorelasi dgn Grup ✘
AssignmentCompletion 0.1847 Independen ✔
OnlineCourses 0.2601 Independen ✔
Discussions 0.6823 Independen ✔
Age 0.0081 Berkorelasi dgn Grup ✘

Uji Asumsi ANCOVA

DV: ExamScore | Faktor: Gender | Kovariat: StudyHours

C1 — Normalitas Residual

m_anc <- lm(ExamScore ~ StudyHours + Gender, data = data)
res   <- residuals(m_anc)

set.seed(42)
sw <- shapiro.test(sample(res, 5000))
ad <- ad.test(res)

data.frame(Uji       = c("Shapiro-Wilk (n=5.000)", "Anderson-Darling"),
           Stat      = round(c(sw$statistic, ad$statistic), 4),
           p         = formatC(c(sw$p.value, ad$p.value), format = "e", digits = 3),
           Keputusan = ifelse(c(sw$p.value, ad$p.value) > 0.05,
                              "Normal ✔", "Tidak Normal ✘")) |>
  kable(caption = "Normalitas Residual ANCOVA") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Normalitas Residual ANCOVA
Uji Stat p Keputusan
W Shapiro-Wilk (n=5.000) 0.9548 9.012e-37 Tidak Normal ✘
A Anderson-Darling 162.0648 3.700e-24 Tidak Normal ✘
par(mfrow = c(1, 2))
qqnorm(res, col = adjustcolor("steelblue", .3), pch = 16, cex = .5, main = "Q-Q Residual")
qqline(res, col = "tomato", lwd = 2)
hist(res, breaks = 60, col = "steelblue", border = "white", freq = FALSE,
     main = "Histogram Residual", xlab = "Residual")
curve(dnorm(x, mean(res), sd(res)), col = "tomato", lwd = 2, add = TRUE)

par(mfrow = c(1, 1))

C2 — Homogenitas, Linearitas & Slope

lv  <- leveneTest(ExamScore ~ Gender, data = data)
bar <- bartlett.test(ExamScore ~ Gender, data = data)
data.frame(Uji       = c("Levene", "Bartlett"),
           Stat      = round(c(lv$`F value`[1], bar$statistic), 4),
           p         = round(c(lv$`Pr(>F)`[1], bar$p.value), 4),
           Keputusan = ifelse(c(lv$`Pr(>F)`[1], bar$p.value) > 0.05,
                              "Homogen ✔", "Tidak Homogen ✘")) |>
  kable(caption = "Homogenitas Varians") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Homogenitas Varians
Uji Stat p Keputusan
Levene 1.6018 0.2057 Homogen ✔
Bartlett’s K-squared Bartlett 0.1396 0.7087 Homogen ✔
ct2 <- cor.test(data$StudyHours, data$ExamScore)
cat("r =", round(ct2$estimate, 4), "| p =", formatC(ct2$p.value, format = "e", digits = 3))
## r = 0.0045 | p = 5.949e-01
av2 <- anova(lm(ExamScore ~ StudyHours * Gender, data = data))
data.frame(Term = rownames(av2), Df = av2$Df,
           F    = round(av2$`F value`, 4),
           p    = round(av2$`Pr(>F)`, 4)) |>
  kable(caption = "Interaksi StudyHours × Gender", na.string = "") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) |>
  row_spec(grep(":", rownames(av2)), bold = TRUE, background = "#fff3cd")
Interaksi StudyHours × Gender
Term Df F p
StudyHours 1 0.2829 0.5948
Gender 1 6.4210 0.0113
StudyHours:Gender 1 4.6689 0.0307
Residuals 13999 NA NA
t2 <- t.test(StudyHours ~ Gender, data = data)
data.frame(Uji       = "t-test: StudyHours ~ Gender",
           t         = round(t2$statistic, 4),
           df        = round(t2$parameter, 2),
           p         = round(t2$p.value, 4),
           Keputusan = ifelse(t2$p.value > 0.05, "Independen ✔", "Berkorelasi dgn Grup ✘")) |>
  kable(caption = "Independensi StudyHours dari Gender") |>
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Independensi StudyHours dari Gender
Uji t df p Keputusan
t t-test: StudyHours ~ Gender 1.6934 13425.41 0.0904 Independen ✔

Ringkasan Uji Asumsi

Ringkasan Semua Uji Asumsi
Metode Kode Asumsi Indikator
MANOVA A1 Normalitas multivariat (Mardia) p > 0.05
A2 Homogenitas matriks varians-kovarians (Box’s M) p > 0.05
A3 Homogenitas varians univariat (Levene) p > 0.05
A4 Korelasi moderat antar DV 0.2 < &#124;r&#124; < 0.9
A5 Tidak ada outlier multivariat (Mahalanobis) Outlier < 5%
MANCOVA B1 Linearitas DV ~ covariate Korelasi & scatterplot linear
B2 Homogenitas slope regresi p interaksi > 0.05
B3 Covariate independen dari faktor grup p > 0.05
ANCOVA C1 Normalitas residual p > 0.05
C2 Homogenitas varians (Levene & Bartlett) p > 0.05
C3 Linearitas covariate ~ DV Korelasi & scatterplot linear
C4 Homogenitas slope regresi (interaksi) p interaksi > 0.05
C5 Covariate independen dari faktor grup p > 0.05

MANOVA

MANOVA per Faktor

models <- lapply(setNames(factors, factors), function(f)
  manova(as.formula(paste("cbind(ExamScore, FinalGrade_num) ~", f)), data = data))

pillai <- lapply(factors, function(f) {
  s <- summary(models[[f]], test = "Pillai")$stats
  data.frame(Faktor = f, Pillai = round(s[1,1],4), `approx F` = round(s[1,3],4),
             `num Df` = s[1,4], `den Df` = s[1,5],
             `p-value` = round(s[1,6],4), Keputusan = sig(s[1,6]),
             check.names = FALSE)
}) |> bind_rows()

kb(pillai, "Tabel 1. Pillai's Trace — Semua Faktor")
Tabel 1. Pillai’s Trace — Semua Faktor
Faktor Pillai approx F num Df den Df p-value Keputusan
Gender 1 3.5749 2 14000 0.0280 Signifikan *
LearningStyle 3 4.6623 6 27998 0.0001 Signifikan *
Extracurricular 1 0.7933 2 14000 0.4524 Tidak Signifikan
Internet 1 0.4026 2 14000 0.6686 Tidak Signifikan
EduTech 1 3.0738 2 14000 0.0463 Signifikan *
Motivation 2 0.6591 4 28000 0.6204 Tidak Signifikan
StressLevel 2 11.3533 4 28000 0.0000 Signifikan *

Detail per Faktor

for (f in factors) {
  cat(sprintf("\n### %s\n", f))
  cat("```\n")
  print(summary(models[[f]], test = "Pillai"))
  cat("\n--- ANOVA Univariat ---\n")
  print(summary.aov(models[[f]]))
  cat("```\n")
}

Gender

             Df     Pillai approx F num Df den Df  Pr(>F)  
Gender        1 0.00051044   3.5749      2  14000 0.02804 *
Residuals 14001                                            
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

--- ANOVA Univariat ---
 Response ExamScore :
               Df  Sum Sq Mean Sq F value  Pr(>F)  
Gender          1    1995 1995.29  6.3798 0.01155 *
Residuals   14001 4378801  312.75                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

 Response FinalGrade_num :
               Df  Sum Sq Mean Sq F value   Pr(>F)   
Gender          1     8.9  8.9297  7.1022 0.007708 **
Residuals   14001 17603.8  1.2573                    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

LearningStyle

                 Df    Pillai approx F num Df den Df    Pr(>F)    
LearningStyle     3 0.0019963   4.6623      6  27998 9.553e-05 ***
Residuals     13999                                               
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

--- ANOVA Univariat ---
 Response ExamScore :
                 Df  Sum Sq Mean Sq F value  Pr(>F)  
LearningStyle     3    3264  1088.0  3.4795 0.01521 *
Residuals     13999 4377533   312.7                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

 Response FinalGrade_num :
                 Df  Sum Sq Mean Sq F value   Pr(>F)   
LearningStyle     3    17.3  5.7557  4.5793 0.003294 **
Residuals     13999 17595.5  1.2569                    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Extracurricular

                   Df     Pillai approx F num Df den Df Pr(>F)
Extracurricular     1 0.00011332  0.79331      2  14000 0.4524
Residuals       14001                                         

--- ANOVA Univariat ---
 Response ExamScore :
                   Df  Sum Sq Mean Sq F value Pr(>F)
Extracurricular     1     489  488.58  1.5617 0.2114
Residuals       14001 4380308  312.86               

 Response FinalGrade_num :
                   Df Sum Sq Mean Sq F value Pr(>F)
Extracurricular     1      2  1.9642  1.5616 0.2115
Residuals       14001  17611  1.2578               

Internet

             Df     Pillai approx F num Df den Df Pr(>F)
Internet      1 5.7514e-05  0.40262      2  14000 0.6686
Residuals 14001                                         

--- ANOVA Univariat ---
 Response ExamScore :
               Df  Sum Sq Mean Sq F value Pr(>F)
Internet        1     114  114.29  0.3653 0.5456
Residuals   14001 4380682  312.88               

 Response FinalGrade_num :
               Df  Sum Sq Mean Sq F value Pr(>F)
Internet        1     0.7 0.70915  0.5637 0.4528
Residuals   14001 17612.0 1.25791               

EduTech

             Df     Pillai approx F num Df den Df  Pr(>F)  
EduTech       1 0.00043893   3.0738      2  14000 0.04627 *
Residuals 14001                                            
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

--- ANOVA Univariat ---
 Response ExamScore :
               Df  Sum Sq Mean Sq F value Pr(>F)
EduTech         1     831  831.01  2.6564 0.1032
Residuals   14001 4379966  312.83               

 Response FinalGrade_num :
               Df  Sum Sq Mean Sq F value Pr(>F)  
EduTech         1     5.3  5.2576  4.1807 0.0409 *
Residuals   14001 17607.5  1.2576                 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Motivation

              Df     Pillai approx F num Df den Df Pr(>F)
Motivation     2 0.00018831  0.65914      4  28000 0.6204
Residuals  14000                                         

--- ANOVA Univariat ---
 Response ExamScore :
               Df  Sum Sq Mean Sq F value Pr(>F)
Motivation      2     281  140.60  0.4494  0.638
Residuals   14000 4380515  312.89               

 Response FinalGrade_num :
               Df Sum Sq Mean Sq F value Pr(>F)
Motivation      2      2 0.97776  0.7773 0.4597
Residuals   14000  17611 1.25791               

StressLevel

               Df    Pillai approx F num Df den Df    Pr(>F)    
StressLevel     2 0.0032385   11.353      4  28000 3.318e-09 ***
Residuals   14000                                               
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

--- ANOVA Univariat ---
 Response ExamScore :
               Df  Sum Sq Mean Sq F value    Pr(>F)    
StressLevel     2    8622  4311.2  13.805 1.025e-06 ***
Residuals   14000 4372174   312.3                      
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

 Response FinalGrade_num :
               Df  Sum Sq Mean Sq F value    Pr(>F)    
StressLevel     2    47.4 23.6795  18.873 6.525e-09 ***
Residuals   14000 17565.4  1.2547                      
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

ANOVA Univariat & Effect Size η²

dvs <- c("ExamScore", "FinalGrade")

res_eta <- lapply(factors, function(f) {
  av <- summary.aov(models[[f]])
  lapply(1:2, function(i) {
    a    <- av[[i]]
    eta2 <- round(a$`Sum Sq`[1] / sum(a$`Sum Sq`, na.rm = TRUE), 4)
    data.frame(Faktor = f, DV = dvs[i],
               `F value`     = round(a$`F value`[1], 4),
               `p-value`     = round(a$`Pr(>F)`[1], 4),
               Keputusan     = sig(a$`Pr(>F)`[1]),
               `η²`          = eta2,
               Interpretasi  = case_when(
                 eta2 < 0.01 ~ "Kecil", eta2 < 0.06 ~ "Sedang",
                 eta2 < 0.14 ~ "Besar", TRUE ~ "Sangat Besar"),
               check.names = FALSE)
  }) |> bind_rows()
}) |> bind_rows()

kb(res_eta, "Tabel 2. ANOVA Univariat & η² — Semua Faktor")
Tabel 2. ANOVA Univariat & η² — Semua Faktor
Faktor DV F value p-value Keputusan η² Interpretasi
Gender ExamScore 6.3798 0.0116 Signifikan * 0.0005 Kecil
Gender FinalGrade 7.1022 0.0077 Signifikan * 0.0005 Kecil
LearningStyle ExamScore 3.4795 0.0152 Signifikan * 0.0007 Kecil
LearningStyle FinalGrade 4.5793 0.0033 Signifikan * 0.0010 Kecil
Extracurricular ExamScore 1.5617 0.2114 Tidak Signifikan 0.0001 Kecil
Extracurricular FinalGrade 1.5616 0.2115 Tidak Signifikan 0.0001 Kecil
Internet ExamScore 0.3653 0.5456 Tidak Signifikan 0.0000 Kecil
Internet FinalGrade 0.5637 0.4528 Tidak Signifikan 0.0000 Kecil
EduTech ExamScore 2.6564 0.1032 Tidak Signifikan 0.0002 Kecil
EduTech FinalGrade 4.1807 0.0409 Signifikan * 0.0003 Kecil
Motivation ExamScore 0.4494 0.6380 Tidak Signifikan 0.0001 Kecil
Motivation FinalGrade 0.7773 0.4597 Tidak Signifikan 0.0001 Kecil
StressLevel ExamScore 13.8047 0.0000 Signifikan * 0.0020 Kecil
StressLevel FinalGrade 18.8731 0.0000 Signifikan * 0.0027 Kecil

MANOVA Model Penuh

m_full <- manova(cbind(ExamScore, FinalGrade_num) ~
                   Gender + LearningStyle + Extracurricular +
                   Internet + EduTech + Motivation + StressLevel, data = data)

sf <- summary(m_full, test = "Pillai")$stats
nr <- nrow(sf) - 1
tbl_full <- data.frame(
  Efek      = rownames(sf)[1:nr],
  Pillai    = round(sf[1:nr,1],4), `approx F` = round(sf[1:nr,3],4),
  `num Df`  = sf[1:nr,4], `den Df` = sf[1:nr,5],
  `p-value` = round(sf[1:nr,6],4), Keputusan = sig(sf[1:nr,6]),
  check.names = FALSE)

kb(tbl_full, "Tabel 3. MANOVA Model Penuh — Pillai's Trace")
Tabel 3. MANOVA Model Penuh — Pillai’s Trace
Efek Pillai approx F num Df den Df p-value Keputusan
Gender Gender 1 3.5888 2 13990 0.0277 Signifikan *
LearningStyle LearningStyle 3 4.6547 6 27982 0.0001 Signifikan *
Extracurricular Extracurricular 1 0.7650 2 13990 0.4653 Tidak Signifikan
Internet Internet 1 0.4594 2 13990 0.6317 Tidak Signifikan
EduTech EduTech 1 2.6612 2 13990 0.0699 Tidak Signifikan
Motivation Motivation 2 0.6626 4 27982 0.6180 Tidak Signifikan
StressLevel StressLevel 2 11.7289 4 27982 0.0000 Signifikan *
af <- summary.aov(m_full)
tbl_af <- lapply(1:2, function(i) {
  a <- af[[i]]; nr2 <- nrow(a) - 1
  data.frame(DV = dvs[i], Efek = rownames(a)[1:nr2],
             `F value` = round(a$`F value`[1:nr2],4),
             `p-value` = round(a$`Pr(>F)`[1:nr2],4),
             Keputusan = sig(a$`Pr(>F)`[1:nr2]),
             check.names = FALSE)
}) |> bind_rows()

kb(tbl_af, "Tabel 4. ANOVA Univariat Follow-up — Model Penuh")
Tabel 4. ANOVA Univariat Follow-up — Model Penuh
DV Efek F value p-value Keputusan
ExamScore Gender 6.3953 0.0115 Signifikan *
ExamScore LearningStyle 3.4908 0.0150 Signifikan *
ExamScore Extracurricular 1.5027 0.2203 Tidak Signifikan
ExamScore Internet 0.4358 0.5092 Tidak Signifikan
ExamScore EduTech 2.0896 0.1483 Tidak Signifikan
ExamScore Motivation 0.4337 0.6481 Tidak Signifikan
ExamScore StressLevel 14.3241 0.0000 Signifikan *
FinalGrade Gender 7.1275 0.0076 Signifikan *
FinalGrade LearningStyle 4.5648 0.0034 Signifikan *
FinalGrade Extracurricular 1.5089 0.2193 Tidak Signifikan
FinalGrade Internet 0.6607 0.4163 Tidak Signifikan
FinalGrade EduTech 3.4185 0.0645 Tidak Signifikan
FinalGrade Motivation 0.7630 0.4663 Tidak Signifikan
FinalGrade StressLevel 19.5465 0.0000 Signifikan *

Estimated Marginal Means (EMM)

emm_plots <- lapply(factors, function(f) {
  get_emm <- function(dv) {
    e <- as.data.frame(summary(
      emmeans(lm(as.formula(paste(dv, "~", f)), data = data),
              as.formula(paste("~", f)))))
    colnames(e)[1] <- "Grup"; e
  }
  lapply(list(c("ExamScore", "#84A7D4"), c("FinalGrade_num", "#E8A0BF")),
         function(x) {
           e <- get_emm(x[1])
           ggplot(e, aes(x = Grup, y = emmean)) +
             geom_col(fill = x[2], alpha = 0.8, width = 0.5) +
             geom_errorbar(aes(ymin = lower.CL, ymax = upper.CL), width = 0.15) +
             labs(title = paste(f, "→", gsub("_num","",x[1])), x = "", y = "EMM") +
             theme_minimal(base_size = 8) +
             theme(axis.text.x = element_text(angle = 20, hjust = 1))
         })
})

ggarrange(plotlist = unlist(emm_plots, recursive = FALSE), ncol = 2, nrow = 7)


ANCOVA

Model ANCOVA Utama

model_ancova <- lm(
  ExamScore ~ .,
  data = data[, c("ExamScore", factors, covars)]
)

ancova_tbl <- Anova(model_ancova, type = "III")
ancova_df  <- as.data.frame(ancova_tbl)
ancova_df$Variabel  <- rownames(ancova_df)
ancova_df$Keputusan <- ifelse(ancova_df$`Pr(>F)` < 0.05, "Signifikan *", "Tidak Signifikan")

knitr::kable(ancova_df, caption = "Tabel ANCOVA (Type III)") %>%
  kable_styling(bootstrap_options = c("striped","hover"), font_size = 14, full_width = FALSE) %>%
  row_spec(which(ancova_df$Keputusan == "Signifikan *"), bold = TRUE, background = "#d4edda")
Tabel ANCOVA (Type III)
Sum Sq Df F value Pr(>F) Variabel Keputusan
(Intercept) 423249.0417 1 1359.1498768 0.0000000 (Intercept) Signifikan *
Gender 2244.3881 1 7.2072455 0.0072695 Gender Signifikan *
LearningStyle 3089.0516 3 3.3065514 0.0192895 LearningStyle Signifikan *
Extracurricular 437.9962 1 1.4065062 0.2356574 Extracurricular Tidak Signifikan
Internet 194.7350 1 0.6253388 0.4290836 Internet Tidak Signifikan
EduTech 671.0092 1 2.1547647 0.1421507 EduTech Tidak Signifikan
Motivation 297.6228 2 0.4778676 0.6201144 Motivation Tidak Signifikan
StressLevel 9256.2133 2 14.8619134 0.0000004 StressLevel Signifikan *
StudyHours 159.4691 1 0.5120920 0.4742466 StudyHours Tidak Signifikan
Attendance 1208.5272 1 3.8808583 0.0488593 Attendance Signifikan *
AssignmentCompletion 2816.3435 1 9.0439258 0.0026404 AssignmentCompletion Signifikan *
OnlineCourses 2017.7387 1 6.4794224 0.0109237 OnlineCourses Signifikan *
Discussions 3758.8932 1 12.0706695 0.0005138 Discussions Signifikan *
Age 201.3211 1 0.6464884 0.4213847 Age Tidak Signifikan
Residuals 4355029.5297 13985 NA NA Residuals NA
summary(model_ancova)
## 
## Call:
## lm(formula = ExamScore ~ ., data = data[, c("ExamScore", factors, 
##     covars)])
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -34.813 -15.097  -0.168  15.448  32.856 
## 
## Coefficients:
##                          Estimate Std. Error t value Pr(>|t|)    
## (Intercept)              71.25686    1.93283  36.867  < 2e-16 ***
## GenderMale                0.80678    0.30052   2.685 0.007270 ** 
## LearningStyleAuditory     1.32320    0.42409   3.120 0.001812 ** 
## LearningStyleReading      0.83620    0.42638   1.961 0.049880 *  
## LearningStyleKinesthetic  0.75076    0.42527   1.765 0.077522 .  
## ExtracurricularYes       -0.36048    0.30396  -1.186 0.235657    
## InternetYes              -0.44955    0.56848  -0.791 0.429084    
## EduTechYes                0.48281    0.32891   1.468 0.142151    
## MotivationMedium          0.15903    0.34608   0.460 0.645863    
## MotivationHigh           -0.22149    0.43299  -0.512 0.608979    
## StressLevelMedium        -2.18644    0.43224  -5.058 4.28e-07 ***
## StressLevelHigh          -1.89462    0.39243  -4.828 1.39e-06 ***
## StudyHours                0.01815    0.02536   0.716 0.474247    
## Attendance               -0.02569    0.01304  -1.970 0.048859 *  
## AssignmentCompletion      0.03068    0.01020   3.007 0.002640 ** 
## OnlineCourses             0.06213    0.02441   2.545 0.010924 *  
## Discussions              -1.06216    0.30572  -3.474 0.000514 ***
## Age                      -0.03423    0.04257  -0.804 0.421385    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 17.65 on 13985 degrees of freedom
## Multiple R-squared:  0.005882,   Adjusted R-squared:  0.004673 
## F-statistic: 4.867 on 17 and 13985 DF,  p-value: 1.35e-10

Uji Interaksi (Homogeneity of Regression Slopes)

model_interaction_full <- lm(
  ExamScore ~
    (Gender + LearningStyle + Extracurricular + Internet + EduTech +
     Motivation + StressLevel) *
    (StudyHours + Attendance + AssignmentCompletion +
     OnlineCourses + Discussions + Age),
  data = data
)

interaction_tbl <- Anova(model_interaction_full, type = "III")
interaction_df  <- as.data.frame(interaction_tbl)
interaction_df$Variabel  <- rownames(interaction_df)
interaction_df$Keputusan <- ifelse(interaction_df$`Pr(>F)` < 0.05, "Signifikan *", "Tidak Signifikan")

knitr::kable(interaction_df, caption = "Tabel Uji Interaksi ANCOVA") %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                font_size = 9.5, full_width = FALSE, position = "center") %>%
  row_spec(which(interaction_df$Keputusan == "Signifikan *"),
           bold = TRUE, background = "#f8d7da")
Tabel Uji Interaksi ANCOVA
Sum Sq Df F value Pr(>F) Variabel Keputusan
(Intercept) 2.933311e+04 1 94.8216491 0.0000000 (Intercept) Signifikan *
Gender 2.628334e+03 1 8.4963003 0.0035644 Gender Signifikan *
LearningStyle 3.360086e+02 3 0.3620583 0.7804133 LearningStyle Tidak Signifikan
Extracurricular 5.752638e+01 1 0.1859586 0.6663084 Extracurricular Tidak Signifikan
Internet 1.667266e+03 1 5.3895703 0.0202718 Internet Signifikan *
EduTech 3.546055e+02 1 1.1462907 0.2843455 EduTech Tidak Signifikan
Motivation 3.687493e+02 2 0.5960060 0.5510221 Motivation Tidak Signifikan
StressLevel 3.368499e+03 2 5.4444726 0.0043293 StressLevel Signifikan *
StudyHours 2.281329e+02 1 0.7374580 0.3904912 StudyHours Tidak Signifikan
Attendance 2.376861e+03 1 7.6833959 0.0055805 Attendance Signifikan *
AssignmentCompletion 1.818159e+02 1 0.5877344 0.4433096 AssignmentCompletion Tidak Signifikan
OnlineCourses 2.820136e+01 1 0.0911632 0.7627081 OnlineCourses Tidak Signifikan
Discussions 2.680672e+02 1 0.8665489 0.3519286 Discussions Tidak Signifikan
Age 5.123235e+01 1 0.1656127 0.6840470 Age Tidak Signifikan
Gender:StudyHours 1.691737e+03 1 5.4686755 0.0193744 Gender:StudyHours Signifikan *
Gender:Attendance 9.968303e+00 1 0.0322233 0.8575410 Gender:Attendance Tidak Signifikan
Gender:AssignmentCompletion 1.466636e+02 1 0.4741020 0.4911176 Gender:AssignmentCompletion Tidak Signifikan
Gender:OnlineCourses 6.661237e+01 1 0.2153298 0.6426289 Gender:OnlineCourses Tidak Signifikan
Gender:Discussions 2.558451e+03 1 8.2703973 0.0040358 Gender:Discussions Signifikan *
Gender:Age 3.259043e+03 1 10.5351177 0.0011740 Gender:Age Signifikan *
LearningStyle:StudyHours 2.472997e+03 3 2.6647208 0.0461741 LearningStyle:StudyHours Signifikan *
LearningStyle:Attendance 2.591007e+03 3 2.7918801 0.0388929 LearningStyle:Attendance Signifikan *
LearningStyle:AssignmentCompletion 6.672209e+02 3 0.7189485 0.5405163 LearningStyle:AssignmentCompletion Tidak Signifikan
LearningStyle:OnlineCourses 3.261420e+02 3 0.3514267 0.7881214 LearningStyle:OnlineCourses Tidak Signifikan
LearningStyle:Discussions 4.054827e+03 3 4.3691844 0.0044214 LearningStyle:Discussions Signifikan *
LearningStyle:Age 8.816030e+03 3 9.4995088 0.0000029 LearningStyle:Age Signifikan *
Extracurricular:StudyHours 7.322572e+02 1 2.3670802 0.1239414 Extracurricular:StudyHours Tidak Signifikan
Extracurricular:Attendance 7.210220e+00 1 0.0233076 0.8786621 Extracurricular:Attendance Tidak Signifikan
Extracurricular:AssignmentCompletion 3.104361e+02 1 1.0035098 0.3164801 Extracurricular:AssignmentCompletion Tidak Signifikan
Extracurricular:OnlineCourses 9.670193e+02 1 3.1259676 0.0770760 Extracurricular:OnlineCourses Tidak Signifikan
Extracurricular:Discussions 8.288861e+01 1 0.2679441 0.6047225 Extracurricular:Discussions Tidak Signifikan
Extracurricular:Age 1.836901e+01 1 0.0593793 0.8074833 Extracurricular:Age Tidak Signifikan
Internet:StudyHours 9.254888e+01 1 0.2991717 0.5844109 Internet:StudyHours Tidak Signifikan
Internet:Attendance 1.306844e+03 1 4.2244788 0.0398635 Internet:Attendance Signifikan *
Internet:AssignmentCompletion 8.508099e+02 1 2.7503115 0.0972580 Internet:AssignmentCompletion Tidak Signifikan
Internet:OnlineCourses 1.662132e+02 1 0.5372977 0.4635672 Internet:OnlineCourses Tidak Signifikan
Internet:Discussions 8.936715e+02 1 2.8888650 0.0892161 Internet:Discussions Tidak Signifikan
Internet:Age 3.899168e+01 1 0.1260437 0.7225752 Internet:Age Tidak Signifikan
EduTech:StudyHours 2.261269e+00 1 0.0073097 0.9318675 EduTech:StudyHours Tidak Signifikan
EduTech:Attendance 7.152208e+02 1 2.3120090 0.1284007 EduTech:Attendance Tidak Signifikan
EduTech:AssignmentCompletion 6.117421e+01 1 0.1977506 0.6565496 EduTech:AssignmentCompletion Tidak Signifikan
EduTech:OnlineCourses 5.727491e+02 1 1.8514575 0.1736360 EduTech:OnlineCourses Tidak Signifikan
EduTech:Discussions 1.938392e+02 1 0.6266007 0.4286184 EduTech:Discussions Tidak Signifikan
EduTech:Age 7.264608e+01 1 0.2348343 0.6279698 EduTech:Age Tidak Signifikan
Motivation:StudyHours 4.396536e+02 2 0.7106079 0.4913633 Motivation:StudyHours Tidak Signifikan
Motivation:Attendance 6.423104e-01 2 0.0010382 0.9989624 Motivation:Attendance Tidak Signifikan
Motivation:AssignmentCompletion 6.806566e+02 2 1.1001386 0.3328539 Motivation:AssignmentCompletion Tidak Signifikan
Motivation:OnlineCourses 6.358801e+02 2 1.0277667 0.3578323 Motivation:OnlineCourses Tidak Signifikan
Motivation:Discussions 3.341618e+01 2 0.0540103 0.9474226 Motivation:Discussions Tidak Signifikan
Motivation:Age 5.820482e+02 2 0.9407588 0.3903563 Motivation:Age Tidak Signifikan
StressLevel:StudyHours 2.606564e+02 2 0.4212964 0.6562039 StressLevel:StudyHours Tidak Signifikan
StressLevel:Attendance 1.078297e+03 2 1.7428404 0.1750608 StressLevel:Attendance Tidak Signifikan
StressLevel:AssignmentCompletion 4.600041e+03 2 7.4350007 0.0005926 StressLevel:AssignmentCompletion Signifikan *
StressLevel:OnlineCourses 1.178655e+03 2 1.9050483 0.1488543 StressLevel:OnlineCourses Tidak Signifikan
StressLevel:Discussions 1.232915e+02 2 0.1992749 0.8193270 StressLevel:Discussions Tidak Signifikan
StressLevel:Age 5.448340e+03 2 8.8060985 0.0001507 StressLevel:Age Signifikan *
Residuals 4.305848e+06 13919 NA NA Residuals NA

Uji Diagnostik

par(mfrow = c(2, 2))
plot(model_ancova)

par(mfrow = c(1, 1))

Estimated Marginal Means (EMMeans)

emm_list <- lapply(factors, function(f) {
  e <- as.data.frame(emmeans(model_ancova, as.formula(paste("~", f))))
  colnames(e)[1] <- "Group"
  e$Faktor <- f
  e
})

emm_all <- do.call(rbind, emm_list)

knitr::kable(emm_all, caption = "EMMeans Semua Faktor") %>%
  kable_styling(bootstrap_options = c("striped","hover","condensed"),
                font_size = 10, full_width = FALSE, position = "center")
EMMeans Semua Faktor
Gender LearningStyle Extracurricular Internet EduTech Motivation StressLevel Group emmean SE df lower.CL upper.CL Faktor
. . . . . . . Female 70.30654 0.3470559 13985 69.62626 70.98681 Gender
. . . . . . . Male 71.11332 0.3377744 13985 70.45124 71.77540 Gender
. . . . . . . Visual 69.98239 0.4036994 13985 69.19108 70.77369 LearningStyle
. . . . . . . Auditory 71.30559 0.4009496 13985 70.51968 72.09151 LearningStyle
. . . . . . . Reading 70.81858 0.4019067 13985 70.03079 71.60637 LearningStyle
. . . . . . . Kinesthetic 70.73315 0.4020014 13985 69.94517 71.52113 LearningStyle
. . . . . . . No 70.89017 0.3575050 13985 70.18941 71.59093 Extracurricular
. . . . . . . Yes 70.52969 0.3282827 13985 69.88621 71.17316 Extracurricular
. . . . . . . No 70.93470 0.5599038 13985 69.83722 72.03219 Internet
. . . . . . . Yes 70.48516 0.1935907 13985 70.10569 70.86462 Internet
. . . . . . . No 70.46852 0.3790881 13985 69.72546 71.21159 EduTech
. . . . . . . Yes 70.95133 0.3158595 13985 70.33221 71.57046 EduTech
. . . . . . . Low 70.73075 0.3787128 13985 69.98842 71.47308 Motivation
. . . . . . . Medium 70.88978 0.3359103 13985 70.23135 71.54821 Motivation
. . . . . . . High 70.50926 0.4267069 13985 69.67285 71.34566 Motivation
. . . . . . . Low 72.07028 0.4248900 13985 71.23744 72.90312 StressLevel
. . . . . . . Medium 69.88384 0.3795660 13985 69.13984 70.62784 StressLevel
. . . . . . . High 70.17566 0.3367156 13985 69.51566 70.83567 StressLevel
plots_ancova <- lapply(factors, function(f) {
  e <- as.data.frame(emmeans(model_ancova, as.formula(paste("~", f))))
  colnames(e)[1] <- "Group"
  ggplot(e, aes(x = Group, y = emmean)) +
    geom_col(fill = "#84A7D4", width = 0.5) +
    geom_errorbar(aes(ymin = lower.CL, ymax = upper.CL), width = 0.2) +
    labs(title = paste("EMMeans:", f), y = "Mean") +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 20, hjust = 1))
})

ggarrange(plotlist = plots_ancova, ncol = 3, nrow = 3)

pairwise_list <- lapply(factors, function(f) {
  pairs(emmeans(model_ancova, as.formula(paste("~", f))))
})
pairwise_list
## [[1]]
##  contrast      estimate    SE    df t.ratio p.value
##  Female - Male   -0.807 0.301 13985  -2.685  0.0073
## 
## Results are averaged over the levels of: LearningStyle, Extracurricular, Internet, EduTech, Motivation, StressLevel, Discussions 
## 
## [[2]]
##  contrast               estimate    SE    df t.ratio p.value
##  Visual - Auditory       -1.3232 0.424 13985  -3.120  0.0098
##  Visual - Reading        -0.8362 0.426 13985  -1.961  0.2029
##  Visual - Kinesthetic    -0.7508 0.425 13985  -1.765  0.2902
##  Auditory - Reading       0.4870 0.421 13985   1.158  0.6534
##  Auditory - Kinesthetic   0.5724 0.419 13985   1.366  0.5209
##  Reading - Kinesthetic    0.0854 0.421 13985   0.203  0.9970
## 
## Results are averaged over the levels of: Gender, Extracurricular, Internet, EduTech, Motivation, StressLevel, Discussions 
## P value adjustment: tukey method for comparing a family of 4 estimates 
## 
## [[3]]
##  contrast estimate    SE    df t.ratio p.value
##  No - Yes     0.36 0.304 13985   1.186  0.2357
## 
## Results are averaged over the levels of: Gender, LearningStyle, Internet, EduTech, Motivation, StressLevel, Discussions 
## 
## [[4]]
##  contrast estimate    SE    df t.ratio p.value
##  No - Yes     0.45 0.568 13985   0.791  0.4291
## 
## Results are averaged over the levels of: Gender, LearningStyle, Extracurricular, EduTech, Motivation, StressLevel, Discussions 
## 
## [[5]]
##  contrast estimate    SE    df t.ratio p.value
##  No - Yes   -0.483 0.329 13985  -1.468  0.1422
## 
## Results are averaged over the levels of: Gender, LearningStyle, Extracurricular, Internet, Motivation, StressLevel, Discussions 
## 
## [[6]]
##  contrast      estimate    SE    df t.ratio p.value
##  Low - Medium    -0.159 0.346 13985  -0.460  0.8901
##  Low - High       0.221 0.433 13985   0.512  0.8657
##  Medium - High    0.381 0.394 13985   0.965  0.5992
## 
## Results are averaged over the levels of: Gender, LearningStyle, Extracurricular, Internet, EduTech, StressLevel, Discussions 
## P value adjustment: tukey method for comparing a family of 3 estimates 
## 
## [[7]]
##  contrast      estimate    SE    df t.ratio p.value
##  Low - Medium     2.186 0.432 13985   5.058 <0.0001
##  Low - High       1.895 0.392 13985   4.828 <0.0001
##  Medium - High   -0.292 0.348 13985  -0.839  0.6785
## 
## Results are averaged over the levels of: Gender, LearningStyle, Extracurricular, Internet, EduTech, Motivation, Discussions 
## P value adjustment: tukey method for comparing a family of 3 estimates

MANCOVA

Model MANCOVA

model_mancova <- lm(
  cbind(ExamScore, FinalGrade_num) ~
    Gender + LearningStyle + Extracurricular +
    Internet + EduTech + Motivation + StressLevel +
    StudyHours + Attendance + AssignmentCompletion +
    OnlineCourses + Discussions + Age,
  data = data
)

summary(model_mancova)
## Response ExamScore :
## 
## Call:
## lm(formula = ExamScore ~ Gender + LearningStyle + Extracurricular + 
##     Internet + EduTech + Motivation + StressLevel + StudyHours + 
##     Attendance + AssignmentCompletion + OnlineCourses + Discussions + 
##     Age, data = data)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -34.813 -15.097  -0.168  15.448  32.856 
## 
## Coefficients:
##                          Estimate Std. Error t value Pr(>|t|)    
## (Intercept)              71.25686    1.93283  36.867  < 2e-16 ***
## GenderMale                0.80678    0.30052   2.685 0.007270 ** 
## LearningStyleAuditory     1.32320    0.42409   3.120 0.001812 ** 
## LearningStyleReading      0.83620    0.42638   1.961 0.049880 *  
## LearningStyleKinesthetic  0.75076    0.42527   1.765 0.077522 .  
## ExtracurricularYes       -0.36048    0.30396  -1.186 0.235657    
## InternetYes              -0.44955    0.56848  -0.791 0.429084    
## EduTechYes                0.48281    0.32891   1.468 0.142151    
## MotivationMedium          0.15903    0.34608   0.460 0.645863    
## MotivationHigh           -0.22149    0.43299  -0.512 0.608979    
## StressLevelMedium        -2.18644    0.43224  -5.058 4.28e-07 ***
## StressLevelHigh          -1.89462    0.39243  -4.828 1.39e-06 ***
## StudyHours                0.01815    0.02536   0.716 0.474247    
## Attendance               -0.02569    0.01304  -1.970 0.048859 *  
## AssignmentCompletion      0.03068    0.01020   3.007 0.002640 ** 
## OnlineCourses             0.06213    0.02441   2.545 0.010924 *  
## Discussions              -1.06216    0.30572  -3.474 0.000514 ***
## Age                      -0.03423    0.04257  -0.804 0.421385    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 17.65 on 13985 degrees of freedom
## Multiple R-squared:  0.005882,   Adjusted R-squared:  0.004673 
## F-statistic: 4.867 on 17 and 13985 DF,  p-value: 1.35e-10
## 
## 
## Response FinalGrade_num :
## 
## Call:
## lm(formula = FinalGrade_num ~ Gender + LearningStyle + Extracurricular + 
##     Internet + EduTech + Motivation + StressLevel + StudyHours + 
##     Attendance + AssignmentCompletion + OnlineCourses + Discussions + 
##     Age, data = data)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1.7017 -1.2958 -0.2696  0.7084  1.8693 
## 
## Coefficients:
##                            Estimate Std. Error t value Pr(>|t|)    
## (Intercept)               2.4772700  0.1224234  20.235  < 2e-16 ***
## GenderMale               -0.0539421  0.0190345  -2.834 0.004605 ** 
## LearningStyleAuditory    -0.0891224  0.0268615  -3.318 0.000909 ***
## LearningStyleReading     -0.0769923  0.0270064  -2.851 0.004366 ** 
## LearningStyleKinesthetic -0.0546092  0.0269361  -2.027 0.042644 *  
## ExtracurricularYes        0.0227062  0.0192524   1.179 0.238262    
## InternetYes               0.0343014  0.0360071   0.953 0.340795    
## EduTechYes               -0.0386085  0.0208329  -1.853 0.063868 .  
## MotivationMedium         -0.0128778  0.0219203  -0.587 0.556890    
## MotivationHigh            0.0182291  0.0274249   0.665 0.506258    
## StressLevelMedium         0.1614438  0.0273774   5.897 3.79e-09 ***
## StressLevelHigh           0.1405766  0.0248559   5.656 1.58e-08 ***
## StudyHours               -0.0021210  0.0016064  -1.320 0.186749    
## Attendance                0.0012679  0.0008259   1.535 0.124762    
## AssignmentCompletion     -0.0023216  0.0006463  -3.592 0.000329 ***
## OnlineCourses            -0.0039434  0.0015460  -2.551 0.010760 *  
## Discussions               0.0919771  0.0193640   4.750 2.06e-06 ***
## Age                       0.0008538  0.0026964   0.317 0.751515    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1.118 on 13985 degrees of freedom
## Multiple R-squared:  0.008012,   Adjusted R-squared:  0.006806 
## F-statistic: 6.644 on 17 and 13985 DF,  p-value: 3.982e-16

Hasil Uji MANCOVA — Pillai’s Trace

mancova_res <- car::Manova(model_mancova, test.statistic = "Pillai")
SSPE  <- mancova_res$SSPE
terms <- mancova_res$terms
df_h  <- mancova_res$df
df_e  <- mancova_res$error.df
p_dv  <- nrow(SSPE)

tabel_mancova <- do.call(rbind, lapply(seq_along(terms), function(i) {
  SSH    <- mancova_res$SSP[[i]]
  vh     <- df_h[i]
  ve     <- df_e

  pillai   <- sum(diag(SSH %*% solve(SSH + SSPE)))
  s        <- min(p_dv, vh)
  m        <- (abs(p_dv - vh) - 1) / 2
  nn       <- (ve - p_dv - 1) / 2
  approx_f <- ((2*nn + s + 1) / (2*m + s + 1)) * (pillai / (s - pillai))
  num_df   <- s * (2*m + s + 1)
  den_df   <- s * (2*nn + s + 1)
  p_val    <- pf(approx_f, num_df, den_df, lower.tail = FALSE)

  data.frame(Efek     = terms[i],
             Pillai   = round(pillai, 4),
             Approx_F = round(approx_f, 4),
             Num_Df   = round(num_df, 0),
             Den_Df   = round(den_df, 0),
             p_value  = round(p_val, 4),
             row.names = NULL)
}))

tabel_mancova |>
  kable(caption  = "Tabel Hasil Uji MANCOVA (Pillai's Trace)",
        col.names = c("Efek","Pillai's Trace","Approx F","Num Df","Den Df","p-value"),
        align     = "lccccc") |>
  kable_styling(bootstrap_options = c("striped","hover","bordered"), full_width = TRUE) |>
  row_spec(which(tabel_mancova$p_value < 0.05),
           bold = TRUE, color = "white", background = "#2980B9") |>
  footnote(general = "Baris biru = efek signifikan secara multivariat (p < 0.05)")
Tabel Hasil Uji MANCOVA (Pillai’s Trace)
Efek Pillai’s Trace Approx F Num Df Den Df p-value
Gender 0.0006 4.0436 2 13984 0.0176
LearningStyle 0.0018 4.2470 6 27970 0.0003
Extracurricular 0.0001 0.7109 2 13984 0.4912
Internet 0.0001 0.5926 2 13984 0.5529
EduTech 0.0004 2.5718 2 13984 0.0764
Motivation 0.0002 0.6345 4 27970 0.6379
StressLevel 0.0035 12.1748 4 27970 0.0000
StudyHours 0.0005 3.4102 2 13984 0.0331
Attendance 0.0004 3.0513 2 13984 0.0473
AssignmentCompletion 0.0012 8.2309 2 13984 0.0003
OnlineCourses 0.0005 3.2987 2 13984 0.0370
Discussions 0.0031 21.4220 2 13984 0.0000
Age 0.0003 2.0329 2 13984 0.1310
Note:
Baris biru = efek signifikan secara multivariat (p < 0.05)

Cara baca: Jika p-value < 0.05, efek tersebut berpengaruh signifikan terhadap kombinasi ExamScore + FinalGrade secara bersama-sama, setelah mengontrol variabel lain dalam model.

Effect Size (Partial Eta Squared)

es_exam <- eta_squared(
  lm(ExamScore ~ Gender + LearningStyle + Extracurricular +
       Internet + EduTech + Motivation + StressLevel +
       StudyHours + Attendance + AssignmentCompletion +
       OnlineCourses + Discussions + Age, data = data),
  partial = TRUE
)

es_grade <- eta_squared(
  lm(FinalGrade_num ~ Gender + LearningStyle + Extracurricular +
       Internet + EduTech + Motivation + StressLevel +
       StudyHours + Attendance + AssignmentCompletion +
       OnlineCourses + Discussions + Age, data = data),
  partial = TRUE
)

cat("=== ExamScore ===\n")
## === ExamScore ===
print(es_exam)
## # Effect Size for ANOVA (Type I)
## 
## Parameter            | Eta2 (partial) |       95% CI
## ----------------------------------------------------
## Gender               |       4.58e-04 | [0.00, 1.00]
## LearningStyle        |       7.50e-04 | [0.00, 1.00]
## Extracurricular      |       1.08e-04 | [0.00, 1.00]
## Internet             |       3.12e-05 | [0.00, 1.00]
## EduTech              |       1.50e-04 | [0.00, 1.00]
## Motivation           |       6.21e-05 | [0.00, 1.00]
## StressLevel          |       2.05e-03 | [0.00, 1.00]
## StudyHours           |       2.41e-05 | [0.00, 1.00]
## Attendance           |       2.56e-04 | [0.00, 1.00]
## AssignmentCompletion |       6.66e-04 | [0.00, 1.00]
## OnlineCourses        |       4.58e-04 | [0.00, 1.00]
## Discussions          |       8.54e-04 | [0.00, 1.00]
## Age                  |       4.62e-05 | [0.00, 1.00]
## 
## - One-sided CIs: upper bound fixed at [1.00].
cat("\n=== FinalGrade ===\n")
## 
## === FinalGrade ===
print(es_grade)
## # Effect Size for ANOVA (Type I)
## 
## Parameter            | Eta2 (partial) |       95% CI
## ----------------------------------------------------
## Gender               |       5.11e-04 | [0.00, 1.00]
## LearningStyle        |       9.81e-04 | [0.00, 1.00]
## Extracurricular      |       1.08e-04 | [0.00, 1.00]
## Internet             |       4.74e-05 | [0.00, 1.00]
## EduTech              |       2.45e-04 | [0.00, 1.00]
## Motivation           |       1.09e-04 | [0.00, 1.00]
## StressLevel          |       2.80e-03 | [0.00, 1.00]
## StudyHours           |       9.72e-05 | [0.00, 1.00]
## Attendance           |       1.53e-04 | [0.00, 1.00]
## AssignmentCompletion |       9.43e-04 | [0.00, 1.00]
## OnlineCourses        |       4.59e-04 | [0.00, 1.00]
## Discussions          |       1.61e-03 | [0.00, 1.00]
## Age                  |       7.17e-06 | [0.00, 1.00]
## 
## - One-sided CIs: upper bound fixed at [1.00].

Pedoman: η²p < 0.06 = kecil | 0.06–0.14 = sedang | > 0.14 = besar