# NAMA : Elita Rohutami
# NIM : 2611018013


# =============================================================================
#  REPEATED MEASURE ANALYSIS DENGAN R
#  Contoh terapan: Program intervensi Anemia Pada Remaja Putri di Wilayah Kerja 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 Anemia derajat 1
# diacak ke tiga kelompok (n = 30 per kelompok):
#   - Kontrol  : edukasi standar puskesmas
#   - TTD     : edukasi TTD (rendah garam, tinggi sayur-buah)
#   - TTD+KIE  : edukasi TTD + Komunikasi Informasi dan Edukasi terstruktur
# Anemia (Hb, 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", "TTD", "TTD+KIE")
minggu  <- c(0, 4, 8, 12)

# Rerata populasi (mg/dl) per kelompok x waktu
mu <- rbind(
  "Kontrol" = c(11, 11.1, 11, 11.2),
  "TTD"    = c(10.8, 11.1, 11.3, 11.5),
  "TTD+KIE" = c(10.9, 11.3, 11.7, 11.9)
)

sd_int   <- 9     # SD intersep acak (perbedaan TTD dasar antarpasien)
sd_slope <- 0.45  # SD kemiringan acak per minggu (perbedaan respons antarpasien)
sd_eps   <- 4.5   # 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("Hb_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(0, 1)),
             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("Hb_M"), names_to = "waktu", values_to = "hb") |>
  mutate(waktu  = factor(waktu, levels = paste0("Hb_M", minggu),
                         labels = paste0("M", minggu)),
         minggu = as.numeric(sub("M", "", waktu)))

head(dat_wide)
##     id kelompok usia jk Hb_M0 Hb_M4 Hb_M8 Hb_M12
## 1 P001  Kontrol   42  P  15.0   6.0  22.4   13.1
## 2 P002  Kontrol   43  P   2.0   8.3  11.5    3.9
## 3 P003  Kontrol   62  P  11.4   9.8  12.4   15.5
## 4 P004  Kontrol   40  P   8.9  15.4  15.5   14.5
## 5 P005  Kontrol   53  P  -2.8  -2.8  -2.2    8.8
## 6 P006  Kontrol   42  P -12.9 -10.6  -5.3    0.0
head(dat_long)
## # A tibble: 6 × 7
##   id    kelompok  usia jk    waktu    hb minggu
##   <fct> <fct>    <dbl> <chr> <fct> <dbl>  <dbl>
## 1 P001  Kontrol     42 P     M0     15        0
## 2 P001  Kontrol     42 P     M4      6        4
## 3 P001  Kontrol     42 P     M8     22.4      8
## 4 P001  Kontrol     42 P     M12    13.1     12
## 5 P002  Kontrol     43 P     M0      2        0
## 6 P002  Kontrol     43 P     M4      8.3      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","TTD",..: 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] "P" "P" "P" "P" ...
##  $ waktu   : Factor w/ 4 levels "M0","M4","M8",..: 1 2 3 4 1 2 3 4 1 2 ...
##  $ hb      : num [1:360] 15 6 22.4 13.1 2 8.3 11.5 3.9 11.4 9.8 ...
##  $ 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(hb, type = "mean_sd")
desk
## # A tibble: 12 × 6
##    kelompok waktu variable     n  mean    sd
##    <fct>    <fct> <fct>    <dbl> <dbl> <dbl>
##  1 Kontrol  M0    hb          30 10.5  11.9 
##  2 Kontrol  M4    hb          30  9.15 10.8 
##  3 Kontrol  M8    hb          30 11.2   9.49
##  4 Kontrol  M12   hb          30  9.89 10.7 
##  5 TTD      M0    hb          30 12.2   9.48
##  6 TTD      M4    hb          30 12.5  10.5 
##  7 TTD      M8    hb          30 12.3   9.69
##  8 TTD      M12   hb          30 12.7   9.91
##  9 TTD+KIE  M0    hb          30 11.4  11.8 
## 10 TTD+KIE  M4    hb          30 12.1  11.9 
## 11 TTD+KIE  M8    hb          30 12.9  12.8 
## 12 TTD+KIE  M12   hb          30 14.3  14.5
# Matriks kovarians & korelasi antarwaktu (seluruh subjek, dalam kelompok)
# -> memberi gambaran awal apakah sfierisitas masuk akal
S  <- cov(dat_wide[, paste0("Hb_M", minggu)])
R  <- cor(dat_wide[, paste0("Hb_M", minggu)])
round(S, 1); round(R, 2)
##        Hb_M0 Hb_M4 Hb_M8 Hb_M12
## Hb_M0  120.8  96.8  90.3   89.2
## Hb_M4   96.8 122.4  97.7  102.0
## Hb_M8   90.3  97.7 114.3  105.8
## Hb_M12  89.2 102.0 105.8  140.9
##        Hb_M0 Hb_M4 Hb_M8 Hb_M12
## Hb_M0   1.00  0.80  0.77   0.68
## Hb_M4   0.80  1.00  0.83   0.78
## Hb_M8   0.77  0.83  1.00   0.83
## Hb_M12  0.68  0.78  0.83   1.00
# Varians selisih antarpasangan waktu (inti asumsi sfierisitas)
pasangan <- combn(paste0("Hb_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)
##  Hb_M0 - Hb_M4  Hb_M0 - Hb_M8 Hb_M0 - Hb_M12  Hb_M4 - Hb_M8 Hb_M4 - Hb_M12 
##           49.6           54.4           83.3           41.2           59.2 
## Hb_M8 - Hb_M12 
##           43.6
# Profile plot: rerata +/- 95% CI per kelompok
p_profil <- ggplot(dat_long, aes(minggu, hb, 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 = "Anemia (mg/dl)", colour = "Kelompok",
       title = "Profil rerata TTD (± 95% CI)") +
  theme(legend.position = "bottom")
p_profil

# Spaghetti plot: lintasan tiap pasien
p_spag <- ggplot(dat_long, aes(minggu, hb, 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 = "Hb (mg/dl)", title = "Lintasan individu dan rerata kelompok")
p_spag

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

## 3a. Uji asumsi -------------------------------------------------------------
# (i) Outlier per waktu (ekstrem = di luar Q1-3IQR / Q3+3IQR)
d1 |> group_by(waktu) |> identify_outliers(hb)
## [1] waktu      id         kelompok   usia       jk         hb         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(hb)
## # A tibble: 4 × 4
##   waktu variable statistic     p
##   <fct> <chr>        <dbl> <dbl>
## 1 M0    hb           0.959 0.295
## 2 M4    hb           0.953 0.204
## 3 M8    hb           0.982 0.869
## 4 M12   hb           0.960 0.310
ggpubr::ggqqplot(d1, "hb", facet.by = "waktu")

# (iii) Sfierisitas: Mauchly (dilaporkan otomatis oleh anova_test & afex)
aov1_rs <- anova_test(data = d1, dv = hb, 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 1.377 0.255       0.045
## 
## $`Mauchly's Test for Sphericity`
##   Effect     W     p p<.05
## 1  waktu 0.533 0.004     *
## 
## $`Sphericity Corrections`
##   Effect   GGe      DF[GG] p[GG] p[GG]<.05   HFe      DF[HF] p[HF] p[HF]<.05
## 1  waktu 0.707 2.12, 61.53  0.26           0.765 2.29, 66.52  0.26
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.12 61.53 1.377 0.26       0.045
## 3b. ANOVA dengan afex (sumber utama laporan) -------------------------------
aov1 <- aov_ez(id = "id", dv = "hb", 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: hb
##   Effect          df   MSE    F  ges  pes p.value
## 1  waktu 2.12, 61.53 47.78 1.38 .007 .045    .260
## ---
## 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) 19304.0      1  16042.6     29 34.8957 2.055e-06 ***
## waktu         139.6      3   2939.9     87  1.3767    0.2553    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Mauchly Tests for Sphericity
## 
##       Test statistic   p-value
## waktu        0.53324 0.0037739
## 
## 
## Greenhouse-Geisser and Huynh-Feldt Corrections
##  for Departure from Sphericity
## 
##        GG eps Pr(>F[GG])
## waktu 0.70726     0.2603
## 
##          HF eps Pr(>F[HF])
## waktu 0.7645997  0.2597175
# Ukuran efek tambahan
eta_squared(aov1, partial = TRUE)
## # Effect Size for ANOVA (Type III)
## 
## Parameter | Eta2 (partial) |       95% CI
## -----------------------------------------
## waktu     |           0.05 | [0.00, 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     |         1.94e-03 | [0.00, 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.54614   34.896      1     29 2.055e-06 ***
## waktu        1   0.07878    0.770      3     27    0.5211    
## ---
## 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      11.4 2.15 29     7.04     15.8
##  M4      12.1 2.17 29     7.62     16.5
##  M8      12.9 2.35 29     8.13     17.7
##  M12     14.3 2.64 29     8.91     19.7
## 
## 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.630 1.44 29  -0.437  1.0000
##  M0 - M8    -1.493 1.52 29  -0.984  1.0000
##  M0 - M12   -2.877 2.05 29  -1.405  1.0000
##  M4 - M8    -0.863 1.00 29  -0.861  1.0000
##  M4 - M12   -2.247 1.52 29  -1.477  0.9034
##  M8 - M12   -1.383 1.28 29  -1.085  1.0000
## 
## 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.63 1.44 29   0.437  0.6665
##  M8 - M0      1.49 1.52 29   0.984  0.6665
##  M12 - M0     2.88 2.05 29   1.405  0.5115
## 
## P value adjustment: holm method for 3 tests
contrast(em1, "poly")                      # tren linear, kuadratik, kubik
##  contrast  estimate   SE df t.ratio p.value
##  linear       9.493 6.44 29   1.475  0.1511
##  quadratic    0.753 1.77 29   0.426  0.6733
##  cubic        0.287 3.24 29   0.089  0.9301
## 3e. Alternatif nonparametrik ------------------------------------------------
friedman_test(d1, hb ~ waktu | id)
## # A tibble: 1 × 6
##   .y.       n statistic    df     p method       
## * <chr> <int>     <dbl> <dbl> <dbl> <chr>        
## 1 hb       30      2.04     3 0.564 Friedman test
friedman_effsize(d1, hb ~ waktu | id)     # Kendall's W
## # A tibble: 1 × 5
##   .y.       n effsize method    magnitude
## * <chr> <int>   <dbl> <chr>     <ord>    
## 1 hb       30  0.0227 Kendall W small
d1 |> wilcox_test(hb ~ 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 hb    M0     M4        30    30      208. 0.630     1 ns          
## 2 hb    M0     M8        30    30      196. 0.455     1 ns          
## 3 hb    M0     M12       30    30      179  0.280     1 ns          
## 4 hb    M4     M8        30    30      186. 0.341     1 ns          
## 5 hb    M4     M12       30    30      167  0.182     1 ns          
## 6 hb    M8     M12       30    30      192. 0.419     1 ns
# (Opsional) ANOVA robust berbasis trimmed mean -- paket WRS2
if (requireNamespace("WRS2", quietly = TRUE)) {
  print(WRS2::rmanova(d1$hb, d1$waktu, d1$id, tr = 0.2))
}
## Call:
## WRS2::rmanova(y = d1$hb, groups = d1$waktu, blocks = d1$id, tr = 0.2)
## 
## Test statistic: F = 0.6974 
## Degrees of freedom 1: 2.21 
## Degrees of freedom 2: 37.51 
## p-value: 0.51766
# 4. MIXED DESIGN ANOVA  (Kelompok [between] x Waktu [within])
#    Pertanyaan: apakah pola perubahan TTD berbeda antar kelompok intervensi?
## 4a. Uji asumsi -------------------------------------------------------------
# (i) Outlier per sel
dat_long |> group_by(kelompok, waktu) |> identify_outliers(hb)
## # A tibble: 2 × 9
##   kelompok waktu id     usia jk       hb minggu is.outlier is.extreme
##   <fct>    <fct> <fct> <dbl> <chr> <dbl>  <dbl> <lgl>      <lgl>     
## 1 Kontrol  M8    P015     57 P     -12.9      8 TRUE       FALSE     
## 2 Kontrol  M12   P015     57 P     -18       12 TRUE       FALSE
# (ii) Normalitas per sel (3 x 4 = 12 sel) dan residual model
dat_long |> group_by(kelompok, waktu) |> shapiro_test(hb)
## # A tibble: 12 × 5
##    kelompok waktu variable statistic     p
##    <fct>    <fct> <chr>        <dbl> <dbl>
##  1 Kontrol  M0    hb           0.990 0.991
##  2 Kontrol  M4    hb           0.963 0.366
##  3 Kontrol  M8    hb           0.972 0.599
##  4 Kontrol  M12   hb           0.976 0.716
##  5 TTD      M0    hb           0.961 0.328
##  6 TTD      M4    hb           0.980 0.819
##  7 TTD      M8    hb           0.968 0.488
##  8 TTD      M12   hb           0.974 0.646
##  9 TTD+KIE  M0    hb           0.959 0.295
## 10 TTD+KIE  M4    hb           0.953 0.204
## 11 TTD+KIE  M8    hb           0.982 0.869
## 12 TTD+KIE  M12   hb           0.960 0.310
ggpubr::ggqqplot(dat_long, "hb", ggtheme = theme_bw()) +
  facet_grid(waktu ~ kelompok)

# (iii) Homogenitas varians antarkelompok pada TIAP waktu (Levene, median-centered)
dat_long |> group_by(waktu) |> levene_test(hb ~ kelompok)
## # A tibble: 4 × 5
##   waktu   df1   df2 statistic      p
##   <fct> <int> <int>     <dbl>  <dbl>
## 1 M0        2    87     0.844 0.434 
## 2 M4        2    87     0.672 0.514 
## 3 M8        2    87     2.04  0.137 
## 4 M12       2    87     4.76  0.0109
# (iv) Homogenitas matriks kovarians antarkelompok (Box's M; uji pada alpha = .001)
box_m(dat_wide[, paste0("Hb_M", minggu)], dat_wide$kelompok)
## # A tibble: 1 × 4
##   statistic p.value parameter method                                            
##       <dbl>   <dbl>     <dbl> <chr>                                             
## 1      19.6   0.486        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 = "hb", data = dat_long,
               between = "kelompok", within = "waktu",
               anova_table = list(es = c("ges", "pes"), correction = "GG"))
aov2
## Anova Table (Type 3 tests)
## 
## Response: hb
##           Effect           df    MSE    F  ges  pes p.value
## 1       kelompok        2, 87 419.79 0.54 .010 .012    .583
## 2          waktu 2.60, 226.52  31.95 0.90 .002 .010    .429
## 3 kelompok:waktu 5.21, 226.52  31.95 0.81 .003 .018    .545
## ---
## 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)     49815      1    36522     87 118.6661 <2e-16 ***
## kelompok          457      2    36522     87   0.5438 0.5825    
## waktu              75      3     7238    261   0.9032 0.4401    
## kelompok:waktu    135      6     7238    261   0.8135 0.5602    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Mauchly Tests for Sphericity
## 
##                Test statistic   p-value
## waktu                 0.80865 0.0026998
## kelompok:waktu        0.80865 0.0026998
## 
## 
## Greenhouse-Geisser and Huynh-Feldt Corrections
##  for Departure from Sphericity
## 
##                 GG eps Pr(>F[GG])
## waktu          0.86788     0.4288
## kelompok:waktu 0.86788     0.5454
## 
##                   HF eps Pr(>F[HF])
## waktu          0.8970366  0.4314913
## kelompok:waktu 0.8970366  0.5488237
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.57698  118.666      1     87 <2e-16 ***
## kelompok        2   0.01235    0.544      2     87 0.5825    
## waktu           1   0.02567    0.746      3     85 0.5274    
## kelompok:waktu  2   0.05205    0.766      6    172 0.5976    
## ---
## 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 = hb, 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 0.544 0.583       0.012
## 2          waktu 2.60 226.52 0.903 0.429       0.010
## 3 kelompok:waktu 5.21 226.52 0.814 0.545       0.018
# Ukuran efek
eta_squared(aov2, partial = TRUE)
## # Effect Size for ANOVA (Type III)
## 
## Parameter      | Eta2 (partial) |       95% CI
## ----------------------------------------------
## kelompok       |           0.01 | [0.00, 1.00]
## waktu          |           0.01 | [0.00, 1.00]
## kelompok:waktu |           0.02 | [0.00, 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 | [0.00, 1.00]
## waktu          |                0 | [0.00, 1.00]
## kelompok:waktu |                0 | [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 = "TTD (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   1.160  0.3299
## 
## kelompok = TTD:
##  model term df1 df2 F.ratio p.value
##  waktu        3  87   0.046  0.9870
## 
## kelompok = TTD+KIE:
##  model term df1 df2 F.ratio p.value
##  waktu        3  87   1.119  0.3460
# Efek KELOMPOK pada tiap waktu
joint_tests(aov2, by = "waktu")
## waktu = M0:
##  model term df1 df2 F.ratio p.value
##  kelompok     2  87   0.177  0.8384
## 
## waktu = M4:
##  model term df1 df2 F.ratio p.value
##  kelompok     2  87   0.816  0.4455
## 
## waktu = M8:
##  model term df1 df2 F.ratio p.value
##  kelompok     2  87   0.205  0.8149
## 
## waktu = M12:
##  model term df1 df2 F.ratio p.value
##  kelompok     2  87   1.067  0.3486
# 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    -1.353 1.29 87  -1.048  0.8920
##  M8 - M0     0.667 1.36 87   0.491  1.0000
##  M12 - M0   -0.617 1.66 87  -0.371  1.0000
## 
## kelompok = TTD:
##  contrast estimate   SE df t.ratio p.value
##  M4 - M0     0.317 1.29 87   0.245  1.0000
##  M8 - M0     0.113 1.36 87   0.083  1.0000
##  M12 - M0    0.473 1.66 87   0.285  1.0000
## 
## kelompok = TTD+KIE:
##  contrast estimate   SE df t.ratio p.value
##  M4 - M0     0.630 1.29 87   0.488  0.6267
##  M8 - M0     1.493 1.36 87   1.100  0.5491
##  M12 - M0    2.877 1.66 87   1.730  0.2618
## 
## 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 - TTD         -1.700 2.86 87  -0.593  0.8240
##  Kontrol - (TTD+KIE)   -0.930 2.86 87  -0.325  0.9436
##  TTD - (TTD+KIE)        0.770 2.86 87   0.269  0.9610
## 
## waktu = M4:
##  contrast            estimate   SE df t.ratio p.value
##  Kontrol - TTD         -3.370 2.86 87  -1.177  0.4697
##  Kontrol - (TTD+KIE)   -2.913 2.86 87  -1.018  0.5676
##  TTD - (TTD+KIE)        0.457 2.86 87   0.160  0.9861
## 
## waktu = M8:
##  contrast            estimate   SE df t.ratio p.value
##  Kontrol - TTD         -1.147 2.78 87  -0.412  0.9109
##  Kontrol - (TTD+KIE)   -1.757 2.78 87  -0.631  0.8036
##  TTD - (TTD+KIE)       -0.610 2.78 87  -0.219  0.9739
## 
## waktu = M12:
##  contrast            estimate   SE df t.ratio p.value
##  Kontrol - TTD         -2.790 3.06 87  -0.911  0.6349
##  Kontrol - (TTD+KIE)   -4.423 3.06 87  -1.444  0.3230
##  TTD - (TTD+KIE)       -1.633 3.06 87  -0.533  0.8552
## 
## 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 - TTD          -1.09 2.35 87  -0.463  0.6442
##  M12-M0       Kontrol - (TTD+KIE)    -3.49 2.35 87  -1.485  0.4234
##  M12-M0       TTD - (TTD+KIE)        -2.40 2.35 87  -1.022  0.6195
## 
## 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.17 5.33 87   0.032  0.9746
##  linear   TTD          1.22 5.33 87   0.228  0.8201
##  linear   TTD+KIE      9.49 5.33 87   1.780  0.0786
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 - TTD -1.046667 7.542958 87 -0.1387608 0.8899599
## 4     linear Kontrol - (TTD+KIE) -9.323333 7.542958 87 -1.2360314 0.2197737
## 7     linear     TTD - (TTD+KIE) -8.276667 7.542958 87 -1.0972707 0.2755509
##      p.holm
## 1 0.8899599
## 4 0.6593212
## 7 0.6593212
# 5. PEMBANDING: LINEAR MIXED MODEL (LMM)
#    Tidak mensyaratkan sfierisitas, menampung data hilang (MAR) dan waktu
#    pengukuran yang tidak seragam.
lmm1 <- lmer(hb ~ kelompok * waktu + (1 | id), data = dat_long, REML = TRUE)
lmm2 <- lmer(hb ~ 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: hb ~ kelompok * waktu + (1 | id)
## lmm2: hb ~ kelompok * waktu + (1 + minggu | id)
##      npar    AIC    BIC  logLik -2*log(L)  Chisq Df Pr(>Chisq)    
## lmm1   14 2466.2 2520.6 -1219.1    2438.2                         
## lmm2   16 2452.4 2514.6 -1210.2    2420.4 17.742  2  0.0001404 ***
## ---
## 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       22.038  11.019     2  87.00  0.5438 0.5825
## waktu          44.064  14.688     3 185.37  0.7215 0.5403
## kelompok:waktu 94.263  15.710     6 206.40  0.7709 0.5936
performance::icc(lmm1)                    # korelasi intrakelas
## # Intraclass Correlation Coefficient
## 
##     Adjusted ICC: 0.779
##   Unadjusted ICC: 0.768
# 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$hb[sample(which(dat_miss$waktu != "M0"), 30)] <- NA
lmm_miss <- lmer(hb ~ 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        26.502  13.251     2  86.984  0.6371 0.5313
## waktu           43.991  14.664     3 168.209  0.7017 0.5523
## kelompok:waktu 152.739  25.456     6 185.860  1.2167 0.2995
# RM ANOVA akan membuang seluruh pasien yang punya >= 1 nilai hilang:
n_distinct(dat_miss$id[is.na(dat_miss$hb)])
## [1] 25
# 6. SIMPAN DATA & SESSION INFO
write.csv(dat_wide, "data_hb_anemia_wide.csv", row.names = FALSE)
write.csv(dat_long, "data_hb_anemia_long.csv", row.names = FALSE)
sessionInfo()
## R version 4.6.1 (2026-06-24 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 10 x64 (build 19045)
## 
## 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/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] tidyselect_1.2.1    farver_2.1.2        S7_0.2.2           
##  [4] fastmap_1.2.0       reshape_0.8.10      bayestestR_0.19.0  
##  [7] digest_0.6.39       rpart_4.1.27        estimability_2.0.0 
## [10] lifecycle_1.0.5     cluster_2.1.8.2     magrittr_2.0.5     
## [13] compiler_4.6.1      rlang_1.3.0         Hmisc_5.3-0        
## [16] sass_0.4.10         tools_4.6.1         utf8_1.2.6         
## [19] yaml_2.3.12         data.table_1.18.6.1 ggsignif_0.6.4     
## [22] knitr_1.51          labeling_0.4.3      htmlwidgets_1.6.4  
## [25] plyr_1.8.9          RColorBrewer_1.1-3  abind_1.4-8        
## [28] withr_3.0.3         foreign_0.8-91      purrr_1.2.2        
## [31] numDeriv_2016.8-1.1 nnet_7.3-20         grid_4.6.1         
## [34] datawizard_1.4.0    ggpubr_1.0.0        colorspace_2.1-3   
## [37] scales_1.4.0        MASS_7.3-65         insight_1.5.4      
## [40] cli_3.6.6           mvtnorm_1.4-2       rmarkdown_2.31     
## [43] reformulas_0.4.4    generics_0.1.4      performance_0.18.2 
## [46] rstudioapi_0.19.0   reshape2_1.4.5      parameters_0.29.3  
## [49] minqa_1.2.8         cachem_1.1.0        stringr_1.6.0      
## [52] splines_4.6.1       parallel_4.6.1      WRS2_1.1-7         
## [55] base64enc_0.1-6     vctrs_0.7.3         boot_1.3-32        
## [58] jsonlite_2.0.0      pbkrtest_0.5.5      Formula_1.2-6      
## [61] htmlTable_2.5.0     jquerylib_0.1.4     glue_1.8.1         
## [64] nloptr_2.2.1        stringi_1.8.9       gtable_0.3.6       
## [67] tibble_3.3.1        pillar_1.11.1       htmltools_0.5.9    
## [70] R6_2.6.1            Rdpack_2.6.6        evaluate_1.0.5     
## [73] lattice_0.22-9      rbibutils_2.4.1     backports_1.5.1    
## [76] broom_1.0.13        bslib_0.12.0        Rcpp_1.1.2         
## [79] gridExtra_2.3.1     nlme_3.1-169        checkmate_2.3.4    
## [82] xfun_0.60           pkgconfig_2.0.3