# =============================================================================
# REPEATED MEASURE ANALYSIS DENGAN R
#TUGAS BIOSTATISTIKA INTERMEDIATE
#NAMA :AHMAD FADHLIL AZHIM
#NIM  :2611018029
# Data simulasi/modifikasi berdasarkan Ory et al. (2025), Frontiers in Public Health
# Desain: 3 treatment x 3 waktu pengukuran HbA1c
# =============================================================================

# 0. PAKET & PENGATURAN
# install.packages(c("tidyverse", "afex", "emmeans", "rstatix", "car",
#                    "effectsize", "lme4", "lmerTest", "performance", "ggpubr"))
suppressPackageStartupMessages({
  library(dplyr)
  library(tidyr)
  library(ggplot2)
  library(afex)
  library(emmeans)
  library(rstatix)
  library(car)
  library(effectsize)
  library(lme4)
  library(lmerTest)
  library(performance)
  library(ggpubr)
})
options(contrasts = c("contr.sum", "contr.poly"))
afex_options(emmeans_model = "multivariate")
theme_set(theme_bw(base_size = 12))

# 1. IMPORT DATA SIMULASI
# Data individual pada file ini BUKAN data mentah artikel.
# Data disimulasikan untuk latihan dengan acuan desain, titik waktu, dan pola A1c
# yang dilaporkan Ory et al. (2025).
dat_wide <- read.csv("Data_Simulasi_A1c_DSMES_3x3.csv")
dat_wide$Treatment <- factor(dat_wide$Treatment,
                             levels = c("TBES", "vMMWD", "Combined"))
dat_wide$ID <- factor(dat_wide$ID)

dat_long <- dat_wide |>
  pivot_longer(cols = starts_with("A1c_"), names_to = "waktu", values_to = "A1c") |>
  mutate(waktu = factor(waktu,
                        levels = c("A1c_Baseline", "A1c_M3", "A1c_M6"),
                        labels = c("Baseline", "M3", "M6")),
         bulan = case_when(waktu == "Baseline" ~ 0,
                           waktu == "M3" ~ 3,
                           waktu == "M6" ~ 6))

head(dat_wide)
##     ID Treatment A1c_Baseline A1c_M3 A1c_M6
## 1 TB01      TBES        10.44   8.61   8.36
## 2 TB02      TBES        11.16   9.70   9.46
## 3 TB03      TBES         6.92   5.50   6.32
## 4 TB04      TBES         8.99   6.94   6.61
## 5 TB05      TBES        10.75  10.56  10.32
## 6 TB06      TBES        10.38   8.19   9.38
head(dat_long)
## # A tibble: 6 × 5
##   ID    Treatment waktu      A1c bulan
##   <fct> <fct>     <fct>    <dbl> <dbl>
## 1 TB01  TBES      Baseline 10.4      0
## 2 TB01  TBES      M3        8.61     3
## 3 TB01  TBES      M6        8.36     6
## 4 TB02  TBES      Baseline 11.2      0
## 5 TB02  TBES      M3        9.7      3
## 6 TB02  TBES      M6        9.46     6
str(dat_long)
## tibble [270 × 5] (S3: tbl_df/tbl/data.frame)
##  $ ID       : Factor w/ 90 levels "CO01","CO02",..: 31 31 31 32 32 32 33 33 33 34 ...
##  $ Treatment: Factor w/ 3 levels "TBES","vMMWD",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ waktu    : Factor w/ 3 levels "Baseline","M3",..: 1 2 3 1 2 3 1 2 3 1 ...
##  $ A1c      : num [1:270] 10.44 8.61 8.36 11.16 9.7 ...
##  $ bulan    : num [1:270] 0 3 6 0 3 6 0 3 6 0 ...
# 2. EKSPLORASI DATA
(dat_long |>
   group_by(Treatment, waktu) |>
   get_summary_stats(A1c, type = "mean_sd"))
## # A tibble: 9 × 6
##   Treatment waktu    variable     n  mean    sd
##   <fct>     <fct>    <fct>    <dbl> <dbl> <dbl>
## 1 TBES      Baseline A1c         30  9.43  1.22
## 2 TBES      M3       A1c         30  8.26  1.32
## 3 TBES      M6       A1c         30  8.28  1.26
## 4 vMMWD     Baseline A1c         30  9.14  1.24
## 5 vMMWD     M3       A1c         30  8.06  1.35
## 6 vMMWD     M6       A1c         30  8.08  1.32
## 7 Combined  Baseline A1c         30  8.86  1.26
## 8 Combined  M3       A1c         30  7.99  1.47
## 9 Combined  M6       A1c         30  7.69  1.22
# Profile plot
p_profil <- ggplot(dat_long,
                   aes(x = bulan, y = A1c, colour = Treatment, group = Treatment)) +
  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 = .35) +
  scale_x_continuous(breaks = c(0, 3, 6)) +
  labs(x = "Bulan", y = "HbA1c (%)", title = "Profil rerata HbA1c (±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
p_spag <- ggplot(dat_long, aes(x = bulan, y = A1c, group = ID)) +
  geom_line(alpha = .25) +
  stat_summary(aes(group = Treatment), fun = mean, geom = "line",
               linewidth = 1.2) +
  facet_wrap(~ Treatment) +
  scale_x_continuous(breaks = c(0, 3, 6)) +
  labs(x = "Bulan", y = "HbA1c (%)", title = "Lintasan individual dan rerata")
p_spag

# 3. REPEATED MEASURE ANOVA SATU ARAH
# Contoh fokus: kelompok TBES
d1 <- droplevels(filter(dat_long, Treatment == "TBES"))

# 3a. Uji asumsi
d1 |> group_by(waktu) |> identify_outliers(A1c)
## # A tibble: 2 × 7
##   waktu ID    Treatment   A1c bulan is.outlier is.extreme
##   <fct> <fct> <fct>     <dbl> <dbl> <lgl>      <lgl>     
## 1 M3    TB03  TBES       5.5      3 TRUE       FALSE     
## 2 M3    TB26  TBES       5.06     3 TRUE       FALSE
d1 |> group_by(waktu) |> shapiro_test(A1c)
## # A tibble: 3 × 4
##   waktu    variable statistic     p
##   <fct>    <chr>        <dbl> <dbl>
## 1 Baseline A1c          0.947 0.142
## 2 M3       A1c          0.964 0.393
## 3 M6       A1c          0.962 0.356
ggpubr::ggqqplot(d1, "A1c", facet.by = "waktu")

aov1_rs <- anova_test(data = d1, dv = A1c, 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   2  58 80.194 2e-17     * 0.734
## 
## $`Mauchly's Test for Sphericity`
##   Effect     W     p p<.05
## 1  waktu 0.967 0.628      
## 
## $`Sphericity Corrections`
##   Effect   GGe      DF[GG]    p[GG] p[GG]<.05   HFe      DF[HF] p[HF] p[HF]<.05
## 1  waktu 0.968 1.94, 56.17 6.02e-17         * 1.037 2.07, 60.12 2e-17         *
get_anova_table(aov1_rs, correction = "auto")
## ANOVA Table (type III tests)
## 
##   Effect DFn DFd      F     p p<.05   pes
## 1  waktu   2  58 80.194 2e-17     * 0.734
# 3b. ANOVA dengan afex
aov1 <- aov_ez(id = "ID", dv = "A1c", data = d1, within = "waktu",
               anova_table = list(es = c("ges", "pes"), correction = "GG"))
aov1
## Anova Table (Type 3 tests)
## 
## Response: A1c
##   Effect          df  MSE         F  ges  pes p.value
## 1  waktu 1.94, 56.17 0.17 80.19 *** .163 .734   <.001
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
## 
## Sphericity correction method: GG
summary(aov1)
## Warning in summary.Anova.mlm(object$Anova, multivariate = FALSE): HF eps > 1
## treated as 1
## 
## Univariate Type III Repeated-Measures ANOVA Assuming Sphericity
## 
##             Sum Sq num Df Error SS den Df  F value    Pr(>F)    
## (Intercept) 6744.4      1  129.384     29 1511.682 < 2.2e-16 ***
## waktu         27.0      2    9.776     58   80.194 < 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.96736 0.62839
## 
## 
## Greenhouse-Geisser and Huynh-Feldt Corrections
##  for Departure from Sphericity
## 
##        GG eps Pr(>F[GG])    
## waktu 0.96839  < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##         HF eps   Pr(>F[HF])
## waktu 1.036527 2.003621e-17
eta_squared(aov1, partial = TRUE)
## # Effect Size for ANOVA (Type III)
## 
## Parameter | Eta2 (partial) |       95% CI
## -----------------------------------------
## waktu     |           0.73 | [0.63, 1.00]
## 
## - One-sided CIs: upper bound fixed at [1.00].
# 3c. Pendekatan multivariat
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.98118  1511.68      1     29 < 2.2e-16 ***
## waktu        1   0.82410    65.59      2     28 2.714e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# 3d. Post hoc berpasangan
em1 <- emmeans(aov1, ~ waktu)
pairs(em1, adjust = "bonferroni")
##  contrast      estimate    SE df t.ratio p.value
##  Baseline - M3   1.1713 0.111 29  10.593 <0.0001
##  Baseline - M6   1.1537 0.111 29  10.413 <0.0001
##  M3 - M6        -0.0177 0.096 29  -0.184  1.0000
## 
## P value adjustment: bonferroni method for 3 tests
contrast(em1, "trt.vs.ctrl", ref = 1, adjust = "holm")
##  contrast      estimate    SE df t.ratio p.value
##  M3 - Baseline    -1.17 0.111 29 -10.593 <0.0001
##  M6 - Baseline    -1.15 0.111 29 -10.413 <0.0001
## 
## P value adjustment: holm method for 2 tests
# 3e. Alternatif nonparametrik
friedman_test(d1, A1c ~ waktu | ID)
## # A tibble: 1 × 6
##   .y.       n statistic    df        p method       
## * <chr> <int>     <dbl> <dbl>    <dbl> <chr>        
## 1 A1c      30      42.1     2 7.33e-10 Friedman test
friedman_effsize(d1, A1c ~ waktu | ID)
## # A tibble: 1 × 5
##   .y.       n effsize method    magnitude
## * <chr> <int>   <dbl> <chr>     <ord>    
## 1 A1c      30   0.701 Kendall W large
d1 |> wilcox_test(A1c ~ waktu, paired = TRUE,
                  p.adjust.method = "bonferroni")
## # A tibble: 3 × 9
##   .y.   group1   group2    n1    n2 statistic             p   p.adj p.adj.signif
## * <chr> <chr>    <chr>  <int> <int>     <dbl>         <dbl>   <dbl> <chr>       
## 1 A1c   Baseline M3        30    30       465 0.00000000186 5.59e-9 ****        
## 2 A1c   Baseline M6        30    30       463 0.00000000559 1.68e-8 ****        
## 3 A1c   M3       M6        30    30       235 0.964         1   e+0 ns
# 4. MIXED DESIGN ANOVA (Treatment [between] x Waktu [within])
# Pertanyaan utama: apakah pola perubahan HbA1c berbeda menurut treatment?

# 4a. Uji asumsi
dat_long |> group_by(Treatment, waktu) |> identify_outliers(A1c)
## # A tibble: 2 × 7
##   Treatment waktu ID      A1c bulan is.outlier is.extreme
##   <fct>     <fct> <fct> <dbl> <dbl> <lgl>      <lgl>     
## 1 TBES      M3    TB03   5.5      3 TRUE       FALSE     
## 2 TBES      M3    TB26   5.06     3 TRUE       FALSE
dat_long |> group_by(Treatment, waktu) |> shapiro_test(A1c)
## # A tibble: 9 × 5
##   Treatment waktu    variable statistic     p
##   <fct>     <fct>    <chr>        <dbl> <dbl>
## 1 TBES      Baseline A1c          0.947 0.142
## 2 TBES      M3       A1c          0.964 0.393
## 3 TBES      M6       A1c          0.962 0.356
## 4 vMMWD     Baseline A1c          0.969 0.503
## 5 vMMWD     M3       A1c          0.956 0.248
## 6 vMMWD     M6       A1c          0.962 0.357
## 7 Combined  Baseline A1c          0.985 0.938
## 8 Combined  M3       A1c          0.976 0.705
## 9 Combined  M6       A1c          0.966 0.431
ggpubr::ggqqplot(dat_long, "A1c", ggtheme = theme_bw()) +
  facet_grid(waktu ~ Treatment)

dat_long |> group_by(waktu) |> levene_test(A1c ~ Treatment)
## # A tibble: 3 × 5
##   waktu      df1   df2 statistic     p
##   <fct>    <int> <int>     <dbl> <dbl>
## 1 Baseline     2    87     0.109 0.897
## 2 M3           2    87     0.497 0.610
## 3 M6           2    87     0.118 0.889
box_m(dat_wide[, c("A1c_Baseline", "A1c_M3", "A1c_M6")],
      dat_wide$Treatment)
## # A tibble: 1 × 4
##   statistic p.value parameter method                                            
##       <dbl>   <dbl>     <dbl> <chr>                                             
## 1      16.0   0.192        12 Box's M-test for Homogeneity of Covariance Matric…
# 4b. Mixed Repeated Measures ANOVA
aov2 <- aov_ez(id = "ID", dv = "A1c", data = dat_long,
               between = "Treatment", within = "waktu",
               anova_table = list(es = c("ges", "pes"), correction = "GG"))
aov2
## Anova Table (Type 3 tests)
## 
## Response: A1c
##            Effect           df  MSE          F  ges  pes p.value
## 1       Treatment        2, 87 4.71       1.09 .023 .024    .340
## 2           waktu 1.79, 156.13 0.18 215.25 *** .139 .712   <.001
## 3 Treatment:waktu 3.59, 156.13 0.18       1.85 .003 .041    .129
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
## 
## Sphericity correction method: GG
summary(aov2)
## 
## Univariate Type III Repeated-Measures ANOVA Assuming Sphericity
## 
##                  Sum Sq num Df Error SS den Df   F value Pr(>F)    
## (Intercept)     19145.4      1   409.65     87 4066.0191 <2e-16 ***
## Treatment          10.3      2   409.65     87    1.0923  0.340    
## waktu              71.0      2    28.70    174  215.2504 <2e-16 ***
## Treatment:waktu     1.2      4    28.70    174    1.8525  0.121    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Mauchly Tests for Sphericity
## 
##                 Test statistic   p-value
## waktu                  0.88555 0.0053716
## Treatment:waktu        0.88555 0.0053716
## 
## 
## Greenhouse-Geisser and Huynh-Feldt Corrections
##  for Departure from Sphericity
## 
##                 GG eps Pr(>F[GG])    
## waktu           0.8973     <2e-16 ***
## Treatment:waktu 0.8973     0.1288    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##                    HF eps   Pr(>F[HF])
## waktu           0.9149949 5.923888e-44
## Treatment:waktu 0.9149949 1.274077e-01
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.97905   4066.0      1     87 <2e-16 ***
## Treatment        2   0.02450      1.1      2     87 0.3400    
## waktu            1   0.86347    272.0      2     86 <2e-16 ***
## Treatment:waktu  2   0.06794      1.5      4    174 0.1956    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Versi rstatix
aov2_rs <- anova_test(data = dat_long, dv = A1c, wid = ID,
                      between = Treatment, 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       Treatment 2.00  87.00   1.092 3.40e-01       0.024
## 2           waktu 1.79 156.13 215.250 3.71e-43     * 0.712
## 3 Treatment:waktu 3.59 156.13   1.852 1.29e-01       0.041
eta_squared(aov2, partial = TRUE)
## # Effect Size for ANOVA (Type III)
## 
## Parameter       | Eta2 (partial) |       95% CI
## -----------------------------------------------
## Treatment       |           0.02 | [0.00, 1.00]
## waktu           |           0.71 | [0.66, 1.00]
## Treatment:waktu |           0.04 | [0.00, 1.00]
## 
## - One-sided CIs: upper bound fixed at [1.00].
# 4c. Efek sederhana / post hoc
# Karena artikel sumber tidak menemukan superioritas antarmodalitas,
# bagian ini dibaca sebagai analisis pendukung, terutama bila interaksi tidak signifikan.
em2 <- emmeans(aov2, ~ waktu | Treatment)
joint_tests(aov2, by = "Treatment")
## Warning in pf(conf$F.ratio, conf$df1, conf$df2, lower.tail = FALSE): NaNs
## produced
## Treatment = TBES:
##  model term df1 df2 F.ratio p.value
##  waktu        2  87  99.691 <0.0001
## 
## Treatment = vMMWD:
##  model term df1 df2 F.ratio p.value
##  waktu        2  87  85.038 <0.0001
## 
## Treatment = Combined:
##  model term df1 df2 F.ratio p.value
##  waktu        2  87  93.525 <0.0001
joint_tests(aov2, by = "waktu")
## waktu = Baseline:
##  model term df1 df2 F.ratio p.value
##  Treatment    2  87   1.610  0.2059
## 
## waktu = M3:
##  model term df1 df2 F.ratio p.value
##  Treatment    2  87   0.309  0.7346
## 
## waktu = M6:
##  model term df1 df2 F.ratio p.value
##  Treatment    2  87   1.681  0.1922
contrast(em2, "trt.vs.ctrl", ref = 1, adjust = "holm")
## Treatment = TBES:
##  contrast      estimate     SE df t.ratio p.value
##  M3 - Baseline   -1.171 0.1170 87 -10.030 <0.0001
##  M6 - Baseline   -1.154 0.0861 87 -13.404 <0.0001
## 
## Treatment = vMMWD:
##  contrast      estimate     SE df t.ratio p.value
##  M3 - Baseline   -1.080 0.1170 87  -9.250 <0.0001
##  M6 - Baseline   -1.066 0.0861 87 -12.386 <0.0001
## 
## Treatment = Combined:
##  contrast      estimate     SE df t.ratio p.value
##  M3 - Baseline   -0.868 0.1170 87  -7.435 <0.0001
##  M6 - Baseline   -1.171 0.0861 87 -13.602 <0.0001
## 
## P value adjustment: holm method for 2 tests
em2b <- emmeans(aov2, ~ Treatment | waktu)
pairs(em2b, adjust = "tukey")
## waktu = Baseline:
##  contrast         estimate    SE df t.ratio p.value
##  TBES - vMMWD        0.289 0.320 87   0.905  0.6384
##  TBES - Combined     0.573 0.320 87   1.794  0.1775
##  vMMWD - Combined    0.284 0.320 87   0.889  0.6488
## 
## waktu = M3:
##  contrast         estimate    SE df t.ratio p.value
##  TBES - vMMWD        0.198 0.356 87   0.557  0.8430
##  TBES - Combined     0.270 0.356 87   0.760  0.7287
##  vMMWD - Combined    0.072 0.356 87   0.202  0.9777
## 
## waktu = M6:
##  contrast         estimate    SE df t.ratio p.value
##  TBES - vMMWD        0.202 0.327 87   0.616  0.8117
##  TBES - Combined     0.590 0.327 87   1.804  0.1744
##  vMMWD - Combined    0.389 0.327 87   1.188  0.4638
## 
## P value adjustment: tukey method for comparing a family of 3 estimates
# 4d. Kontras perubahan baseline -> 6 bulan
em_full <- emmeans(aov2, ~ waktu * Treatment)
contrast(em_full,
         interaction = list(waktu = list("M6-Baseline" = c(-1, 0, 1)),
                            Treatment = "pairwise"),
         adjust = "holm")
##  waktu_custom Treatment_pairwise estimate    SE df t.ratio p.value
##  M6-Baseline  TBES - vMMWD        -0.0877 0.122 87  -0.720  1.0000
##  M6-Baseline  TBES - Combined      0.0170 0.122 87   0.140  1.0000
##  M6-Baseline  vMMWD - Combined     0.1047 0.122 87   0.860  1.0000
## 
## P value adjustment: holm method for 3 tests
# 5. PEMBANDING: LINEAR MIXED MODEL
lmm1 <- lmer(A1c ~ Treatment * waktu + (1 | ID), data = dat_long, REML = TRUE)
lmm2 <- lmer(A1c ~ Treatment * waktu + (1 + bulan | ID), data = dat_long,
             REML = TRUE, control = lmerControl(optimizer = "bobyqa"))
## boundary (singular) fit: see help('isSingular')
anova(lmm1, lmm2, refit = FALSE)
## Data: dat_long
## Models:
## lmm1: A1c ~ Treatment * waktu + (1 | ID)
## lmm2: A1c ~ Treatment * waktu + (1 + bulan | ID)
##      npar    AIC    BIC  logLik -2*log(L)  Chisq Df Pr(>Chisq)
## lmm1   11 627.70 667.28 -302.85    605.70                     
## lmm2   13 631.35 678.13 -302.67    605.35 0.3521  2     0.8386
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)    
## Treatment        0.360   0.180     2  87.00   1.0923 0.3400    
## waktu           70.769  35.384     2 115.30 213.7465 <2e-16 ***
## Treatment:waktu  1.222   0.305     4 129.03   1.8416 0.1248    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
performance::icc(lmm1)
## # Intraclass Correlation Coefficient
## 
##     Adjusted ICC: 0.902
##   Unadjusted ICC: 0.763
performance::check_model(lmm2)

# 6. SIMPAN RINGKASAN
write.csv(dat_long, "Data_Simulasi_A1c_DSMES_3x3_LONG.csv", row.names = FALSE)