# Nama : Desy Ulfayanti Kalauw
# NIM : 2611018046
# =============================================================================
# REPEATED MEASURE ANALYSIS DENGAN R
# Contoh terapan: Program intervensi Diabetes Melitus dan Gula Darah Sewaktu
# pada pasien Diabetes Melitus Tipe 2 di puskesmas (DATA SIMULASI)
# =============================================================================
#
# Isi:
# 0. Paket & pengaturan
# 1. Simulasi data (format panjang & lebar)
# 2. Eksplorasi data: statistik deskriptif, profile plot, spaghetti plot
# 3. Repeated Measure ANOVA satu arah (within-subject: Waktu)
# 3a. Uji asumsi: outlier, normalitas, sfierisitas (Mauchly)
# 3b. ANOVA + koreksi Greenhouse-Geisser / Huynh-Feldt
# 3c. Pendekatan multivariat (MANOVA) sebagai pembanding
# 3d. Post hoc berpasangan & kontras polinomial (tren)
# 3e. Alternatif nonparametrik: uji Friedman
# 4. Mixed Design ANOVA (between: Kelompok x within: Waktu)
# 4a. Uji asumsi: outlier, normalitas, Levene, Box's M, Mauchly
# 4b. ANOVA campuran + ukuran efek
# 4c. Analisis efek sederhana (simple effects) & post hoc
# 4d. Kontras interaksi (perubahan dari baseline antarkelompok)
# 5. Pembanding: Linear Mixed Model (LMM)
# 6. Menyimpan data & session info
# =============================================================================
# 0. PAKET & PENGATURAN
# Jalankan sekali bila paket belum terpasang:
# install.packages(c("tidyverse", "afex", "emmeans", "rstatix", "car",
# "effectsize", "lme4", "lmerTest", "performance", "ggpubr"))
suppressPackageStartupMessages({
library(dplyr) # manipulasi data
library(tidyr) # format panjang <-> lebar
library(ggplot2) # grafik
library(afex) # ANOVA within/mixed
library(emmeans) # rerata marginal, post hoc, kontras
library(rstatix) # uji asumsi
library(car) # Levene/Anova
library(effectsize) # ukuran efek
library(lme4) # linear mixed model
library(lmerTest) # uji F/t LMM
})
options(contrasts = c("contr.sum", "contr.poly"))
afex_options(emmeans_model = "multivariate")
theme_set(theme_bw(base_size = 12))
# 1. SIMULASI DATA
# Skenario:
# 90 pasien Diabetes Melitus Tipe 2 dibagi menjadi tiga kelompok
# (n = 30 per kelompok):
# - Kontrol : edukasi standar pengelolaan diabetes
# - Diet : edukasi diet diabetes terstruktur
# - Diet+Aktivitas: edukasi diet + aktivitas fisik terstruktur
#
# Gula Darah Sewaktu (GDS, mg/dL) diukur pada minggu ke-0, 4, 8, dan 12.
# Nilai dibuat realistis sebagai data simulasi untuk latihan analisis statistik.
set.seed(2026)
n_per <- 30
kel_lab <- c("Kontrol", "Diet", "Diet+Aktivitas")
minggu <- c(0, 4, 8, 12)
# Rerata populasi GDS (mg/dL) per kelompok x waktu
# Semakin efektif intervensi, semakin besar penurunan GDS.
mu <- rbind(
"Kontrol" = c(245, 240, 237, 235),
"Diet" = c(245, 225, 210, 198),
"Diet+Aktivitas" = c(245, 215, 195, 178)
)
# Komponen variasi individual
sd_int <- 25 # variasi kadar GDS dasar antarpasien
sd_slope <- 1.20 # variasi respons perubahan per minggu
sd_eps <- 14 # galat pengukuran
# Membentuk data format wide
dat_wide <- lapply(seq_along(kel_lab), function(g) {
id <- (g - 1) * n_per + seq_len(n_per)
# efek acak individu
b0 <- rnorm(n_per, 0, sd_int)
b1 <- rnorm(n_per, 0, sd_slope)
y <- sapply(seq_along(minggu), function(t) {
mu[g, t] + b0 + b1 * minggu[t] + rnorm(n_per, 0, sd_eps)
})
colnames(y) <- paste0("GDS_M", minggu)
data.frame(
id = sprintf("DM%03d", id),
kelompok = kel_lab[g],
usia = round(runif(n_per, 40, 70)),
jk = sample(c("L", "P"), n_per, replace = TRUE, prob = c(.45, .55)),
lama_dm = round(runif(n_per, 1, 12), 1),
round(y, 1)
)
}) |> bind_rows()
# Mengubah tipe variabel menjadi faktor
dat_wide$kelompok <- factor(dat_wide$kelompok, levels = kel_lab)
dat_wide$id <- factor(dat_wide$id)
# Mengubah data wide menjadi format long
# Satu baris = satu pengukuran pada satu pasien
dat_long <- dat_wide |>
pivot_longer(
starts_with("GDS_M"),
names_to = "waktu",
values_to = "gds"
) |>
mutate(
waktu = factor(
waktu,
levels = paste0("GDS_M", minggu),
labels = paste0("M", minggu)
),
minggu = as.numeric(sub("M", "", waktu))
)
# Melihat struktur data
head(dat_wide)
## id kelompok usia jk lama_dm GDS_M0 GDS_M4 GDS_M8 GDS_M12
## 1 DM001 Kontrol 47 L 3.1 256.0 223.1 271.6 240.7
## 2 DM002 Kontrol 48 L 1.6 220.4 234.4 241.5 215.1
## 3 DM003 Kontrol 67 P 9.6 245.7 235.6 241.3 248.7
## 4 DM004 Kontrol 45 P 5.7 238.8 253.2 250.6 244.7
## 5 DM005 Kontrol 58 P 4.0 204.1 198.7 197.7 229.0
## 6 DM006 Kontrol 47 L 4.2 178.3 179.0 191.8 204.9
head(dat_long)
## # A tibble: 6 × 8
## id kelompok usia jk lama_dm waktu gds minggu
## <fct> <fct> <dbl> <chr> <dbl> <fct> <dbl> <dbl>
## 1 DM001 Kontrol 47 L 3.1 M0 256 0
## 2 DM001 Kontrol 47 L 3.1 M4 223. 4
## 3 DM001 Kontrol 47 L 3.1 M8 272. 8
## 4 DM001 Kontrol 47 L 3.1 M12 241. 12
## 5 DM002 Kontrol 48 L 1.6 M0 220. 0
## 6 DM002 Kontrol 48 L 1.6 M4 234. 4
str(dat_long)
## tibble [360 × 8] (S3: tbl_df/tbl/data.frame)
## $ id : Factor w/ 90 levels "DM001","DM002",..: 1 1 1 1 2 2 2 2 3 3 ...
## $ kelompok: Factor w/ 3 levels "Kontrol","Diet",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ usia : num [1:360] 47 47 47 47 48 48 48 48 67 67 ...
## $ jk : chr [1:360] "L" "L" "L" "L" ...
## $ lama_dm : num [1:360] 3.1 3.1 3.1 3.1 1.6 1.6 1.6 1.6 9.6 9.6 ...
## $ waktu : Factor w/ 4 levels "M0","M4","M8",..: 1 2 3 4 1 2 3 4 1 2 ...
## $ gds : num [1:360] 256 223 272 241 220 ...
## $ minggu : num [1:360] 0 4 8 12 0 4 8 12 0 4 ...
# 2. EKSPLORASI DATA
# Statistik deskriptif GDS: mean dan SD per kelompok dan waktu
desk <- dat_long |>
group_by(kelompok, waktu) |>
get_summary_stats(gds, type = "mean_sd")
desk
## # A tibble: 12 × 6
## kelompok waktu variable n mean sd
## <fct> <fct> <fct> <dbl> <dbl> <dbl>
## 1 Kontrol M0 gds 30 244. 34.0
## 2 Kontrol M4 gds 30 234. 30.7
## 3 Kontrol M8 gds 30 238. 26.5
## 4 Kontrol M12 gds 30 231. 29.8
## 5 Diet M0 gds 30 250. 24.2
## 6 Diet M4 gds 30 231. 24.6
## 7 Diet M8 gds 30 216. 26.5
## 8 Diet M12 gds 30 201. 28.2
## 9 Diet+Aktivitas M0 gds 30 254. 38.7
## 10 Diet+Aktivitas M4 gds 30 225. 33.5
## 11 Diet+Aktivitas M8 gds 30 207. 37.6
## 12 Diet+Aktivitas M12 gds 30 188. 36.4
# Matriks kovarians dan korelasi antarwaktu
S <- cov(dat_wide[, paste0("GDS_M", minggu)])
R <- cor(dat_wide[, paste0("GDS_M", minggu)])
round(S, 1)
## GDS_M0 GDS_M4 GDS_M8 GDS_M12
## GDS_M0 1072.5 762.6 643.3 620.6
## GDS_M4 762.6 885.5 731.0 814.6
## GDS_M8 643.3 731.0 1085.6 974.7
## GDS_M12 620.6 814.6 974.7 1317.2
round(R, 2)
## GDS_M0 GDS_M4 GDS_M8 GDS_M12
## GDS_M0 1.00 0.78 0.60 0.52
## GDS_M4 0.78 1.00 0.75 0.75
## GDS_M8 0.60 0.75 1.00 0.82
## GDS_M12 0.52 0.75 0.82 1.00
# Varians selisih antarpasangan waktu
# Digunakan sebagai gambaran awal asumsi sfierisitas
pasangan <- combn(paste0("GDS_M", minggu), 2)
var_selisih <- apply(
pasangan,
2,
function(p) var(dat_wide[[p[1]]] - dat_wide[[p[2]]])
)
names(var_selisih) <- apply(pasangan, 2, paste, collapse = " - ")
round(var_selisih, 1)
## GDS_M0 - GDS_M4 GDS_M0 - GDS_M8 GDS_M0 - GDS_M12 GDS_M4 - GDS_M8
## 432.8 871.6 1148.4 509.1
## GDS_M4 - GDS_M12 GDS_M8 - GDS_M12
## 573.5 453.5
# Profile plot: rerata +/- 95% CI per kelompok
p_profil <- ggplot(
dat_long,
aes(x = minggu, y = gds, colour = kelompok, group = kelompok)
) +
stat_summary(fun = mean, geom = "line", linewidth = 1) +
stat_summary(fun = mean, geom = "point", size = 2.5) +
stat_summary(fun.data = mean_cl_normal, geom = "errorbar", width = .6) +
scale_x_continuous(breaks = minggu) +
labs(
x = "Minggu ke-",
y = "Gula Darah Sewaktu (mg/dL)",
colour = "Kelompok",
title = "Profil Rerata GDS (+/- 95% CI)"
) +
theme(legend.position = "bottom")
p_profil
## Warning: Computation failed in `stat_summary()`.
## Caused by error in `fun.data()`:
## ! The package "Hmisc" is required.

# Spaghetti plot: lintasan GDS tiap pasien
p_spag <- ggplot(dat_long, aes(x = minggu, y = gds, group = id)) +
geom_line(alpha = .3) +
stat_summary(
aes(group = kelompok),
fun = mean,
geom = "line",
colour = "firebrick",
linewidth = 1.2
) +
facet_wrap(~ kelompok) +
scale_x_continuous(breaks = minggu) +
labs(
x = "Minggu ke-",
y = "GDS (mg/dL)",
title = "Lintasan Individu dan Rerata GDS per Kelompok"
)
p_spag

# 3. REPEATED MEASURE ANOVA SATU ARAH
# Pertanyaan:
# Apakah GDS berubah selama 12 minggu pada kelompok Diet+Aktivitas?
d1 <- droplevels(filter(dat_long, kelompok == "Diet+Aktivitas"))
d1w <- filter(dat_wide, kelompok == "Diet+Aktivitas")
# 3a. UJI ASUMSI ---------------------------------------------------------------
# (i) Outlier per waktu
# Outlier ekstrem = nilai di luar Q1-3IQR atau Q3+3IQR
d1 |>
group_by(waktu) |>
identify_outliers(gds)
## # A tibble: 1 × 10
## waktu id kelompok usia jk lama_dm gds minggu is.outlier is.extreme
## <fct> <fct> <fct> <dbl> <chr> <dbl> <dbl> <dbl> <lgl> <lgl>
## 1 M0 DM082 Diet+Aktiv… 57 L 3.1 156. 0 TRUE FALSE
# (ii) Normalitas per waktu dengan Shapiro-Wilk
d1 |>
group_by(waktu) |>
shapiro_test(gds)
## # A tibble: 4 × 4
## waktu variable statistic p
## <fct> <chr> <dbl> <dbl>
## 1 M0 gds 0.967 0.462
## 2 M4 gds 0.932 0.0545
## 3 M8 gds 0.953 0.200
## 4 M12 gds 0.931 0.0518
# Q-Q plot
ggpubr::ggqqplot(d1, "gds", facet.by = "waktu")

# (iii) Uji sfierisitas Mauchly
# anova_test akan menghasilkan ANOVA, Mauchly, serta koreksi GG/HF
aov1_rs <- anova_test(
data = d1,
dv = gds,
wid = id,
within = waktu,
effect.size = "pes"
)
aov1_rs
## ANOVA Table (type III tests)
##
## $ANOVA
## Effect DFn DFd F p p<.05 pes
## 1 waktu 3 87 110.797 1.29e-29 * 0.793
##
## $`Mauchly's Test for Sphericity`
## Effect W p p<.05
## 1 waktu 0.752 0.162
##
## $`Sphericity Corrections`
## Effect GGe DF[GG] p[GG] p[GG]<.05 HFe DF[HF] p[HF]
## 1 waktu 0.852 2.55, 74.08 1.42e-25 * 0.941 2.82, 81.85 5.27e-28
## p[HF]<.05
## 1 *
# Secara otomatis memakai koreksi GG bila Mauchly p < 0.05
get_anova_table(aov1_rs, correction = "auto")
## ANOVA Table (type III tests)
##
## Effect DFn DFd F p p<.05 pes
## 1 waktu 3 87 110.797 1.29e-29 * 0.793
# 3b. REPEATED MEASURE ANOVA DENGAN AFEX --------------------------------------
aov1 <- aov_ez(
id = "id",
dv = "gds",
data = d1,
within = "waktu",
anova_table = list(
es = c("ges", "pes"),
correction = "GG"
)
)
# Tabel utama repeated measure ANOVA
aov1
## Anova Table (Type 3 tests)
##
## Response: gds
## Effect df MSE F ges pes p.value
## 1 waktu 2.55, 74.08 249.91 110.80 *** .313 .793 <.001
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
##
## Sphericity correction method: GG
# Output lengkap termasuk Mauchly dan epsilon GG/HF
summary(aov1)
##
## Univariate Type III Repeated-Measures ANOVA Assuming Sphericity
##
## Sum Sq num Df Error SS den Df F value Pr(>F)
## (Intercept) 5726973 1 136735 29 1214.6 < 2.2e-16 ***
## waktu 70737 3 18515 87 110.8 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Mauchly Tests for Sphericity
##
## Test statistic p-value
## waktu 0.75214 0.16229
##
##
## Greenhouse-Geisser and Huynh-Feldt Corrections
## for Departure from Sphericity
##
## GG eps Pr(>F[GG])
## waktu 0.85154 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## HF eps Pr(>F[HF])
## waktu 0.9407933 5.273886e-28
# Ukuran efek tambahan
eta_squared(aov1, partial = TRUE)
## # Effect Size for ANOVA (Type III)
##
## Parameter | Eta2 (partial) | 95% CI
## -----------------------------------------
## waktu | 0.79 | [0.73, 1.00]
##
## - One-sided CIs: upper bound fixed at [1.00].
omega_squared(aov1, partial = TRUE)
## # Effect Size for ANOVA (Type III)
##
## Parameter | Omega2 (partial) | 95% CI
## -------------------------------------------
## waktu | 0.30 | [0.16, 1.00]
##
## - One-sided CIs: upper bound fixed at [1.00].
# 3c. PENDEKATAN MULTIVARIAT (MANOVA) ----------------------------------------
# Tidak memerlukan asumsi sfierisitas
# Menampilkan Pillai, Wilks, Hotelling-Lawley, dan Roy
aov1$Anova
##
## Type III Repeated Measures MANOVA Tests: Pillai test statistic
## Df test stat approx F num Df den Df Pr(>F)
## (Intercept) 1 0.97668 1214.62 1 29 < 2.2e-16 ***
## waktu 1 0.88557 69.65 3 27 7.857e-13 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# 3d. POST HOC DAN KONTRAS TREN ------------------------------------------------
# Estimated Marginal Means tiap waktu
em1 <- emmeans(aov1, ~ waktu)
em1
## waktu emmean SE df lower.CL upper.CL
## M0 254 7.06 29 240 268
## M4 225 6.11 29 212 237
## M8 207 6.86 29 193 221
## M12 188 6.65 29 174 201
##
## Confidence level used: 0.95
# Semua pasangan waktu
pairs(em1, adjust = "bonferroni")
## contrast estimate SE df t.ratio p.value
## M0 - M4 29.4 3.23 29 9.102 <0.0001
## M0 - M8 46.6 4.29 29 10.862 <0.0001
## M0 - M12 66.1 4.46 29 14.818 <0.0001
## M4 - M8 17.2 3.61 29 4.758 0.0003
## M4 - M12 36.7 3.25 29 11.298 <0.0001
## M8 - M12 19.5 3.57 29 5.461 <0.0001
##
## P value adjustment: bonferroni method for 6 tests
# Membandingkan setiap waktu dengan baseline (M0)
contrast(
em1,
"trt.vs.ctrl",
ref = 1,
adjust = "holm"
)
## contrast estimate SE df t.ratio p.value
## M4 - M0 -29.4 3.23 29 -9.102 <0.0001
## M8 - M0 -46.6 4.29 29 -10.862 <0.0001
## M12 - M0 -66.1 4.46 29 -14.818 <0.0001
##
## P value adjustment: holm method for 3 tests
# Tren linear, kuadratik, dan kubik
contrast(em1, "poly")
## contrast estimate SE df t.ratio p.value
## linear -215.53 14.50 29 -14.890 <0.0001
## quadratic 9.92 4.38 29 2.264 0.0312
## cubic -14.53 11.00 29 -1.326 0.1952
# 3e. ALTERNATIF NONPARAMETRIK -------------------------------------------------
# Uji Friedman
friedman_test(d1, gds ~ waktu | id)
## # A tibble: 1 × 6
## .y. n statistic df p method
## * <chr> <int> <dbl> <dbl> <dbl> <chr>
## 1 gds 30 74.5 3 4.59e-16 Friedman test
# Ukuran efek Kendall's W
friedman_effsize(d1, gds ~ waktu | id)
## # A tibble: 1 × 5
## .y. n effsize method magnitude
## * <chr> <int> <dbl> <chr> <ord>
## 1 gds 30 0.828 Kendall W large
# Post hoc Wilcoxon berpasangan
d1 |>
wilcox_test(
gds ~ waktu,
paired = TRUE,
p.adjust.method = "bonferroni"
)
## # A tibble: 6 × 9
## .y. group1 group2 n1 n2 statistic p p.adj p.adj.signif
## * <chr> <chr> <chr> <int> <int> <dbl> <dbl> <dbl> <chr>
## 1 gds M0 M4 30 30 458 0.0000000354 2.12e-7 ****
## 2 gds M0 M8 30 30 465 0.00000000186 1.12e-8 ****
## 3 gds M0 M12 30 30 465 0.00000000186 1.12e-8 ****
## 4 gds M4 M8 30 30 416 0.0000493 2.96e-4 ***
## 5 gds M4 M12 30 30 465 0.00000000186 1.12e-8 ****
## 6 gds M8 M12 30 30 430 0.00000799 4.80e-5 ****
# ANOVA robust berbasis trimmed mean (opsional)
if (requireNamespace("WRS2", quietly = TRUE)) {
print(WRS2::rmanova(d1$gds, d1$waktu, d1$id, tr = 0.2))
}
## Call:
## WRS2::rmanova(y = d1$gds, groups = d1$waktu, blocks = d1$id,
## tr = 0.2)
##
## Test statistic: F = 66.3848
## Degrees of freedom 1: 2.85
## Degrees of freedom 2: 48.52
## p-value: 0
# 4. MIXED DESIGN ANOVA
# Between-subject = Kelompok
# Within-subject = Waktu
#
# Pertanyaan:
# Apakah pola perubahan GDS selama 12 minggu berbeda antarkelompok?
# 4a. UJI ASUMSI ---------------------------------------------------------------
# (i) Outlier per kelompok x waktu
dat_long |>
group_by(kelompok, waktu) |>
identify_outliers(gds)
## # A tibble: 7 × 10
## kelompok waktu id usia jk lama_dm gds minggu is.outlier is.extreme
## <fct> <fct> <fct> <dbl> <chr> <dbl> <dbl> <dbl> <lgl> <lgl>
## 1 Kontrol M0 DM030 55 P 1.2 322. 0 TRUE FALSE
## 2 Kontrol M8 DM015 62 P 10.4 172. 8 TRUE FALSE
## 3 Kontrol M12 DM015 62 P 10.4 154. 12 TRUE FALSE
## 4 Diet M0 DM042 47 P 8.9 183. 0 TRUE FALSE
## 5 Diet M8 DM042 47 P 8.9 156. 8 TRUE FALSE
## 6 Diet M8 DM060 58 L 6.4 283. 8 TRUE FALSE
## 7 Diet+Aktiv… M0 DM082 57 L 3.1 156. 0 TRUE FALSE
# (ii) Normalitas setiap sel (3 kelompok x 4 waktu = 12 sel)
dat_long |>
group_by(kelompok, waktu) |>
shapiro_test(gds)
## # A tibble: 12 × 5
## kelompok waktu variable statistic p
## <fct> <fct> <chr> <dbl> <dbl>
## 1 Kontrol M0 gds 0.987 0.971
## 2 Kontrol M4 gds 0.963 0.361
## 3 Kontrol M8 gds 0.969 0.511
## 4 Kontrol M12 gds 0.974 0.643
## 5 Diet M0 gds 0.966 0.444
## 6 Diet M4 gds 0.972 0.594
## 7 Diet M8 gds 0.972 0.609
## 8 Diet M12 gds 0.950 0.165
## 9 Diet+Aktivitas M0 gds 0.967 0.462
## 10 Diet+Aktivitas M4 gds 0.932 0.0545
## 11 Diet+Aktivitas M8 gds 0.953 0.200
## 12 Diet+Aktivitas M12 gds 0.931 0.0518
# Q-Q plot per kelompok dan waktu
ggpubr::ggqqplot(dat_long, "gds", ggtheme = theme_bw()) +
facet_grid(waktu ~ kelompok)

# (iii) Homogenitas varians antarkelompok pada setiap waktu
# Levene test
dat_long |>
group_by(waktu) |>
levene_test(gds ~ kelompok)
## # A tibble: 4 × 5
## waktu df1 df2 statistic p
## <fct> <int> <int> <dbl> <dbl>
## 1 M0 2 87 2.27 0.109
## 2 M4 2 87 0.939 0.395
## 3 M8 2 87 2.26 0.111
## 4 M12 2 87 1.47 0.237
# (iv) Homogenitas matriks kovarians antarkelompok
# Box's M biasanya dievaluasi secara konservatif, misalnya alpha 0.001
box_m(
dat_wide[, paste0("GDS_M", minggu)],
dat_wide$kelompok
)
## # A tibble: 1 × 4
## statistic p.value parameter method
## <dbl> <dbl> <dbl> <chr>
## 1 14.9 0.783 20 Box's M-test for Homogeneity of Covariance Matric…
# (v) Sfierisitas diperoleh dari summary model mixed ANOVA di bawah
# 4b. MIXED DESIGN ANOVA -------------------------------------------------------
aov2 <- aov_ez(
id = "id",
dv = "gds",
data = dat_long,
between = "kelompok",
within = "waktu",
anova_table = list(
es = c("ges", "pes"),
correction = "GG"
)
)
# Tabel ANOVA campuran
aov2
## Anova Table (Type 3 tests)
##
## Response: gds
## Effect df MSE F ges pes p.value
## 1 kelompok 2, 87 3199.10 3.29 * .058 .070 .042
## 2 waktu 2.65, 230.91 268.15 120.52 *** .201 .581 <.001
## 3 kelompok:waktu 5.31, 230.91 268.15 18.85 *** .073 .302 <.001
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
##
## Sphericity correction method: GG
# Mauchly, epsilon GG/HF, dan output lengkap
summary(aov2)
##
## Univariate Type III Repeated-Measures ANOVA Assuming Sphericity
##
## Sum Sq num Df Error SS den Df F value Pr(>F)
## (Intercept) 18489609 1 278322 87 5779.6305 < 2e-16 ***
## kelompok 21034 2 278322 87 3.2876 0.04203 *
## waktu 85776 3 61920 261 120.5198 < 2e-16 ***
## kelompok:waktu 26834 6 61920 261 18.8515 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Mauchly Tests for Sphericity
##
## Test statistic p-value
## waktu 0.81372 0.0033904
## kelompok:waktu 0.81372 0.0033904
##
##
## Greenhouse-Geisser and Huynh-Feldt Corrections
## for Departure from Sphericity
##
## GG eps Pr(>F[GG])
## waktu 0.88473 < 2.2e-16 ***
## kelompok:waktu 0.88473 2.233e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## HF eps Pr(>F[HF])
## waktu 0.9151557 4.470672e-45
## kelompok:waktu 0.9151557 7.305154e-17
# Pendekatan multivariat
aov2$Anova
##
## Type III Repeated Measures MANOVA Tests: Pillai test statistic
## Df test stat approx F num Df den Df Pr(>F)
## (Intercept) 1 0.98517 5779.6 1 87 < 2.2e-16 ***
## kelompok 2 0.07027 3.3 2 87 0.04203 *
## waktu 1 0.74437 82.5 3 85 < 2.2e-16 ***
## kelompok:waktu 2 0.48960 9.3 6 172 8.03e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Versi rstatix
aov2_rs <- anova_test(
data = dat_long,
dv = gds,
wid = id,
between = kelompok,
within = waktu,
effect.size = "pes",
type = 3
)
get_anova_table(aov2_rs, correction = "GG")
## ANOVA Table (type III tests)
##
## Effect DFn DFd F p p<.05 pes
## 1 kelompok 2.00 87.00 3.288 4.20e-02 * 0.070
## 2 waktu 2.65 230.91 120.520 1.14e-43 * 0.581
## 3 kelompok:waktu 5.31 230.91 18.852 2.23e-16 * 0.302
# Ukuran efek
eta_squared(aov2, partial = TRUE)
## # Effect Size for ANOVA (Type III)
##
## Parameter | Eta2 (partial) | 95% CI
## ----------------------------------------------
## kelompok | 0.07 | [0.00, 1.00]
## waktu | 0.58 | [0.52, 1.00]
## kelompok:waktu | 0.30 | [0.22, 1.00]
##
## - One-sided CIs: upper bound fixed at [1.00].
omega_squared(aov2, partial = TRUE)
## # Effect Size for ANOVA (Type III)
##
## Parameter | Omega2 (partial) | 95% CI
## ------------------------------------------------
## kelompok | 0.05 | [0.00, 1.00]
## waktu | 0.20 | [0.13, 1.00]
## kelompok:waktu | 0.07 | [0.01, 1.00]
##
## - One-sided CIs: upper bound fixed at [1.00].
# Plot interaksi kelompok x waktu
afex_plot(
aov2,
x = "waktu",
trace = "kelompok",
error = "within",
mapping = c("colour", "shape", "linetype")
) +
labs(
y = "GDS (mg/dL)",
x = "Waktu Pengukuran"
)
## Warning: Panel(s) show a mixed within-between-design.
## Error bars do not allow comparisons across all means.
## Suppress error bars with: error = "none"

# 4c. EFEK SEDERHANA (SIMPLE EFFECTS) -----------------------------------------
# Digunakan terutama bila interaksi kelompok x waktu signifikan
# Estimated marginal means waktu dalam setiap kelompok
em2 <- emmeans(aov2, ~ waktu | kelompok)
em2
## kelompok = Kontrol:
## waktu emmean SE df lower.CL upper.CL
## M0 244 5.99 87 232 256
## M4 234 5.44 87 223 245
## M8 238 5.60 87 227 249
## M12 231 5.78 87 220 243
##
## kelompok = Diet:
## waktu emmean SE df lower.CL upper.CL
## M0 250 5.99 87 238 262
## M4 231 5.44 87 220 241
## M8 216 5.60 87 205 227
## M12 201 5.78 87 190 213
##
## kelompok = Diet+Aktivitas:
## waktu emmean SE df lower.CL upper.CL
## M0 254 5.99 87 242 266
## M4 225 5.44 87 214 235
## M8 207 5.60 87 196 219
## M12 188 5.78 87 176 199
##
## Confidence level used: 0.95
# Efek waktu di dalam masing-masing kelompok
joint_tests(aov2, by = "kelompok")
## Warning in pf(conf$F.ratio, conf$df1, conf$df2, lower.tail = FALSE): NaNs
## produced
## kelompok = Kontrol:
## model term df1 df2 F.ratio p.value
## waktu 3 87 3.025 0.0338
##
## kelompok = Diet:
## model term df1 df2 F.ratio p.value
## waktu 3 87 38.975 <0.0001
##
## kelompok = Diet+Aktivitas:
## model term df1 df2 F.ratio p.value
## waktu 3 87 69.360 <0.0001
# Efek kelompok pada masing-masing waktu
joint_tests(aov2, by = "waktu")
## waktu = M0:
## model term df1 df2 F.ratio p.value
## kelompok 2 87 0.764 0.4687
##
## waktu = M4:
## model term df1 df2 F.ratio p.value
## kelompok 2 87 0.808 0.4490
##
## waktu = M8:
## model term df1 df2 F.ratio p.value
## kelompok 2 87 7.919 0.0007
##
## waktu = M12:
## model term df1 df2 F.ratio p.value
## kelompok 2 87 14.895 <0.0001
# Setiap waktu dibandingkan baseline pada masing-masing kelompok
contrast(
em2,
"trt.vs.ctrl",
ref = 1,
adjust = "holm"
)
## kelompok = Kontrol:
## contrast estimate SE df t.ratio p.value
## M4 - M0 -9.38 3.53 87 -2.659 0.0280
## M8 - M0 -5.72 4.43 87 -1.293 0.1995
## M12 - M0 -12.18 4.66 87 -2.616 0.0280
##
## kelompok = Diet:
## contrast estimate SE df t.ratio p.value
## M4 - M0 -19.85 3.53 87 -5.625 <0.0001
## M8 - M0 -34.31 4.43 87 -7.750 <0.0001
## M12 - M0 -49.02 4.66 87 -10.524 <0.0001
##
## kelompok = Diet+Aktivitas:
## contrast estimate SE df t.ratio p.value
## M4 - M0 -29.42 3.53 87 -8.338 <0.0001
## M8 - M0 -46.61 4.43 87 -10.528 <0.0001
## M12 - M0 -66.11 4.66 87 -14.193 <0.0001
##
## P value adjustment: holm method for 3 tests
# Perbandingan antarkelompok pada setiap waktu
em2b <- emmeans(aov2, ~ kelompok | waktu)
pairs(em2b, adjust = "tukey")
## waktu = M0:
## contrast estimate SE df t.ratio p.value
## Kontrol - Diet -6.71 8.48 87 -0.791 0.7095
## Kontrol - (Diet+Aktivitas) -10.33 8.48 87 -1.218 0.4456
## Diet - (Diet+Aktivitas) -3.62 8.48 87 -0.427 0.9043
##
## waktu = M4:
## contrast estimate SE df t.ratio p.value
## Kontrol - Diet 3.76 7.70 87 0.488 0.8772
## Kontrol - (Diet+Aktivitas) 9.71 7.70 87 1.261 0.4212
## Diet - (Diet+Aktivitas) 5.95 7.70 87 0.773 0.7207
##
## waktu = M8:
## contrast estimate SE df t.ratio p.value
## Kontrol - Diet 21.88 7.91 87 2.765 0.0189
## Kontrol - (Diet+Aktivitas) 30.56 7.91 87 3.861 0.0006
## Diet - (Diet+Aktivitas) 8.68 7.91 87 1.096 0.5189
##
## waktu = M12:
## contrast estimate SE df t.ratio p.value
## Kontrol - Diet 30.13 8.18 87 3.683 0.0012
## Kontrol - (Diet+Aktivitas) 43.60 8.18 87 5.330 <0.0001
## Diet - (Diet+Aktivitas) 13.47 8.18 87 1.647 0.2318
##
## P value adjustment: tukey method for comparing a family of 3 estimates
# 4d. KONTRAS INTERAKSI --------------------------------------------------------
# Membandingkan perubahan dari minggu ke-0 sampai minggu ke-12 antarkelompok
em_full <- emmeans(aov2, ~ waktu * kelompok)
contrast(
em_full,
interaction = list(
waktu = list("M12-M0" = c(-1, 0, 0, 1)),
kelompok = "pairwise"
),
adjust = "holm"
)
## waktu_custom kelompok_pairwise estimate SE df t.ratio p.value
## M12-M0 Kontrol - Diet 36.8 6.59 87 5.592 <0.0001
## M12-M0 Kontrol - (Diet+Aktivitas) 53.9 6.59 87 8.187 <0.0001
## M12-M0 Diet - (Diet+Aktivitas) 17.1 6.59 87 2.595 0.0111
##
## P value adjustment: holm method for 3 tests
# Tren polinomial waktu di setiap kelompok
contrast(em2, "poly")
## kelompok = Kontrol:
## contrast estimate SE df t.ratio p.value
## linear -32.89 15.00 87 -2.195 0.0308
## quadratic 2.92 4.76 87 0.614 0.5410
## cubic -23.16 11.70 87 -1.983 0.0505
##
## kelompok = Diet:
## contrast estimate SE df t.ratio p.value
## linear -161.53 15.00 87 -10.779 <0.0001
## quadratic 5.14 4.76 87 1.079 0.2835
## cubic -5.62 11.70 87 -0.481 0.6317
##
## kelompok = Diet+Aktivitas:
## contrast estimate SE df t.ratio p.value
## linear -215.53 15.00 87 -14.383 <0.0001
## quadratic 9.92 4.76 87 2.083 0.0402
## cubic -14.53 11.70 87 -1.244 0.2168
# Membandingkan tren linear antarkelompok
tren_int <- summary(
contrast(
em_full,
interaction = c(waktu = "poly", kelompok = "pairwise"),
adjust = "none"
)
)
tren_lin <- subset(tren_int, waktu_poly == "linear")
tren_lin$p.holm <- p.adjust(tren_lin$p.value, method = "holm")
tren_lin
## waktu_poly kelompok_pairwise estimate SE df t.ratio
## 1 linear Kontrol - Diet 128.63667 21.19312 87 6.069738
## 4 linear Kontrol - (Diet+Aktivitas) 182.64333 21.19312 87 8.618049
## 7 linear Diet - (Diet+Aktivitas) 54.00667 21.19312 87 2.548312
## p.value p.holm
## 1 3.256211e-08 6.512423e-08
## 4 2.717529e-13 8.152587e-13
## 7 1.257968e-02 1.257968e-02
# 5. PEMBANDING: LINEAR MIXED MODEL (LMM)
# LMM tidak mensyaratkan sfierisitas dan dapat menangani data hilang
# dengan lebih fleksibel dibanding repeated measure ANOVA klasik.
# Random intercept
lmm1 <- lmer(
gds ~ kelompok * waktu + (1 | id),
data = dat_long,
REML = TRUE
)
# Random intercept + random slope waktu numerik
lmm2 <- lmer(
gds ~ kelompok * waktu + (1 + minggu | id),
data = dat_long,
REML = TRUE
)
# Membandingkan struktur random effect
anova(lmm1, lmm2, refit = FALSE)
## Data: dat_long
## Models:
## lmm1: gds ~ kelompok * waktu + (1 | id)
## lmm2: gds ~ kelompok * waktu + (1 + minggu | id)
## npar AIC BIC logLik -2*log(L) Chisq Df Pr(>Chisq)
## lmm1 14 3203.1 3257.5 -1587.5 3175.1
## lmm2 16 3196.5 3258.7 -1582.2 3164.5 10.566 2 0.005077 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Uji efek tetap dengan derajat bebas Kenward-Roger
anova(lmm2, ddf = "Kenward-Roger")
## Type III Analysis of Variance Table with Kenward-Roger's method
## Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
## kelompok 1232 616.2 2 87.00 3.2875 0.04204 *
## waktu 48504 16167.9 3 185.37 85.8646 < 2.2e-16 ***
## kelompok:waktu 15119 2519.8 6 206.40 13.3674 8.722e-13 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Intraclass Correlation Coefficient
performance::icc(lmm1)
## # Intraclass Correlation Coefficient
##
## Adjusted ICC: 0.757
## Unadjusted ICC: 0.549
# Diagnostik residual LMM
par(mfrow = c(1, 3))
qqnorm(
resid(lmm2),
main = "Q-Q Residual"
)
qqline(resid(lmm2))
qqnorm(
ranef(lmm2)$id[, 1],
main = "Q-Q Intersep Acak"
)
qqline(ranef(lmm2)$id[, 1])
plot(
fitted(lmm2),
resid(lmm2),
xlab = "Nilai Prediksi",
ylab = "Residual",
main = "Residual vs Prediksi"
)
abline(h = 0, lty = 2)

par(mfrow = c(1, 1))
# SIMULASI DATA HILANG UNTUK MENUNJUKKAN KEUNGGULAN LMM
set.seed(1)
dat_miss <- dat_long
# Membuat 30 nilai GDS pasca-baseline menjadi missing secara acak
# Kira-kira 11% dari pengukuran pasca-baseline
dat_miss$gds[
sample(which(dat_miss$waktu != "M0"), 30)
] <- NA
# LMM masih dapat memakai pasien dengan sebagian observasi tersedia
lmm_miss <- lmer(
gds ~ kelompok * waktu + (1 + minggu | id),
data = dat_miss,
control = lmerControl(optimizer = "bobyqa")
)
anova(lmm_miss, ddf = "Kenward-Roger")
## Type III Analysis of Variance Table with Kenward-Roger's method
## Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
## kelompok 1324 662.1 2 86.982 3.2981 0.04162 *
## waktu 58705 19568.4 3 168.607 97.0147 < 2.2e-16 ***
## kelompok:waktu 18936 3155.9 6 186.374 15.6282 1.681e-14 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Jumlah pasien yang memiliki minimal satu data hilang
n_distinct(dat_miss$id[is.na(dat_miss$gds)])
## [1] 25
# 6. SIMPAN DATA & SESSION INFO
# Menyimpan data wide
write.csv(
dat_wide,
"data_diabetes_gds_wide.csv",
row.names = FALSE
)
# Menyimpan data long
write.csv(
dat_long,
"data_diabetes_gds_long.csv",
row.names = FALSE
)
# Menyimpan statistik deskriptif
write.csv(
desk,
"ringkasan_deskriptif_gds.csv",
row.names = FALSE
)
# Informasi versi R dan paket
sessionInfo()
## R version 4.6.1 (2026-06-24 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=English_Indonesia.utf8 LC_CTYPE=English_Indonesia.utf8
## [3] LC_MONETARY=English_Indonesia.utf8 LC_NUMERIC=C
## [5] LC_TIME=English_Indonesia.utf8
##
## time zone: Asia/Makassar
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] lmerTest_3.2-1 effectsize_1.0.3 car_3.1-5 carData_3.0-6
## [5] rstatix_1.1.0 emmeans_2.0.4 afex_1.5-1 lme4_2.0-6
## [9] Matrix_1.7-5 ggplot2_4.0.3 tidyr_1.3.2 dplyr_1.2.1
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 xfun_0.61 bslib_0.12.0
## [4] bayestestR_0.19.0 insight_1.5.4 lattice_0.22-9
## [7] numDeriv_2016.8-1.1 vctrs_0.7.3 tools_4.6.1
## [10] Rdpack_2.6.6 generics_0.1.4 pbkrtest_0.5.5
## [13] parallel_4.6.1 datawizard_1.4.0 tibble_3.3.1
## [16] pkgconfig_2.0.3 WRS2_1.1-7 RColorBrewer_1.1-3
## [19] S7_0.2.2 lifecycle_1.0.5 compiler_4.6.1
## [22] farver_2.1.2 stringr_1.6.0 htmltools_0.5.9
## [25] sass_0.4.10 yaml_2.3.12 Formula_1.2-6
## [28] ggpubr_1.0.0 pillar_1.11.1 nloptr_2.2.1
## [31] jquerylib_0.1.4 MASS_7.3-65 cachem_1.1.0
## [34] reformulas_0.4.4 boot_1.3-32 abind_1.4-8
## [37] nlme_3.1-169 tidyselect_1.2.1 digest_0.6.39
## [40] performance_0.18.2 mvtnorm_1.4-2 stringi_1.8.9
## [43] reshape2_1.4.5 purrr_1.2.2 labeling_0.4.3
## [46] splines_4.6.1 fastmap_1.2.0 grid_4.6.1
## [49] cli_3.6.6 magrittr_2.0.5 utf8_1.2.6
## [52] broom_1.0.13 withr_3.0.3 scales_1.4.0
## [55] backports_1.5.1 estimability_2.0.0 rmarkdown_2.32
## [58] ggsignif_0.6.4 evaluate_1.0.5 knitr_1.52
## [61] parameters_0.29.3 rbibutils_2.4.1 rlang_1.3.0
## [64] Rcpp_1.1.2 glue_1.8.1 reshape_0.8.10
## [67] minqa_1.2.8 jsonlite_2.0.0 R6_2.6.1
## [70] plyr_1.8.9
# =============================================================================
# CATATAN INTERPRETASI SINGKAT
# =============================================================================
# 1. Deskriptif:
# Perhatikan mean dan SD GDS tiap kelompok pada M0, M4, M8, dan M12.
#
# 2. Efek waktu:
# Jika p < 0.05, terdapat perubahan GDS yang signifikan sepanjang waktu.
#
# 3. Efek kelompok:
# Jika p < 0.05, secara keseluruhan terdapat perbedaan GDS antarkelompok.
#
# 4. Interaksi kelompok x waktu:
# Jika p < 0.05, pola perubahan GDS berbeda antarkelompok.
# Ini biasanya merupakan hasil paling penting dalam penelitian intervensi.
#
# 5. Post hoc:
# Digunakan untuk mengetahui waktu mana yang berbeda dan kelompok mana
# yang berbeda setelah hasil utama signifikan.
#
# 6. Effect size:
# Gunakan partial eta squared (pes), generalized eta squared (ges), atau
# omega squared untuk menjelaskan besar efek, bukan hanya signifikansi.
#
# 7. LMM:
# Digunakan sebagai analisis pembanding, terutama bila terdapat data hilang
# atau struktur korelasi antarwaktu yang lebih kompleks.
# =============================================================================