# =============================================================================
# Nama : Rofiani Vitrilia Rachman
# NIM : 2611018018
# =============================================================================

#  REPEATED MEASURE ANALYSIS DENGAN R
#  Contoh terapan: Program intervensi gaya hidup dan kadar glukosa darah puasa
#  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 & 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: uji klinis berkelompok di puskesmas. 90 pasien diabetes melitus tipe 2
# diacak ke tiga kelompok (n = 30 per kelompok):
#   - Kontrol  : edukasi standar puskesmas
#   - DM       : edukasi diet diabetes dan pengaturan pola makan
#   - DM+AF    : edukasi diet diabetes + aktivitas fisik terstruktur (senam 3x/minggu)
# Glukosa darah puasa (GDP, 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", "DM", "DM+AF")
minggu  <- c(0, 4, 8, 12)

# Rerata populasi (mmHg) per kelompok x waktu
mu <- rbind(
  "Kontrol" = c(185, 183, 181, 180),
  "DM"      = c(185, 174, 166, 158),
  "DM+AF"   = c(185, 170, 157, 146)
)

# SD intersep acak (perbedaan GDP dasar antar pasien)
sd_int   <- 18
sd_slope <- 0.55  # SD kemiringan acak per minggu (perbedaan respons antarpasien)
sd_eps   <- 8     # 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("GDP_M", minggu)
  data.frame(id       = sprintf("P%03d", id),
             kelompok = kel_lab[g],
             usia     = round(runif(n_per, 35, 70)),
             jk       = sample(c("L", "P"), n_per, replace = TRUE, prob = c(.45, .55)),
             round(y, 1))
}) |> 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("GDP_M"), names_to = "waktu", values_to = "gdp") |>
  mutate(waktu  = factor(waktu, levels = paste0("GDP_M", minggu),
                         labels = paste0("M", minggu)),
         minggu = as.numeric(sub("M", "", waktu)))

head(dat_wide)
##     id kelompok usia jk GDP_M0 GDP_M4 GDP_M8 GDP_M12
## 1 P001  Kontrol   43  L  193.2  175.5  203.3   186.1
## 2 P002  Kontrol   44  L  166.9  175.7  179.4   164.3
## 3 P003  Kontrol   67  P  185.9  181.1  184.2   188.7
## 4 P004  Kontrol   41  P  181.1  190.1  188.1   184.7
## 5 P005  Kontrol   56  P  159.1  156.8  155.9   173.8
## 6 P006  Kontrol   43  L  137.5  138.2  144.6   151.5
head(dat_long)
## # A tibble: 6 × 7
##   id    kelompok  usia jk    waktu   gdp minggu
##   <fct> <fct>    <dbl> <chr> <fct> <dbl>  <dbl>
## 1 P001  Kontrol     43 L     M0     193.      0
## 2 P001  Kontrol     43 L     M4     176.      4
## 3 P001  Kontrol     43 L     M8     203.      8
## 4 P001  Kontrol     43 L     M12    186.     12
## 5 P002  Kontrol     44 L     M0     167.      0
## 6 P002  Kontrol     44 L     M4     176.      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","DM",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ usia    : num [1:360] 43 43 43 43 44 44 44 44 67 67 ...
##  $ 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 ...
##  $ gdp     : num [1:360] 193 176 203 186 167 ...
##  $ 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(gdp, type = "mean_sd")
desk
## # A tibble: 12 × 6
##    kelompok waktu variable     n  mean    sd
##    <fct>    <fct> <fct>    <dbl> <dbl> <dbl>
##  1 Kontrol  M0    gdp         30  184.  23.1
##  2 Kontrol  M4    gdp         30  180.  21.1
##  3 Kontrol  M8    gdp         30  181.  18.5
##  4 Kontrol  M12   gdp         30  178.  20.2
##  5 DM       M0    gdp         30  188.  18.8
##  6 DM       M4    gdp         30  177.  20.8
##  7 DM       M8    gdp         30  168.  19.4
##  8 DM       M12   gdp         30  160.  19.3
##  9 DM+AF    M0    gdp         30  186.  23.1
## 10 DM+AF    M4    gdp         30  171.  22.9
## 11 DM+AF    M8    gdp         30  159.  24.2
## 12 DM+AF    M12   gdp         30  149.  25.6
# Matriks kovarians & korelasi antarwaktu (seluruh subjek, dalam kelompok)
# -> memberi gambaran awal apakah sfierisitas masuk akal
S  <- cov(dat_wide[, paste0("GDP_M", minggu)])
R  <- cor(dat_wide[, paste0("GDP_M", minggu)])
round(S, 1); round(R, 2)
##         GDP_M0 GDP_M4 GDP_M8 GDP_M12
## GDP_M0   465.8  385.3  362.0   356.7
## GDP_M4   385.3  470.5  411.7   423.3
## GDP_M8   362.0  411.7  513.9   496.1
## GDP_M12  356.7  423.3  496.1   610.5
##         GDP_M0 GDP_M4 GDP_M8 GDP_M12
## GDP_M0    1.00   0.82   0.74    0.67
## GDP_M4    0.82   1.00   0.84    0.79
## GDP_M8    0.74   0.84   1.00    0.89
## GDP_M12   0.67   0.79   0.89    1.00
# Varians selisih antarpasangan waktu (inti asumsi sfierisitas)
pasangan <- combn(paste0("GDP_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)
##  GDP_M0 - GDP_M4  GDP_M0 - GDP_M8 GDP_M0 - GDP_M12  GDP_M4 - GDP_M8 
##            165.7            255.6            363.1            161.0 
## GDP_M4 - GDP_M12 GDP_M8 - GDP_M12 
##            234.5            132.2
# Profile plot: rerata +/- 95% CI per kelompok
p_profil <- ggplot(dat_long, aes(minggu, gdp, 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 = "Glukosa darah puasa (mg/dL)", colour = "Kelompok",
       title = "Profil rerata GDP (± 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, gdp, 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 = "GDP (mg/dL)", title = "Lintasan individu dan rerata kelompok")
p_spag

# 3. REPEATED MEASURE ANOVA SATU ARAH
#    Pertanyaan: apakah GDP berubah selama 12 minggu pada kelompok DM+AF?
d1  <- droplevels(filter(dat_long, kelompok == "DM+AF"))
d1w <- filter(dat_wide, kelompok == "DM+AF")

## 3a. Uji asumsi -------------------------------------------------------------
# (i) Outlier per waktu (ekstrem = di luar Q1-3IQR / Q3+3IQR)
d1 |> group_by(waktu) |> identify_outliers(gdp)
## [1] waktu      id         kelompok   usia       jk         gdp        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(gdp)
## # A tibble: 4 × 4
##   waktu variable statistic     p
##   <fct> <chr>        <dbl> <dbl>
## 1 M0    gdp          0.960 0.303
## 2 M4    gdp          0.957 0.262
## 3 M8    gdp          0.974 0.640
## 4 M12   gdp          0.969 0.505
ggpubr::ggqqplot(d1, "gdp", facet.by = "waktu")

# (iii) Sfierisitas: Mauchly (dilaporkan otomatis oleh anova_test & afex)
aov1_rs <- anova_test(data = d1, dv = gdp, 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 86.185 5.72e-26     * 0.748
## 
## $`Mauchly's Test for Sphericity`
##   Effect     W     p p<.05
## 1  waktu 0.695 0.073      
## 
## $`Sphericity Corrections`
##   Effect   GGe      DF[GG]    p[GG] p[GG]<.05   HFe      DF[HF]    p[HF]
## 1  waktu 0.812 2.44, 70.68 1.53e-21         * 0.892 2.68, 77.64 1.97e-23
##   p[HF]<.05
## 1         *
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   3  87 86.185 5.72e-26     * 0.748
## 3b. ANOVA dengan afex (sumber utama laporan) -------------------------------
aov1 <- aov_ez(id = "id", dv = "gdp", 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: gdp
##   Effect          df    MSE         F  ges  pes p.value
## 1  waktu 2.44, 70.68 107.73 86.18 *** .253 .748   <.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) 3313729      1    59141     29 1624.893 < 2.2e-16 ***
## waktu         22629      3     7614     87   86.185 < 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.69491 0.072886
## 
## 
## Greenhouse-Geisser and Huynh-Feldt Corrections
##  for Departure from Sphericity
## 
##       GG eps Pr(>F[GG])    
## waktu 0.8124  < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##          HF eps   Pr(>F[HF])
## waktu 0.8924254 1.973737e-23
# Ukuran efek tambahan
eta_squared(aov1, partial = TRUE)
## # Effect Size for ANOVA (Type III)
## 
## Parameter | Eta2 (partial) |       95% CI
## -----------------------------------------
## waktu     |           0.75 | [0.67, 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.24 | [0.11, 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.98247  1624.89      1     29 < 2.2e-16 ***
## waktu        1   0.86074    55.63      3     27 1.099e-11 ***
## ---
## 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       186 4.22 29      177      195
##  M4       171 4.19 29      162      180
##  M8       159 4.41 29      150      168
##  M12      149 4.68 29      140      159
## 
## Confidence level used: 0.95
pairs(em1, adjust = "bonferroni")          # semua pasangan waktu (6 perbandingan)
##  contrast estimate   SE df t.ratio p.value
##  M0 - M4     14.89 2.44 29   6.109 <0.0001
##  M0 - M8     27.36 2.39 29  11.423 <0.0001
##  M0 - M12    36.57 3.08 29  11.863 <0.0001
##  M4 - M8     12.46 1.75 29   7.126 <0.0001
##  M4 - M12    21.67 2.47 29   8.771 <0.0001
##  M8 - M12     9.21 2.16 29   4.265  0.0012
## 
## 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     -14.9 2.44 29  -6.109 <0.0001
##  M8 - M0     -27.4 2.39 29 -11.423 <0.0001
##  M12 - M0    -36.6 3.08 29 -11.863 <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    -122.163 9.61 29 -12.718 <0.0001
##  quadratic    5.683 3.14 29   1.807  0.0811
##  cubic        0.823 5.77 29   0.143  0.8876
## 3e. Alternatif nonparametrik ------------------------------------------------
friedman_test(d1, gdp ~ waktu | id)
## # A tibble: 1 × 6
##   .y.       n statistic    df        p method       
## * <chr> <int>     <dbl> <dbl>    <dbl> <chr>        
## 1 gdp      30      67.9     3 1.21e-14 Friedman test
friedman_effsize(d1, gdp ~ waktu | id)     # Kendall's W
## # A tibble: 1 × 5
##   .y.       n effsize method    magnitude
## * <chr> <int>   <dbl> <chr>     <ord>    
## 1 gdp      30   0.754 Kendall W large
d1 |> wilcox_test(gdp ~ 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 gdp   M0     M4        30    30      436. 0.00000281      1.69e-5 ****        
## 2 gdp   M0     M8        30    30      464  0.00000000373   2.24e-8 ****        
## 3 gdp   M0     M12       30    30      464  0.00000000373   2.24e-8 ****        
## 4 gdp   M4     M8        30    30      452  0.000000164     9.83e-7 ****        
## 5 gdp   M4     M12       30    30      459  0.0000000261    1.56e-7 ****        
## 6 gdp   M8     M12       30    30      402  0.000226        1.36e-3 **
# (Opsional) ANOVA robust berbasis trimmed mean -- paket WRS2
if (requireNamespace("WRS2", quietly = TRUE)) {
  print(WRS2::rmanova(d1$gdp, d1$waktu, d1$id, tr = 0.2))
}
## Call:
## WRS2::rmanova(y = d1$gdp, groups = d1$waktu, blocks = d1$id, 
##     tr = 0.2)
## 
## Test statistic: F = 60.0673 
## Degrees of freedom 1: 2.63 
## Degrees of freedom 2: 44.77 
## p-value: 0
# 4. MIXED DESIGN ANOVA  (Kelompok [between] x Waktu [within])
#    Pertanyaan: apakah pola perubahan GDP berbeda antarkelompok intervensi?
## 4a. Uji asumsi -------------------------------------------------------------
# (i) Outlier per sel
dat_long |> group_by(kelompok, waktu) |> identify_outliers(gdp)
## # A tibble: 2 × 9
##   kelompok waktu id     usia jk      gdp minggu is.outlier is.extreme
##   <fct>    <fct> <fct> <dbl> <chr> <dbl>  <dbl> <lgl>      <lgl>     
## 1 Kontrol  M8    P015     61 P      135       8 TRUE       FALSE     
## 2 Kontrol  M12   P015     61 P      125.     12 TRUE       FALSE
# (ii) Normalitas per sel (3 x 4 = 12 sel) dan residual model
dat_long |> group_by(kelompok, waktu) |> shapiro_test(gdp)
## # A tibble: 12 × 5
##    kelompok waktu variable statistic     p
##    <fct>    <fct> <chr>        <dbl> <dbl>
##  1 Kontrol  M0    gdp          0.991 0.996
##  2 Kontrol  M4    gdp          0.961 0.334
##  3 Kontrol  M8    gdp          0.965 0.411
##  4 Kontrol  M12   gdp          0.973 0.636
##  5 DM       M0    gdp          0.962 0.348
##  6 DM       M4    gdp          0.980 0.830
##  7 DM       M8    gdp          0.974 0.643
##  8 DM       M12   gdp          0.987 0.970
##  9 DM+AF    M0    gdp          0.960 0.303
## 10 DM+AF    M4    gdp          0.957 0.262
## 11 DM+AF    M8    gdp          0.974 0.640
## 12 DM+AF    M12   gdp          0.969 0.505
ggpubr::ggqqplot(dat_long, "gdp", ggtheme = theme_bw()) +
  facet_grid(waktu ~ kelompok)

# (iii) Homogenitas varians antarkelompok pada TIAP waktu (Levene, median-centered)
dat_long |> group_by(waktu) |> levene_test(gdp ~ kelompok)
## # A tibble: 4 × 5
##   waktu   df1   df2 statistic      p
##   <fct> <int> <int>     <dbl>  <dbl>
## 1 M0        2    87     0.709 0.495 
## 2 M4        2    87     0.551 0.578 
## 3 M8        2    87     1.56  0.216 
## 4 M12       2    87     2.68  0.0746
# (iv) Homogenitas matriks kovarians antarkelompok (Box's M; uji pada alpha = .001)
box_m(dat_wide[, paste0("GDP_M", minggu)], dat_wide$kelompok)
## # A tibble: 1 × 4
##   statistic p.value parameter method                                            
##       <dbl>   <dbl>     <dbl> <chr>                                             
## 1      17.4   0.626        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 = "gdp", data = dat_long,
               between = "kelompok", within = "waktu",
               anova_table = list(es = c("ges", "pes"), correction = "GG"))
aov2
## Anova Table (Type 3 tests)
## 
## Response: gdp
##           Effect           df     MSE          F  ges  pes p.value
## 1       kelompok        2, 87 1626.36     3.91 * .073 .082    .024
## 2          waktu 2.83, 245.97   81.33 116.96 *** .143 .573   <.001
## 3 kelompok:waktu 5.65, 245.97   81.33  19.98 *** .054 .315   <.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)    10811494      1   141494     87 6647.6487 < 2e-16 ***
## kelompok          12716      2   141494     87    3.9093 0.02367 *  
## waktu             26894      3    20005    261  116.9599 < 2e-16 ***
## kelompok:waktu     9190      6    20005    261   19.9833 < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Mauchly Tests for Sphericity
## 
##                Test statistic p-value
## waktu                 0.91481 0.17768
## kelompok:waktu        0.91481 0.17768
## 
## 
## Greenhouse-Geisser and Huynh-Feldt Corrections
##  for Departure from Sphericity
## 
##                GG eps Pr(>F[GG])    
## waktu          0.9424  < 2.2e-16 ***
## kelompok:waktu 0.9424  < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##                   HF eps   Pr(>F[HF])
## waktu          0.9773238 5.375425e-47
## kelompok:waktu 0.9773238 8.117757e-19
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.98708   6647.6      1     87 < 2.2e-16 ***
## kelompok        2   0.08246      3.9      2     87   0.02367 *  
## waktu           1   0.75327     86.5      3     85 < 2.2e-16 ***
## kelompok:waktu  2   0.52070     10.1      6    172 1.509e-09 ***
## ---
## 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 = gdp, 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.909 2.40e-02     * 0.082
## 2          waktu 2.83 245.97 116.960 2.05e-45     * 0.573
## 3 kelompok:waktu 5.65 245.97  19.983 3.16e-18     * 0.315
# Ukuran efek
eta_squared(aov2, partial = TRUE)
## # Effect Size for ANOVA (Type III)
## 
## Parameter      | Eta2 (partial) |       95% CI
## ----------------------------------------------
## kelompok       |           0.08 | [0.01, 1.00]
## waktu          |           0.57 | [0.51, 1.00]
## kelompok:waktu |           0.31 | [0.23, 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.06 | [0.00, 1.00]
## waktu          |             0.14 | [0.08, 1.00]
## kelompok:waktu |             0.05 | [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 = "GDP (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 (karena 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   2.280  0.0849
## 
## kelompok = DM:
##  model term df1 df2 F.ratio p.value
##  waktu        3  87  42.425 <0.0001
## 
## kelompok = DM+AF:
##  model term df1 df2 F.ratio p.value
##  waktu        3  87  75.216 <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.222  0.8016
## 
## waktu = M4:
##  model term df1 df2 F.ratio p.value
##  kelompok     2  87   1.207  0.3040
## 
## waktu = M8:
##  model term df1 df2 F.ratio p.value
##  kelompok     2  87   9.189  0.0002
## 
## waktu = M12:
##  model term df1 df2 F.ratio p.value
##  kelompok     2  87  13.117 <0.0001
# 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     -4.44 2.24 87  -1.983  0.1011
##  M8 - M0     -2.53 2.23 87  -1.132  0.2607
##  M12 - M0    -6.01 2.59 87  -2.323  0.0676
## 
## kelompok = DM:
##  contrast estimate   SE df t.ratio p.value
##  M4 - M0    -11.11 2.24 87  -4.967 <0.0001
##  M8 - M0    -19.95 2.23 87  -8.938 <0.0001
##  M12 - M0   -27.81 2.59 87 -10.755 <0.0001
## 
## kelompok = DM+AF:
##  contrast estimate   SE df t.ratio p.value
##  M4 - M0    -14.89 2.24 87  -6.656 <0.0001
##  M8 - M0    -27.36 2.23 87 -12.257 <0.0001
##  M12 - M0   -36.57 2.59 87 -14.141 <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 - DM         -3.74 5.62 87  -0.666  0.7839
##  Kontrol - (DM+AF)    -1.91 5.62 87  -0.340  0.9382
##  DM - (DM+AF)          1.83 5.62 87   0.325  0.9433
## 
## waktu = M4:
##  contrast          estimate   SE df t.ratio p.value
##  Kontrol - DM          2.93 5.59 87   0.525  0.8593
##  Kontrol - (DM+AF)     8.54 5.59 87   1.529  0.2824
##  DM - (DM+AF)          5.61 5.59 87   1.004  0.5763
## 
## waktu = M8:
##  contrast          estimate   SE df t.ratio p.value
##  Kontrol - DM         13.68 5.38 87   2.543  0.0338
##  Kontrol - (DM+AF)    22.92 5.38 87   4.260  0.0002
##  DM - (DM+AF)          9.24 5.38 87   1.717  0.2046
## 
## waktu = M12:
##  contrast          estimate   SE df t.ratio p.value
##  Kontrol - DM         18.06 5.66 87   3.193  0.0055
##  Kontrol - (DM+AF)    28.65 5.66 87   5.065 <0.0001
##  DM - (DM+AF)         10.59 5.66 87   1.872  0.1530
## 
## P value adjustment: tukey method for comparing a family of 3 estimates
## 4d. Kontras interaksi -------------------------------------------------------
# Apakah perubahan (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 - DM         21.80 3.66 87   5.962 <0.0001
##  M12-M0       Kontrol - (DM+AF)    30.56 3.66 87   8.357 <0.0001
##  M12-M0       DM - (DM+AF)          8.76 3.66 87   2.394  0.0188
## 
## 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     -16.1 8.24 87  -1.956  0.0537
##  linear   DM          -92.3 8.24 87 -11.203 <0.0001
##  linear   DM+AF      -122.2 8.24 87 -14.833 <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      p.value
## 1     linear      Kontrol - DM  76.15667 11.64755 87 6.538426 4.086522e-09
## 4     linear Kontrol - (DM+AF) 106.05333 11.64755 87 9.105203 2.734732e-14
## 7     linear      DM - (DM+AF)  29.89667 11.64755 87 2.566777 1.197458e-02
##         p.holm
## 1 8.173045e-09
## 4 8.204197e-14
## 7 1.197458e-02
# 5. PEMBANDING: LINEAR MIXED MODEL (LMM)
#    Tidak mensyaratkan sfierisitas, menampung data hilang (MAR) dan waktu
#    pengukuran yang tidak seragam.
lmm1 <- lmer(gdp ~ kelompok * waktu + (1 | id), data = dat_long, REML = TRUE)
lmm2 <- lmer(gdp ~ 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: gdp ~ kelompok * waktu + (1 | id)
## lmm2: gdp ~ kelompok * waktu + (1 + minggu | id)
##      npar    AIC    BIC  logLik -2*log(L) Chisq Df Pr(>Chisq)  
## lmm1   14 2849.3 2903.7 -1410.7    2821.3                      
## lmm2   16 2846.8 2909.0 -1407.4    2814.8  6.47  2    0.03936 *
## ---
## 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         501.1   250.6     2  87.00  3.9093   0.02367 *  
## waktu          17060.6  5686.9     3 185.37 88.3172 < 2.2e-16 ***
## kelompok:waktu  5870.6   978.4     6 206.40 15.1783  2.23e-14 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
performance::icc(lmm1)                    # korelasi intrakelas
## # Intraclass Correlation Coefficient
## 
##     Adjusted ICC: 0.835
##   Unadjusted ICC: 0.646
# 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
set.seed(1)
dat_miss <- dat_long
dat_miss$gdp[sample(which(dat_miss$waktu != "M0"), 30)] <- NA
lmm_miss <- lmer(gdp ~ kelompok * waktu + (1 + minggu | id), data = dat_miss,
                 control = lmerControl(optimizer = "bobyqa"))
anova(lmm_miss, ddf = "Kenward-Roger")    # semua pasien tetap dianalisis
## Type III Analysis of Variance Table with Kenward-Roger's method
##                 Sum Sq Mean Sq NumDF   DenDF F value    Pr(>F)    
## kelompok         491.0   245.5     2  86.994  3.7196   0.02818 *  
## waktu          16814.3  5604.8     3 168.197 84.5202 < 2.2e-16 ***
## kelompok:waktu  6084.4  1014.1     6 185.876 15.2744 3.369e-14 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# RM ANOVA akan membuang seluruh pasien yang punya >= 1 nilai hilang:
n_distinct(dat_miss$id[is.na(dat_miss$gdp)])
## [1] 25
# 6. SIMPAN DATA & SESSION INFO
write.csv(dat_wide, "data_gdp_dm_wide.csv", row.names = FALSE)
write.csv(dat_long, "data_gdp_dm_long.csv", row.names = FALSE)
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_United States.utf8 
## [2] LC_CTYPE=English_United States.utf8   
## [3] LC_MONETARY=English_United States.utf8
## [4] LC_NUMERIC=C                          
## [5] LC_TIME=English_United States.utf8    
## 
## time zone: Asia/Singapore
## 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.60           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] datawizard_1.4.0    parallel_4.6.1      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.31     
## [58] otel_0.2.0          ggsignif_0.6.4      evaluate_1.0.5     
## [61] knitr_1.51          parameters_0.29.3   rbibutils_2.4.1    
## [64] rlang_1.3.0         Rcpp_1.1.2          glue_1.8.1         
## [67] reshape_0.8.10      rstudioapi_0.19.0   minqa_1.2.8        
## [70] jsonlite_2.0.0      R6_2.6.1            plyr_1.8.9