# Nama : P. Rupian Nur Amin
# NIM : 2611018019
#=============================================================================
#REPEATED MEASURE ANALYSIS DENGAN R
# Contoh terapan: Program edukasi diet rendah purin dan kadar asam urat
# pada pasien dengan kadar asam urat tinggi 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 & ringkasan hasil
# =============================================================================
# 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 (tipe III, koreksi GG/HF, MANOVA)
library(emmeans) # rerata marginal, efek sederhana, post hoc, kontras
library(rstatix) # uji asumsi yang ramah pipe (outlier, Shapiro, Levene, Box's M)
library(car) # leveneTest, Anova
library(effectsize) # eta kuadrat parsial, omega kuadrat
library(lme4) # linear mixed model
library(lmerTest) # uji F/t dengan derajat bebas Satterthwaite / Kenward-Roger
})
options(contrasts = c("contr.sum", "contr.poly")) # kontras jumlah-nol untuk SS tipe III
afex_options(emmeans_model = "multivariate") # post hoc memakai model multivariat (tahan terhadap non-sfierisitas)
theme_set(theme_bw(base_size = 12))
# 1. SIMULASI DATA
# Skenario: simulasi uji klinis di puskesmas. 90 pasien dengan asam urat tinggi
# diacak ke tiga kelompok (n = 30 per kelompok):
# - Kontrol : edukasi standar puskesmas
# - Diet_RP : edukasi diet rendah purin
# - Diet_RP+Konseling : edukasi diet rendah purin + konseling gizi berkala
# Kadar asam urat serum (AU, mg/dL) diukur pada minggu ke-0 (baseline), 4, 8, 12.
#
# Struktur korelasi dibangkitkan dari intersep acak + kemiringan acak per subjek,
# sehingga varians meningkat dan korelasi menurun seiring jarak waktu --
# kondisi realistis yang cenderung MELANGGAR asumsi sfierisitas.
set.seed(2026)
n_per <- 30
kel_lab <- c("Kontrol", "Diet_RP", "Diet_RP+Konseling")
minggu <- c(0, 4, 8, 12)
# Rerata SIMULASI (mg/dL) per kelompok x waktu; bukan bukti efek klinis
mu <- rbind(
"Kontrol" = c(8.50, 8.45, 8.40, 8.375),
"Diet_RP" = c(8.50, 8.275, 8.125, 8.025),
"Diet_RP+Konseling" = c(8.50, 8.175, 7.925, 7.750)
)
sd_int <- 0.45 # SD intersep acak (perbedaan asam urat dasar antarpasien)
sd_slope <- 0.0225 # SD kemiringan acak per minggu (perbedaan respons antarpasien)
sd_eps <- 0.225 # SD galat pengukuran
dat_wide <- lapply(seq_along(kel_lab), function(g) {
id <- (g - 1) * n_per + seq_len(n_per)
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("AU_M", minggu)
data.frame(id = sprintf("P%03d", id),
kelompok = kel_lab[g],
usia = round(runif(n_per, 35, 65)),
jk = sample(c("L", "P"), n_per, replace = TRUE, prob = c(.45, .55)),
round(y, 2))
}) |> bind_rows()
dat_wide$kelompok <- factor(dat_wide$kelompok, levels = kel_lab)
dat_wide$id <- factor(dat_wide$id)
# Format panjang (satu baris = satu pengukuran), dibutuhkan afex/rstatix/lme4
dat_long <- dat_wide |>
pivot_longer(starts_with("AU_M"), names_to = "waktu", values_to = "asam_urat") |>
mutate(waktu = factor(waktu, levels = paste0("AU_M", minggu),
labels = paste0("M", minggu)),
minggu = as.numeric(sub("M", "", waktu)))
head(dat_wide)
## id kelompok usia jk AU_M0 AU_M4 AU_M8 AU_M12
## 1 P001 Kontrol 42 L 8.70 8.20 8.97 8.47
## 2 P002 Kontrol 43 L 8.05 8.31 8.42 8.01
## 3 P003 Kontrol 62 P 8.52 8.38 8.47 8.59
## 4 P004 Kontrol 40 P 8.40 8.66 8.62 8.54
## 5 P005 Kontrol 53 P 7.81 7.76 7.74 8.25
## 6 P006 Kontrol 42 L 7.31 7.36 7.58 7.81
head(dat_long)
## # A tibble: 6 × 7
## id kelompok usia jk waktu asam_urat minggu
## <fct> <fct> <dbl> <chr> <fct> <dbl> <dbl>
## 1 P001 Kontrol 42 L M0 8.7 0
## 2 P001 Kontrol 42 L M4 8.2 4
## 3 P001 Kontrol 42 L M8 8.97 8
## 4 P001 Kontrol 42 L M12 8.47 12
## 5 P002 Kontrol 43 L M0 8.05 0
## 6 P002 Kontrol 43 L M4 8.31 4
str(dat_long)
## tibble [360 × 7] (S3: tbl_df/tbl/data.frame)
## $ id : Factor w/ 90 levels "P001","P002",..: 1 1 1 1 2 2 2 2 3 3 ...
## $ kelompok : Factor w/ 3 levels "Kontrol","Diet_RP",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ usia : num [1:360] 42 42 42 42 43 43 43 43 62 62 ...
## $ jk : chr [1:360] "L" "L" "L" "L" ...
## $ waktu : Factor w/ 4 levels "M0","M4","M8",..: 1 2 3 4 1 2 3 4 1 2 ...
## $ asam_urat: num [1:360] 8.7 8.2 8.97 8.47 8.05 8.31 8.42 8.01 8.52 8.38 ...
## $ minggu : num [1:360] 0 4 8 12 0 4 8 12 0 4 ...
# 2. EKSPLORASI DATA
desk <- dat_long |>
group_by(kelompok, waktu) |>
get_summary_stats(asam_urat, type = "mean_sd")
desk
## # A tibble: 12 × 6
## kelompok waktu variable n mean sd
## <fct> <fct> <fct> <dbl> <dbl> <dbl>
## 1 Kontrol M0 asam_urat 30 8.48 0.594
## 2 Kontrol M4 asam_urat 30 8.35 0.54
## 3 Kontrol M8 asam_urat 30 8.41 0.476
## 4 Kontrol M12 asam_urat 30 8.31 0.536
## 5 Diet_RP M0 asam_urat 30 8.57 0.472
## 6 Diet_RP M4 asam_urat 30 8.35 0.525
## 7 Diet_RP M8 asam_urat 30 8.18 0.485
## 8 Diet_RP M12 asam_urat 30 8.08 0.496
## 9 Diet_RP+Konseling M0 asam_urat 30 8.53 0.588
## 10 Diet_RP+Konseling M4 asam_urat 30 8.21 0.595
## 11 Diet_RP+Konseling M8 asam_urat 30 7.99 0.642
## 12 Diet_RP+Konseling M12 asam_urat 30 7.87 0.722
# Matriks kovarians & korelasi antarwaktu (seluruh subjek, dalam kelompok)
# -> memberi gambaran awal apakah sfierisitas masuk akal
S <- cov(dat_wide[, paste0("AU_M", minggu)])
R <- cor(dat_wide[, paste0("AU_M", minggu)])
round(S, 1); round(R, 2)
## AU_M0 AU_M4 AU_M8 AU_M12
## AU_M0 0.3 0.2 0.2 0.2
## AU_M4 0.2 0.3 0.3 0.3
## AU_M8 0.2 0.3 0.3 0.3
## AU_M12 0.2 0.3 0.3 0.4
## AU_M0 AU_M4 AU_M8 AU_M12
## AU_M0 1.00 0.79 0.72 0.64
## AU_M4 0.79 1.00 0.81 0.77
## AU_M8 0.72 0.81 1.00 0.85
## AU_M12 0.64 0.77 0.85 1.00
# Varians selisih antarpasangan waktu (inti asumsi sfierisitas)
pasangan <- combn(paste0("AU_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)
## AU_M0 - AU_M4 AU_M0 - AU_M8 AU_M0 - AU_M12 AU_M4 - AU_M8 AU_M4 - AU_M12
## 0.1 0.2 0.2 0.1 0.2
## AU_M8 - AU_M12
## 0.1
# Profile plot: rerata +/- 95% CI per kelompok
p_profil <- ggplot(dat_long, aes(minggu, asam_urat, 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 = "Kadar asam urat serum (mg/dL)", colour = "Kelompok",
title = "Profil rerata asam urat (± 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 tiap pasien
p_spag <- ggplot(dat_long, aes(minggu, asam_urat, 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 = "asam urat (mg/dL)", title = "Lintasan individu dan rerata kelompok")
p_spag

# 3. REPEATED MEASURE ANOVA SATU ARAH
# Pertanyaan: apakah asam urat berubah selama 12 minggu pada kelompok Diet_RP+Konseling?
d1 <- droplevels(filter(dat_long, kelompok == "Diet_RP+Konseling"))
d1w <- filter(dat_wide, kelompok == "Diet_RP+Konseling")
## 3a. Uji asumsi -------------------------------------------------------------
# (i) Outlier per waktu (ekstrem = di luar Q1-3IQR / Q3+3IQR)
d1 |> group_by(waktu) |> identify_outliers(asam_urat)
## [1] waktu id kelompok usia jk asam_urat minggu
## [8] is.outlier is.extreme
## <0 rows> (or 0-length row.names)
# (ii) Normalitas per waktu (Shapiro-Wilk) dan Q-Q plot
d1 |> group_by(waktu) |> shapiro_test(asam_urat)
## # A tibble: 4 × 4
## waktu variable statistic p
## <fct> <chr> <dbl> <dbl>
## 1 M0 asam_urat 0.959 0.298
## 2 M4 asam_urat 0.953 0.200
## 3 M8 asam_urat 0.982 0.868
## 4 M12 asam_urat 0.960 0.301
ggpubr::ggqqplot(d1, "asam_urat", facet.by = "waktu")

# (iii) Sfierisitas: Mauchly (dilaporkan otomatis oleh anova_test & afex)
aov1_rs <- anova_test(data = d1, dv = asam_urat, wid = id, within = waktu,
effect.size = "pes")
aov1_rs # berisi: ANOVA, Mauchly's Test, koreksi GG & HF
## ANOVA Table (type III tests)
##
## $ANOVA
## Effect DFn DFd F p p<.05 pes
## 1 waktu 3 87 29.791 2.41e-13 * 0.507
##
## $`Mauchly's Test for Sphericity`
## Effect W p p<.05
## 1 waktu 0.535 0.004 *
##
## $`Sphericity Corrections`
## Effect GGe DF[GG] p[GG] p[GG]<.05 HFe DF[HF] p[HF] p[HF]<.05
## 1 waktu 0.709 2.13, 61.7 4.2e-10 * 0.767 2.3, 66.73 9.52e-11 *
get_anova_table(aov1_rs, correction = "auto") # auto: GG dipakai jika Mauchly p < .05
## ANOVA Table (type III tests)
##
## Effect DFn DFd F p p<.05 pes
## 1 waktu 2.13 61.7 29.791 4.2e-10 * 0.507
## 3b. ANOVA dengan afex (sumber utama laporan) -------------------------------
aov1 <- aov_ez(id = "id", dv = "asam_urat", data = d1, within = "waktu",
anova_table = list(es = c("ges", "pes"), correction = "GG"))
aov1 # tabel ringkas (df sudah dikoreksi GG)
## Anova Table (Type 3 tests)
##
## Response: asam_urat
## Effect df MSE F ges pes p.value
## 1 waktu 2.13, 61.70 0.12 29.79 *** .137 .507 <.001
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
##
## Sphericity correction method: GG
summary(aov1) # univariat tanpa koreksi + Mauchly + epsilon GG & HF
##
## Univariate Type III Repeated-Measures ANOVA Assuming Sphericity
##
## Sum Sq num Df Error SS den Df F value Pr(>F)
## (Intercept) 7968.9 1 40.079 29 5766.138 < 2.2e-16 ***
## waktu 7.5 3 7.323 87 29.791 2.409e-13 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Mauchly Tests for Sphericity
##
## Test statistic p-value
## waktu 0.53539 0.0039558
##
##
## Greenhouse-Geisser and Huynh-Feldt Corrections
## for Departure from Sphericity
##
## GG eps Pr(>F[GG])
## waktu 0.70922 4.203e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## HF eps Pr(>F[HF])
## waktu 0.76696 9.515012e-11
# Ukuran efek tambahan
eta_squared(aov1, partial = TRUE)
## # Effect Size for ANOVA (Type III)
##
## Parameter | Eta2 (partial) | 95% CI
## -----------------------------------------
## waktu | 0.51 | [0.38, 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.13 | [0.02, 1.00]
##
## - One-sided CIs: upper bound fixed at [1.00].
## 3c. Pendekatan multivariat (tidak memerlukan sfierisitas) ------------------
aov1$Anova # Pillai, Wilks, Hotelling-Lawley, Roy
##
## Type III Repeated Measures MANOVA Tests: Pillai test statistic
## Df test stat approx F num Df den Df Pr(>F)
## (Intercept) 1 0.9950 5766.1 1 29 < 2.2e-16 ***
## waktu 1 0.6635 17.7 3 27 1.452e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 3d. Post hoc & kontras tren ------------------------------------------------
em1 <- emmeans(aov1, ~ waktu)
em1
## waktu emmean SE df lower.CL upper.CL
## M0 8.53 0.107 29 8.31 8.75
## M4 8.21 0.109 29 7.99 8.44
## M8 7.99 0.117 29 7.75 8.23
## M12 7.87 0.132 29 7.60 8.14
##
## Confidence level used: 0.95
pairs(em1, adjust = "bonferroni") # semua pasangan waktu (6 perbandingan)
## contrast estimate SE df t.ratio p.value
## M0 - M4 0.314 0.0721 29 4.350 0.0009
## M0 - M8 0.541 0.0756 29 7.154 <0.0001
## M0 - M12 0.656 0.1020 29 6.430 <0.0001
## M4 - M8 0.227 0.0502 29 4.526 0.0006
## M4 - M12 0.342 0.0761 29 4.496 0.0006
## M8 - M12 0.115 0.0636 29 1.815 0.4796
##
## P value adjustment: bonferroni method for 6 tests
contrast(em1, "trt.vs.ctrl", ref = 1, adjust = "holm") # tiap waktu vs baseline
## contrast estimate SE df t.ratio p.value
## M4 - M0 -0.314 0.0721 29 -4.350 0.0002
## M8 - M0 -0.541 0.0756 29 -7.154 <0.0001
## M12 - M0 -0.656 0.1020 29 -6.430 <0.0001
##
## P value adjustment: holm method for 3 tests
contrast(em1, "poly") # tren linear, kuadratik, kubik
## contrast estimate SE df t.ratio p.value
## linear -2.195 0.3210 29 -6.839 <0.0001
## quadratic 0.198 0.0885 29 2.242 0.0328
## cubic 0.025 0.1620 29 0.154 0.8784
## 3e. Alternatif nonparametrik ------------------------------------------------
friedman_test(d1, asam_urat ~ waktu | id)
## # A tibble: 1 × 6
## .y. n statistic df p method
## * <chr> <int> <dbl> <dbl> <dbl> <chr>
## 1 asam_urat 30 44.6 3 0.00000000112 Friedman test
friedman_effsize(d1, asam_urat ~ waktu | id) # Kendall's W
## # A tibble: 1 × 5
## .y. n effsize method magnitude
## * <chr> <int> <dbl> <chr> <ord>
## 1 asam_urat 30 0.496 Kendall W moderate
d1 |> wilcox_test(asam_urat ~ 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 asam_urat M0 M4 30 30 406. 0.000141 8.45e-4 ***
## 2 asam_urat M0 M8 30 30 445 0.000000691 4.15e-6 ****
## 3 asam_urat M0 M12 30 30 440 0.00000168 1.01e-5 ****
## 4 asam_urat M4 M8 30 30 401 0.000239 1.43e-3 **
## 5 asam_urat M4 M12 30 30 410 0.0000961 5.77e-4 ***
## 6 asam_urat M8 M12 30 30 322 0.0658 3.95e-1 ns
# (Opsional) ANOVA robust berbasis trimmed mean -- paket WRS2
if (requireNamespace("WRS2", quietly = TRUE)) {
print(WRS2::rmanova(d1$asam_urat, d1$waktu, d1$id, tr = 0.2))
}
## Call:
## WRS2::rmanova(y = d1$asam_urat, groups = d1$waktu, blocks = d1$id,
## tr = 0.2)
##
## Test statistic: F = 22.2234
## Degrees of freedom 1: 2.22
## Degrees of freedom 2: 37.74
## p-value: 0
# 4. MIXED DESIGN ANOVA (Kelompok [between] x Waktu [within])
# Pertanyaan: apakah pola perubahan asam urat berbeda antarkelompok intervensi?
## 4a. Uji asumsi -------------------------------------------------------------
# (i) Outlier per sel
dat_long |> group_by(kelompok, waktu) |> identify_outliers(asam_urat)
## # A tibble: 2 × 9
## kelompok waktu id usia jk asam_urat minggu is.outlier is.extreme
## <fct> <fct> <fct> <dbl> <chr> <dbl> <dbl> <lgl> <lgl>
## 1 Kontrol M8 P015 57 P 7.2 8 TRUE FALSE
## 2 Kontrol M12 P015 57 P 6.91 12 TRUE FALSE
# (ii) Normalitas per sel (3 x 4 = 12 sel) dan residual model
dat_long |> group_by(kelompok, waktu) |> shapiro_test(asam_urat)
## # A tibble: 12 × 5
## kelompok waktu variable statistic p
## <fct> <fct> <chr> <dbl> <dbl>
## 1 Kontrol M0 asam_urat 0.990 0.989
## 2 Kontrol M4 asam_urat 0.963 0.376
## 3 Kontrol M8 asam_urat 0.972 0.587
## 4 Kontrol M12 asam_urat 0.976 0.717
## 5 Diet_RP M0 asam_urat 0.961 0.322
## 6 Diet_RP M4 asam_urat 0.980 0.831
## 7 Diet_RP M8 asam_urat 0.968 0.486
## 8 Diet_RP M12 asam_urat 0.974 0.660
## 9 Diet_RP+Konseling M0 asam_urat 0.959 0.298
## 10 Diet_RP+Konseling M4 asam_urat 0.953 0.200
## 11 Diet_RP+Konseling M8 asam_urat 0.982 0.868
## 12 Diet_RP+Konseling M12 asam_urat 0.960 0.301
ggpubr::ggqqplot(dat_long, "asam_urat", ggtheme = theme_bw()) +
facet_grid(waktu ~ kelompok)

# (iii) Homogenitas varians antarkelompok pada TIAP waktu (Levene, median-centered)
dat_long |> group_by(waktu) |> levene_test(asam_urat ~ kelompok)
## # A tibble: 4 × 5
## waktu df1 df2 statistic p
## <fct> <int> <int> <dbl> <dbl>
## 1 M0 2 87 0.852 0.430
## 2 M4 2 87 0.682 0.508
## 3 M8 2 87 2.03 0.138
## 4 M12 2 87 4.73 0.0112
# (iv) Homogenitas matriks kovarians antarkelompok (Box's M; uji pada alpha = .001)
box_m(dat_wide[, paste0("AU_M", minggu)], dat_wide$kelompok)
## # A tibble: 1 × 4
## statistic p.value parameter method
## <dbl> <dbl> <dbl> <chr>
## 1 19.5 0.487 20 Box's M-test for Homogeneity of Covariance Matric…
# (v) Sfierisitas: Mauchly (dari summary model di bawah)
## 4b. ANOVA campuran ---------------------------------------------------------
aov2 <- aov_ez(id = "id", dv = "asam_urat", data = dat_long,
between = "kelompok", within = "waktu",
anova_table = list(es = c("ges", "pes"), correction = "GG"))
aov2
## Anova Table (Type 3 tests)
##
## Response: asam_urat
## Effect df MSE F ges pes p.value
## 1 kelompok 2, 87 1.05 1.64 .030 .036 .201
## 2 waktu 2.61, 226.78 0.08 45.61 *** .080 .344 <.001
## 3 kelompok:waktu 5.21, 226.78 0.08 6.38 *** .024 .128 <.001
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
##
## Sphericity correction method: GG
summary(aov2) # Mauchly, epsilon GG/HF, p terkoreksi
##
## Univariate Type III Repeated-Measures ANOVA Assuming Sphericity
##
## Sum Sq num Df Error SS den Df F value Pr(>F)
## (Intercept) 24661.0 1 91.325 87 23493.1790 < 2.2e-16 ***
## kelompok 3.4 2 91.325 87 1.6369 0.2005
## waktu 9.5 3 18.058 261 45.6057 < 2.2e-16 ***
## kelompok:waktu 2.6 6 18.058 261 6.3756 2.798e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Mauchly Tests for Sphericity
##
## Test statistic p-value
## waktu 0.80987 0.0028537
## kelompok:waktu 0.80987 0.0028537
##
##
## Greenhouse-Geisser and Huynh-Feldt Corrections
## for Departure from Sphericity
##
## GG eps Pr(>F[GG])
## waktu 0.86889 < 2.2e-16 ***
## kelompok:waktu 0.86889 1.052e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## HF eps Pr(>F[HF])
## waktu 0.8981199 1.435661e-21
## kelompok:waktu 0.8981199 7.826445e-06
aov2$Anova # uji multivariat untuk efek within & interaksi
##
## Type III Repeated Measures MANOVA Tests: Pillai test statistic
## Df test stat approx F num Df den Df Pr(>F)
## (Intercept) 1 0.99631 23493.2 1 87 < 2.2e-16 ***
## kelompok 2 0.03627 1.6 2 87 0.2005195
## waktu 1 0.51456 30.0 3 85 2.463e-13 ***
## kelompok:waktu 2 0.25355 4.2 6 172 0.0006228 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Versi rstatix (hasil identik; format ringkas untuk laporan)
aov2_rs <- anova_test(data = dat_long, dv = asam_urat, 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 1.637 2.01e-01 0.036
## 2 waktu 2.61 226.78 45.606 5.99e-21 * 0.344
## 3 kelompok:waktu 5.21 226.78 6.376 1.05e-05 * 0.128
# Ukuran efek
eta_squared(aov2, partial = TRUE)
## # Effect Size for ANOVA (Type III)
##
## Parameter | Eta2 (partial) | 95% CI
## ----------------------------------------------
## kelompok | 0.04 | [0.00, 1.00]
## waktu | 0.34 | [0.27, 1.00]
## kelompok:waktu | 0.13 | [0.06, 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.01 | [0.00, 1.00]
## waktu | 0.08 | [0.03, 1.00]
## kelompok:waktu | 0.02 | [0.00, 1.00]
##
## - One-sided CIs: upper bound fixed at [1.00].
# Plot interaksi dari model
afex_plot(aov2, x = "waktu", trace = "kelompok", error = "within",
mapping = c("colour", "shape", "linetype")) +
labs(y = "asam urat (mg/dL)", x = "Waktu")
## 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 (bila interaksi signifikan) -----------------------------
em2 <- emmeans(aov2, ~ waktu | kelompok)
# Efek WAKTU di dalam tiap kelompok (uji F gabungan per kelompok)
joint_tests(aov2, by = "kelompok")
## kelompok = Kontrol:
## model term df1 df2 F.ratio p.value
## waktu 3 87 1.935 0.1298
##
## kelompok = Diet_RP:
## model term df1 df2 F.ratio p.value
## waktu 3 87 13.607 <0.0001
##
## kelompok = Diet_RP+Konseling:
## model term df1 df2 F.ratio p.value
## waktu 3 87 24.986 <0.0001
# Efek KELOMPOK pada tiap waktu
joint_tests(aov2, by = "waktu")
## waktu = M0:
## model term df1 df2 F.ratio p.value
## kelompok 2 87 0.224 0.8001
##
## waktu = M4:
## model term df1 df2 F.ratio p.value
## kelompok 2 87 0.609 0.5461
##
## waktu = M8:
## model term df1 df2 F.ratio p.value
## kelompok 2 87 4.612 0.0125
##
## waktu = M12:
## model term df1 df2 F.ratio p.value
## kelompok 2 87 4.106 0.0198
# Post hoc: tiap waktu vs baseline di dalam tiap kelompok
contrast(em2, "trt.vs.ctrl", ref = 1, adjust = "holm")
## kelompok = Kontrol:
## contrast estimate SE df t.ratio p.value
## M4 - M0 -0.123 0.0646 87 -1.900 0.1461
## M8 - M0 -0.067 0.0677 87 -0.989 0.3252
## M12 - M0 -0.166 0.0830 87 -1.999 0.1461
##
## kelompok = Diet_RP:
## contrast estimate SE df t.ratio p.value
## M4 - M0 -0.224 0.0646 87 -3.475 0.0008
## M8 - M0 -0.395 0.0677 87 -5.833 <0.0001
## M12 - M0 -0.487 0.0830 87 -5.865 <0.0001
##
## kelompok = Diet_RP+Konseling:
## contrast estimate SE df t.ratio p.value
## M4 - M0 -0.314 0.0646 87 -4.858 <0.0001
## M8 - M0 -0.541 0.0677 87 -7.984 <0.0001
## M12 - M0 -0.656 0.0830 87 -7.900 <0.0001
##
## P value adjustment: holm method for 3 tests
# Post hoc: perbandingan antarkelompok pada tiap waktu
em2b <- emmeans(aov2, ~ kelompok | waktu)
pairs(em2b, adjust = "tukey") # Tukey per waktu
## waktu = M0:
## contrast estimate SE df t.ratio p.value
## Kontrol - Diet_RP -0.0957 0.143 87 -0.668 0.7825
## Kontrol - (Diet_RP+Konseling) -0.0513 0.143 87 -0.359 0.9317
## Diet_RP - (Diet_RP+Konseling) 0.0443 0.143 87 0.310 0.9486
##
## waktu = M4:
## contrast estimate SE df t.ratio p.value
## Kontrol - Diet_RP 0.0060 0.143 87 0.042 0.9990
## Kontrol - (Diet_RP+Konseling) 0.1397 0.143 87 0.976 0.5939
## Diet_RP - (Diet_RP+Konseling) 0.1337 0.143 87 0.934 0.6203
##
## waktu = M8:
## contrast estimate SE df t.ratio p.value
## Kontrol - Diet_RP 0.2323 0.139 87 1.668 0.2233
## Kontrol - (Diet_RP+Konseling) 0.4223 0.139 87 3.032 0.0089
## Diet_RP - (Diet_RP+Konseling) 0.1900 0.139 87 1.364 0.3642
##
## waktu = M12:
## contrast estimate SE df t.ratio p.value
## Kontrol - Diet_RP 0.2253 0.153 87 1.472 0.3094
## Kontrol - (Diet_RP+Konseling) 0.4387 0.153 87 2.865 0.0143
## Diet_RP - (Diet_RP+Konseling) 0.2133 0.153 87 1.393 0.3488
##
## P value adjustment: tukey method for comparing a family of 3 estimates
## 4d. Kontras interaksi -------------------------------------------------------
# Apakah penurunan (M12 - M0) berbeda antarkelompok? -- inti pertanyaan uji klinis
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_RP 0.321 0.117 87 2.734 0.0152
## M12-M0 Kontrol - (Diet_RP+Konseling) 0.490 0.117 87 4.173 0.0002
## M12-M0 Diet_RP - (Diet_RP+Konseling) 0.169 0.117 87 1.439 0.1537
##
## P value adjustment: holm method for 3 tests
# Tren linear per kelompok dan perbandingannya
contrast(em2, "poly")[c(1, 4, 7)]
## contrast kelompok estimate SE df t.ratio p.value
## linear Kontrol -0.442 0.266 87 -1.662 0.1001
## linear Diet_RP -1.632 0.266 87 -6.131 <0.0001
## linear Diet_RP+Konseling -2.195 0.266 87 -8.248 <0.0001
tren_int <- summary(contrast(em_full, interaction = c(waktu = "poly", kelompok = "pairwise"),
adjust = "none"))
tren_lin <- subset(tren_int, waktu_poly == "linear") # apakah laju penurunan linear berbeda?
tren_lin$p.holm <- p.adjust(tren_lin$p.value, "holm") # koreksi Holm untuk 3 perbandingan
tren_lin
## waktu_poly kelompok_pairwise estimate SE df t.ratio
## 1 linear Kontrol - Diet_RP 1.1893333 0.3763644 87 3.160058
## 4 linear Kontrol - (Diet_RP+Konseling) 1.7526667 0.3763644 87 4.656834
## 7 linear Diet_RP - (Diet_RP+Konseling) 0.5633333 0.3763644 87 1.496776
## p.value p.holm
## 1 2.170353e-03 0.0043407057
## 4 1.144627e-05 0.0000343388
## 7 1.380707e-01 0.1380707024
# 5. PEMBANDING: LINEAR MIXED MODEL (LMM)
# Tidak mensyaratkan sfierisitas, menampung data hilang (MAR) dan waktu
# pengukuran yang tidak seragam.
lmm1 <- lmer(asam_urat ~ kelompok * waktu + (1 | id), data = dat_long, REML = TRUE)
lmm2 <- lmer(asam_urat ~ kelompok * waktu + (1 + minggu | id), data = dat_long, REML = TRUE)
anova(lmm1, lmm2, refit = FALSE) # uji rasio kemungkinan struktur acak
## Data: dat_long
## Models:
## lmm1: asam_urat ~ kelompok * waktu + (1 | id)
## lmm2: asam_urat ~ kelompok * waktu + (1 + minggu | id)
## npar AIC BIC logLik -2*log(L) Chisq Df Pr(>Chisq)
## lmm1 14 380.61 435.02 -176.31 352.61
## lmm2 16 367.07 429.24 -167.53 335.07 17.544 2 0.000155 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(lmm2, ddf = "Kenward-Roger") # uji F tipe III efek tetap
## Type III Analysis of Variance Table with Kenward-Roger's method
## Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
## kelompok 0.1659 0.08293 2 87.00 1.6369 0.2005208
## waktu 4.6994 1.56648 3 185.37 30.7776 3.413e-16 ***
## kelompok:waktu 1.3903 0.23171 6 206.40 4.5475 0.0002353 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
performance::icc(lmm1) # korelasi intrakelas
## # Intraclass Correlation Coefficient
##
## Adjusted ICC: 0.780
## Unadjusted ICC: 0.685
# Diagnostik residual LMM (normalitas & homogenitas)
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))
# (paket 'see' + performance::check_model(lmm2) memberi panel diagnostik lengkap)
# Simulasi 30 nilai hilang (MCAR, +/- 11% pengukuran pasca-baseline)
# untuk menunjukkan keunggulan LMM