# TUGAS BIOSTATISTIKA INTERMEDIATE
# Nama : Nisa Nur Fitriana
# Nim : 2611018026
# REPEATED MEASURE ANALYSIS - PERCEIVED SUSCEPTIBILITY, 3 TREATMENT x 3 WAKTU
# Sumber: Bahrami A, Rahimzadeh M, Safari-Moradabadi A. (2025)
# Preventive Medicine Reports, 57, 103198. DOI: 10.1016/j.pmedr.2025.103198
# PMID: 40838176; PMCID: PMC12362373.
#
# CATATAN: Data individual pada CSV adalah DATA SIMULASI untuk latihan statistik.
# Mean dan SD setiap sel dikalibrasi pada Tabel 3 artikel asli untuk konstruk
# perceived susceptibility. Statistik inferensial di bawah berasal dari data simulasi,
# bukan reproduksi output statistik artikel asli.
# 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. DATA
dat_wide <- read.csv("FINAL_PAP_HBM_Data.csv")
dat_wide$id <- factor(dat_wide$id)
dat_wide$treatment <- factor(dat_wide$treatment,
levels=c("Control","In-Person","Mobile-Based"))
dat_long <- dat_wide |>
pivot_longer(c(susceptibility_baseline,
susceptibility_immediate,
susceptibility_3months),
names_to="time",values_to="susceptibility") |>
mutate(time=factor(time,
levels=c("susceptibility_baseline","susceptibility_immediate","susceptibility_3months"),
labels=c("Baseline","Immediate post","3 months")))
head(dat_wide)
## id treatment susceptibility_baseline susceptibility_immediate
## 1 C01 Control 25.870 28.213
## 2 C02 Control 22.517 28.209
## 3 C03 Control 18.664 17.195
## 4 C04 Control 17.766 19.127
## 5 C05 Control 26.151 23.855
## 6 C06 Control 29.986 25.205
## susceptibility_3months
## 1 23.668
## 2 25.822
## 3 20.268
## 4 20.381
## 5 30.006
## 6 21.566
str(dat_long)
## tibble [405 × 4] (S3: tbl_df/tbl/data.frame)
## $ id : Factor w/ 135 levels "C01","C02","C03",..: 1 1 1 2 2 2 3 3 3 4 ...
## $ treatment : Factor w/ 3 levels "Control","In-Person",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ time : Factor w/ 3 levels "Baseline","Immediate post",..: 1 2 3 1 2 3 1 2 3 1 ...
## $ susceptibility: num [1:405] 25.9 28.2 23.7 22.5 28.2 ...
# 2. EKSPLORASI DATA
dat_long |> group_by(treatment,time) |>
get_summary_stats(susceptibility,type="mean_sd")
## # A tibble: 9 × 6
## treatment time variable n mean sd
## <fct> <fct> <fct> <dbl> <dbl> <dbl>
## 1 Control Baseline susceptibility 45 22.5 5.19
## 2 Control Immediate post susceptibility 45 22.8 5.15
## 3 Control 3 months susceptibility 45 22.8 5.28
## 4 In-Person Baseline susceptibility 45 22.5 5.25
## 5 In-Person Immediate post susceptibility 45 26.4 4.19
## 6 In-Person 3 months susceptibility 45 25.2 4.6
## 7 Mobile-Based Baseline susceptibility 45 24.3 3.96
## 8 Mobile-Based Immediate post susceptibility 45 27.5 3.62
## 9 Mobile-Based 3 months susceptibility 45 25.6 3.76
ggplot(dat_long,aes(time,susceptibility,colour=treatment,group=treatment)) +
stat_summary(fun=mean,geom="line",linewidth=1) +
stat_summary(fun=mean,geom="point",size=2.5) +
labs(y="Perceived susceptibility score",x="Time",colour="Treatment")

ggplot(dat_long,aes(time,susceptibility,group=id,colour=treatment)) +
geom_line(alpha=.20) + facet_wrap(~treatment) +
stat_summary(aes(group=1),fun=mean,geom="line",linewidth=1.2) +
labs(y="Perceived susceptibility score",x="Time")

# 3. REPEATED MEASURE ANOVA SATU ARAH - Mobile-Based
d1 <- droplevels(filter(dat_long,treatment=="Mobile-Based"))
d1 |> group_by(time) |> identify_outliers(susceptibility)
## # A tibble: 1 × 6
## time id treatment susceptibility is.outlier is.extreme
## <fct> <fct> <fct> <dbl> <lgl> <lgl>
## 1 3 months M28 Mobile-Based 15.2 TRUE FALSE
d1 |> group_by(time) |> shapiro_test(susceptibility)
## # A tibble: 3 × 4
## time variable statistic p
## <fct> <chr> <dbl> <dbl>
## 1 Baseline susceptibility 0.962 0.146
## 2 Immediate post susceptibility 0.975 0.435
## 3 3 months susceptibility 0.967 0.234
ggpubr::ggqqplot(d1,"susceptibility",facet.by="time")

aov1_rs <- anova_test(data=d1,dv=susceptibility,wid=id,within=time,effect.size="pes")
aov1_rs
## ANOVA Table (type III tests)
##
## $ANOVA
## Effect DFn DFd F p p<.05 pes
## 1 time 2 88 23.687 5.88e-09 * 0.35
##
## $`Mauchly's Test for Sphericity`
## Effect W p p<.05
## 1 time 0.994 0.872
##
## $`Sphericity Corrections`
## Effect GGe DF[GG] p[GG] p[GG]<.05 HFe DF[HF] p[HF]
## 1 time 0.994 1.99, 87.44 6.49e-09 * 1.041 2.08, 91.57 5.88e-09
## p[HF]<.05
## 1 *
get_anova_table(aov1_rs,correction="auto")
## ANOVA Table (type III tests)
##
## Effect DFn DFd F p p<.05 pes
## 1 time 2 88 23.687 5.88e-09 * 0.35
aov1 <- aov_ez(id="id",dv="susceptibility",data=d1,within="time",
anova_table=list(es=c("ges","pes"),correction="GG"))
aov1
## Anova Table (Type 3 tests)
##
## Response: susceptibility
## Effect df MSE F ges pes p.value
## 1 time 1.99, 87.44 4.93 23.69 *** .109 .350 <.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) 89815 1 1457.71 44 2710.994 < 2.2e-16 ***
## time 232 2 430.93 88 23.687 5.883e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Mauchly Tests for Sphericity
##
## Test statistic p-value
## time 0.99363 0.87166
##
##
## Greenhouse-Geisser and Huynh-Feldt Corrections
## for Departure from Sphericity
##
## GG eps Pr(>F[GG])
## time 0.99367 6.493e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## HF eps Pr(>F[HF])
## time 1.040525 5.882585e-09
em1 <- emmeans(aov1,~time)
pairs(em1,adjust="holm")
## contrast estimate SE df t.ratio p.value
## Baseline - Immediate.post -3.20 0.478 44 -6.697 <0.0001
## Baseline - X3.months -1.37 0.473 44 -2.894 0.0059
## Immediate.post - X3.months 1.83 0.448 44 4.088 0.0004
##
## P value adjustment: holm method for 3 tests
friedman_test(d1,susceptibility ~ time | id)
## # A tibble: 1 × 6
## .y. n statistic df p method
## * <chr> <int> <dbl> <dbl> <dbl> <chr>
## 1 susceptibility 45 28.3 2 0.000000712 Friedman test
d1 |> wilcox_test(susceptibility ~ time,paired=TRUE,p.adjust.method="holm")
## # 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 susceptibili… Basel… Immed… 45 45 88 9.62e-8 2.89e-7 ****
## 2 susceptibili… Basel… 3 mon… 45 45 286 8.21e-3 8.21e-3 **
## 3 susceptibili… Immed… 3 mon… 45 45 839 1.63e-4 3.27e-4 ***
# 4. MIXED DESIGN ANOVA: treatment (between) x time (within)
dat_long |> group_by(treatment,time) |> shapiro_test(susceptibility)
## # A tibble: 9 × 5
## treatment time variable statistic p
## <fct> <fct> <chr> <dbl> <dbl>
## 1 Control Baseline susceptibility 0.962 0.152
## 2 Control Immediate post susceptibility 0.985 0.813
## 3 Control 3 months susceptibility 0.978 0.531
## 4 In-Person Baseline susceptibility 0.974 0.407
## 5 In-Person Immediate post susceptibility 0.968 0.253
## 6 In-Person 3 months susceptibility 0.971 0.326
## 7 Mobile-Based Baseline susceptibility 0.962 0.146
## 8 Mobile-Based Immediate post susceptibility 0.975 0.435
## 9 Mobile-Based 3 months susceptibility 0.967 0.234
dat_long |> group_by(time) |> levene_test(susceptibility ~ treatment)
## # A tibble: 3 × 5
## time df1 df2 statistic p
## <fct> <int> <int> <dbl> <dbl>
## 1 Baseline 2 132 0.665 0.516
## 2 Immediate post 2 132 1.61 0.203
## 3 3 months 2 132 2.02 0.136
box_m(dat_wide[,c("susceptibility_baseline","susceptibility_immediate","susceptibility_3months")],
dat_wide$treatment)
## # A tibble: 1 × 4
## statistic p.value parameter method
## <dbl> <dbl> <dbl> <chr>
## 1 12.2 0.432 12 Box's M-test for Homogeneity of Covariance Matric…
aov2 <- aov_ez(id="id",dv="susceptibility",data=dat_long,
between="treatment",within="time",
anova_table=list(es=c("ges","pes"),correction="GG"))
aov2
## Anova Table (Type 3 tests)
##
## Response: susceptibility
## Effect df MSE F ges pes p.value
## 1 treatment 2, 132 53.71 6.23 ** .074 .086 .003
## 2 time 1.95, 257.30 5.03 42.59 *** .047 .244 <.001
## 3 treatment:time 3.90, 257.30 5.03 9.38 *** .021 .124 <.001
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
##
## Sphericity correction method: GG
summary(aov2) # termasuk Mauchly dan koreksi GG/HF
##
## Univariate Type III Repeated-Measures ANOVA Assuming Sphericity
##
## Sum Sq num Df Error SS den Df F value Pr(>F)
## (Intercept) 240989 1 7089.3 132 4487.1076 < 2.2e-16 ***
## treatment 669 2 7089.3 132 6.2255 0.002608 **
## time 418 2 1294.5 264 42.5906 < 2.2e-16 ***
## treatment:time 184 4 1294.5 264 9.3762 4.226e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Mauchly Tests for Sphericity
##
## Test statistic p-value
## time 0.97398 0.17782
## treatment:time 0.97398 0.17782
##
##
## Greenhouse-Geisser and Huynh-Feldt Corrections
## for Departure from Sphericity
##
## GG eps Pr(>F[GG])
## time 0.97464 < 2.2e-16 ***
## treatment:time 0.97464 5.688e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## HF eps Pr(>F[HF])
## time 0.9890518 1.333939e-16
## treatment:time 0.9890518 4.804119e-07
eta_squared(aov2,partial=TRUE)
## # Effect Size for ANOVA (Type III)
##
## Parameter | Eta2 (partial) | 95% CI
## ----------------------------------------------
## treatment | 0.09 | [0.02, 1.00]
## time | 0.24 | [0.17, 1.00]
## treatment:time | 0.12 | [0.06, 1.00]
##
## - One-sided CIs: upper bound fixed at [1.00].
# Simple effects
joint_tests(aov2,by="treatment")
## treatment = Control:
## model term df1 df2 F.ratio p.value
## time 2 132 0.245 0.7834
##
## treatment = In-Person:
## model term df1 df2 F.ratio p.value
## time 2 132 34.317 <0.0001
##
## treatment = Mobile-Based:
## model term df1 df2 F.ratio p.value
## time 2 132 23.753 <0.0001
joint_tests(aov2,by="time")
## time = Baseline:
## model term df1 df2 F.ratio p.value
## treatment 2 132 2.043 0.1337
##
## time = Immediate.post:
## model term df1 df2 F.ratio p.value
## treatment 2 132 14.454 <0.0001
##
## time = X3.months:
## model term df1 df2 F.ratio p.value
## treatment 2 132 4.891 0.0089
# Post hoc waktu pada tiap treatment
em2 <- emmeans(aov2,~time|treatment)
contrast(em2,"trt.vs.ctrl",ref=1,adjust="holm")
## treatment = Control:
## contrast estimate SE df t.ratio p.value
## Immediate.post - Baseline 0.27 0.478 132 0.565 1.0000
## X3.months - Baseline 0.33 0.492 132 0.671 1.0000
##
## treatment = In-Person:
## contrast estimate SE df t.ratio p.value
## Immediate.post - Baseline 3.95 0.478 132 8.265 <0.0001
## X3.months - Baseline 2.70 0.492 132 5.491 <0.0001
##
## treatment = Mobile-Based:
## contrast estimate SE df t.ratio p.value
## Immediate.post - Baseline 3.20 0.478 132 6.696 <0.0001
## X3.months - Baseline 1.37 0.492 132 2.786 0.0061
##
## P value adjustment: holm method for 2 tests
# Perbandingan treatment pada tiap waktu
em2b <- emmeans(aov2,~treatment|time)
pairs(em2b,adjust="tukey")
## time = Baseline:
## contrast estimate SE df t.ratio p.value
## Control - (In-Person) 0.00996 1.020 132 0.010 0.9999
## Control - (Mobile-Based) -1.78000 1.020 132 -1.746 0.1922
## (In-Person) - (Mobile-Based) -1.78996 1.020 132 -1.755 0.1888
##
## time = Immediate.post:
## contrast estimate SE df t.ratio p.value
## Control - (In-Person) -3.67000 0.920 132 -3.987 0.0003
## Control - (Mobile-Based) -4.71000 0.920 132 -5.117 <0.0001
## (In-Person) - (Mobile-Based) -1.04000 0.920 132 -1.130 0.4974
##
## time = X3.months:
## contrast estimate SE df t.ratio p.value
## Control - (In-Person) -2.35996 0.967 132 -2.439 0.0421
## Control - (Mobile-Based) -2.81998 0.967 132 -2.915 0.0116
## (In-Person) - (Mobile-Based) -0.46002 0.967 132 -0.476 0.8830
##
## P value adjustment: tukey method for comparing a family of 3 estimates
# Kontras perubahan: immediate-baseline dan 3months-baseline antarkelompok
em_full <- emmeans(aov2,~time*treatment)
contrast(em_full,
interaction=list(time=list("Immediate-Baseline"=c(-1,1,0)),treatment="pairwise"),
adjust="holm")
## time_custom treatment_pairwise estimate SE df t.ratio
## Immediate-Baseline Control - (In-Person) -3.68 0.676 132 -5.445
## Immediate-Baseline Control - (Mobile-Based) -2.93 0.676 132 -4.335
## Immediate-Baseline (In-Person) - (Mobile-Based) 0.75 0.676 132 1.110
## p.value
## <0.0001
## <0.0001
## 0.2692
##
## P value adjustment: holm method for 3 tests
contrast(em_full,
interaction=list(time=list("3months-Baseline"=c(-1,0,1)),treatment="pairwise"),
adjust="holm")
## time_custom treatment_pairwise estimate SE df t.ratio
## 3months-Baseline Control - (In-Person) -2.37 0.695 132 -3.408
## 3months-Baseline Control - (Mobile-Based) -1.04 0.695 132 -1.496
## 3months-Baseline (In-Person) - (Mobile-Based) 1.33 0.695 132 1.913
## p.value
## 0.0026
## 0.1371
## 0.1159
##
## P value adjustment: holm method for 3 tests
# 5. PEMBANDING: LINEAR MIXED MODEL
lmm <- lmer(susceptibility ~ treatment*time + (1|id),data=dat_long,REML=TRUE)
anova(lmm,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 61.05 30.526 2 132 6.2255 0.002608 **
## time 417.69 208.843 2 264 42.5906 < 2.2e-16 ***
## treatment:time 183.90 45.976 4 264 9.3762 4.226e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(lmm)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: susceptibility ~ treatment * time + (1 | id)
## Data: dat_long
##
## REML criterion at convergence: 2116.8
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -2.02243 -0.54999 -0.03114 0.55121 2.40500
##
## Random effects:
## Groups Name Variance Std.Dev.
## id (Intercept) 16.268 4.033
## Residual 4.903 2.214
## Number of obs: 405, groups: id, 135
##
## Fixed effects:
## Estimate Std. Error df t value Pr(>|t|)
## (Intercept) 24.3933 0.3642 132.0000 66.986 < 2e-16 ***
## treatment1 -1.7033 0.5150 132.0000 -3.307 0.00121 **
## treatment2 0.3033 0.5150 132.0000 0.589 0.55686
## time1 -1.3133 0.1556 264.0000 -8.440 2.13e-15 ***
## time2 1.1600 0.1556 264.0000 7.455 1.29e-12 ***
## treatment1:time1 1.1133 0.2201 264.0000 5.059 7.90e-07 ***
## treatment2:time1 -0.9033 0.2201 264.0000 -4.105 5.40e-05 ***
## treatment1:time2 -1.0900 0.2201 264.0000 -4.953 1.31e-06 ***
## treatment2:time2 0.5733 0.2201 264.0000 2.605 0.00970 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of Fixed Effects:
## (Intr) trtmn1 trtmn2 time1 time2 trt1:1 trt2:1 trt1:2
## treatment1 0.000
## treatment2 0.000 -0.500
## time1 0.000 0.000 0.000
## time2 0.000 0.000 0.000 -0.500
## trtmnt1:tm1 0.000 0.000 0.000 0.000 0.000
## trtmnt2:tm1 0.000 0.000 0.000 0.000 0.000 -0.500
## trtmnt1:tm2 0.000 0.000 0.000 0.000 0.000 -0.500 0.250
## trtmnt2:tm2 0.000 0.000 0.000 0.000 0.000 0.250 -0.500 -0.500
performance::icc(lmm)
## # Intraclass Correlation Coefficient
##
## Adjusted ICC: 0.768
## Unadjusted ICC: 0.669
# 6. SIMPAN RINGKASAN
write.csv(dat_long,"FINAL_PAP_HBM_Data_LONG.csv",row.names=FALSE)