Pemberian Makanan Tambahan (PMT) merupakan salah satu intervensi untuk memperbaiki status gizi balita. Laporan ini membandingkan perubahan berat badan balita selama tiga bulan pada tiga kelompok:
Berat badan ditimbang empat kali (bulan ke-0, 1, 2, dan 3) pada balita yang sama, sehingga datanya berupa pengukuran berulang (repeated measures).
Tujuan analisis:
| Komponen | Keterangan |
|---|---|
| Subjek | 90 balita (30 per kelompok) |
| Faktor antar-subjek (between) | Kelompok: Kontrol, PMT Biskuit, PMT Lokal |
| Faktor dalam-subjek (within) | Waktu: bulan 0, 1, 2, 3 |
| Variabel terikat | Berat badan (BB_kg, kg) |
| Kovariat | Usia (Usia_bulan, 12–59 bulan) dan jenis kelamin |
| Uji utama | Mixed (two-way) ANOVA 3 × 4, dengan koreksi Greenhouse-Geisser |
| Uji pendukung | RM ANOVA satu arah, Friedman, ANCOVA, linear mixed model (LMM) |
| Pertanyaan | Hasil utama |
|---|---|
| Apakah berat badan berubah seiring waktu? | Ya. Efek waktu signifikan, F(1,98; 172,38) = 803,10; p < 0,001; η² parsial = 0,902. |
| Apakah pola perubahan berbeda antarkelompok? | Ya. Interaksi kelompok × waktu signifikan, F(3,96; 172,38) = 70,39; p < 0,001; η² parsial = 0,618. |
| Berapa kenaikan berat badan bulan 3 − bulan 0? | Kontrol 0,34 kg; PMT Biskuit 0,95 kg; PMT Lokal 1,13 kg. |
| Apakah kenaikan antarkelompok berbeda? | Ya. Biskuit − Kontrol 0,61 kg; Lokal − Kontrol 0,79 kg; Lokal − Biskuit 0,18 kg (semua p ≤ 0,0014, Holm). |
| Apakah efek utama kelompok signifikan? | Tidak (p = 0,687). Berat badan awal ketiga kelompok mirip, sehingga perbedaan baru tampak pada laju kenaikan. |
| Apakah hasil tahan terhadap koreksi usia dan jenis kelamin? | Ya. Pada ANCOVA dan LMM, perbedaan antarkelompok tetap signifikan (p < 0,001). |
paket <- c("readxl", "dplyr", "tidyr", "ggplot2", "afex", "emmeans",
"rstatix", "car", "effectsize", "lme4", "lmerTest", "pbkrtest",
"performance", "ggpubr", "Hmisc", "knitr")
baru <- paket[!paket %in% rownames(installed.packages())]
if (length(baru) > 0) install.packages(baru)
suppressPackageStartupMessages({
library(readxl)
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))
Pengaturan contr.sum dipakai agar jumlah kuadrat tipe
III benar. emmeans_model = "multivariate" membuat uji
lanjut tetap sahih walaupun asumsi sferisitas dilanggar.
File Excel dicari otomatis di folder yang sama dengan file
.Rmd ini (nama boleh berawalan angka atau berakhiran
(2)). Jika ada beberapa file, dipilih yang memiliki sheet
Data_Long dan Data_Wide.
kandidat <- list.files(pattern = "Data_PMT_Balita.*\\.xlsx$", ignore.case = TRUE)
kandidat <- kandidat[!startsWith(kandidat, "~$")] # abaikan file sementara Excel
if (length(kandidat) == 0) {
stop("File Excel 'Data_PMT_Balita...xlsx' tidak ditemukan di folder: ", getwd(),
"\nLetakkan file Excel satu folder dengan file .Rmd ini.")
}
skor <- vapply(
kandidat,
function(f) {
s <- excel_sheets(f)
2 * ("Data_Long" %in% s) + 1 * ("Data_Wide" %in% s)
},
numeric(1)
)
if (max(skor) < 2) stop("Tidak ada file Excel dengan sheet 'Data_Long'.")
file_data <- kandidat[which.max(skor)]
message("Membaca file: ", file_data)
# LONG: satu baris = satu penimbangan
data_long <- as.data.frame(read_excel(file_data, sheet = "Data_Long"))
# WIDE: satu baris = satu balita (dari sheet Data_Wide bila ada, jika tidak dibentuk dari LONG)
if ("Data_Wide" %in% excel_sheets(file_data)) {
data_wide <- as.data.frame(read_excel(file_data, sheet = "Data_Wide"))
names(data_wide) <- sub("^BB_Bulan", "BB_B", names(data_wide))
} else {
data_wide <- data_long %>%
mutate(Waktu_lab = paste0("B", Waktu)) %>%
dplyr::select(ID, Kelompok, Usia_bulan, Jenis_Kelamin, Waktu_lab, BB_kg) %>%
pivot_wider(names_from = Waktu_lab, values_from = BB_kg, names_prefix = "BB_") %>%
as.data.frame()
}
head(data_long)
head(data_wide)
str(data_long)
## 'data.frame': 360 obs. of 6 variables:
## $ ID : chr "BSK-01" "BSK-01" "BSK-01" "BSK-01" ...
## $ Kelompok : chr "PMT Biskuit" "PMT Biskuit" "PMT Biskuit" "PMT Biskuit" ...
## $ Usia_bulan : num 24 24 24 24 19 19 19 19 39 39 ...
## $ Jenis_Kelamin: chr "Laki-laki" "Laki-laki" "Laki-laki" "Laki-laki" ...
## $ Waktu : num 0 1 2 3 0 1 2 3 0 1 ...
## $ BB_kg : num 10.2 10.5 10.7 11.1 9.1 9.1 9.5 9.9 15.6 16 ...
str(data_wide)
## 'data.frame': 90 obs. of 8 variables:
## $ ID : chr "KON-01" "KON-02" "KON-03" "KON-04" ...
## $ Kelompok : chr "Kontrol" "Kontrol" "Kontrol" "Kontrol" ...
## $ Usia_bulan : num 52 51 19 42 52 59 58 40 15 42 ...
## $ Jenis_Kelamin: chr "Laki-laki" "Perempuan" "Perempuan" "Perempuan" ...
## $ BB_B0 : num 15.4 14.3 9.6 12.7 15.3 15.7 17.4 12.6 8.3 15.4 ...
## $ BB_B1 : num 15.3 14.4 9.7 12.9 15.4 16 17.6 12.6 8.3 15.4 ...
## $ BB_B2 : num 15.6 14.5 9.8 13 15.5 16 17.8 12.8 8.6 15.8 ...
## $ BB_B3 : num 15.8 14.6 9.9 12.9 15.7 16.1 17.8 13 8.8 16.3 ...
stopifnot(
all(c("ID", "Kelompok", "Usia_bulan", "Jenis_Kelamin", "Waktu", "BB_kg") %in% names(data_long)),
all(c("ID", "Kelompok", "BB_B0", "BB_B1", "BB_B2", "BB_B3") %in% names(data_wide))
)
# Jumlah balita (harus 90) dan per kelompok (harus 30)
n_distinct(data_long$ID)
## [1] 90
n_distinct(data_wide$ID)
## [1] 90
data_wide %>% count(Kelompok)
# Jumlah penimbangan tiap balita (harus 4)
data_long %>% count(ID) %>% count(n, name = "jumlah_balita")
# Duplikasi ID x Waktu (harus 0 baris)
data_long %>% count(ID, Waktu) %>% filter(n != 1)
# Data hilang
colSums(is.na(data_long))
## ID Kelompok Usia_bulan Jenis_Kelamin Waktu
## 0 0 0 0 0
## BB_kg
## 0
colSums(is.na(data_wide))
## ID Kelompok Usia_bulan Jenis_Kelamin BB_B0
## 0 0 0 0 0
## BB_B1 BB_B2 BB_B3
## 0 0 0
# Ringkasan outcome
summary(data_long$BB_kg)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 7.80 10.80 12.80 12.88 14.72 18.30
Hasil. Data lengkap: 90 balita (30 per kelompok), masing-masing 4 penimbangan (360 baris pada format LONG), tanpa duplikasi dan tanpa data hilang. Berat badan berkisar 7,8–18,3 kg dengan rerata 12,88 kg. Struktur ini seimbang (balanced), sehingga memenuhi syarat untuk RM ANOVA.
Waktu_num tetap numerik (untuk grafik dan LMM),
sedangkan Waktu dijadikan faktor (untuk RM ANOVA).
kel_lab <- c("Kontrol", "PMT Biskuit", "PMT Lokal")
data_long <- data_long %>%
mutate(
ID = factor(ID),
Kelompok = factor(Kelompok, levels = kel_lab),
Jenis_Kelamin = factor(Jenis_Kelamin),
Waktu_num = as.numeric(Waktu),
Waktu = factor(Waktu, levels = c(0, 1, 2, 3), labels = c("B0", "B1", "B2", "B3"))
)
data_wide <- data_wide %>%
mutate(
ID = factor(ID),
Kelompok = factor(Kelompok, levels = kel_lab),
Jenis_Kelamin = factor(Jenis_Kelamin)
)
levels(data_long$Kelompok)
## [1] "Kontrol" "PMT Biskuit" "PMT Lokal"
levels(data_long$Waktu)
## [1] "B0" "B1" "B2" "B3"
wide_from_long <- data_long %>%
dplyr::select(ID, Kelompok, Waktu, BB_kg) %>%
pivot_wider(names_from = Waktu, values_from = BB_kg, names_prefix = "BB_")
nrow(wide_from_long)
## [1] 90
nrow(data_wide)
## [1] 90
kol_bb <- c("BB_B0", "BB_B1", "BB_B2", "BB_B3")
urut <- match(as.character(data_wide$ID), as.character(wide_from_long$ID))
stopifnot(!anyNA(urut))
# Hasil yang diharapkan: TRUE
all.equal(
as.matrix(wide_from_long[urut, kol_bb]),
as.matrix(data_wide[, kol_bb]),
check.attributes = FALSE
)
## [1] TRUE
Nilai pada format WIDE identik dengan format LONG
(TRUE), sehingga kedua format dapat dipakai bergantian.
karakteristik <- data_wide %>%
group_by(Kelompok) %>%
summarise(
n = n(),
usia_mean = mean(Usia_bulan), usia_sd = sd(Usia_bulan),
laki = sum(Jenis_Kelamin == "Laki-laki"),
perempuan = sum(Jenis_Kelamin == "Perempuan"),
bb0_mean = mean(BB_B0), bb0_sd = sd(BB_B0),
.groups = "drop"
)
knitr::kable(karakteristik, digits = 2,
caption = "Karakteristik awal balita menurut kelompok")
| Kelompok | n | usia_mean | usia_sd | laki | perempuan | bb0_mean | bb0_sd |
|---|---|---|---|---|---|---|---|
| Kontrol | 30 | 41.9 | 13.90 | 12 | 18 | 12.97 | 2.36 |
| PMT Biskuit | 30 | 34.5 | 13.59 | 19 | 11 | 12.14 | 2.39 |
| PMT Lokal | 30 | 35.9 | 14.28 | 19 | 11 | 12.32 | 2.47 |
chisq.test(table(data_wide$Kelompok, data_wide$Jenis_Kelamin)) # jenis kelamin
##
## Pearson's Chi-squared test
##
## data: table(data_wide$Kelompok, data_wide$Jenis_Kelamin)
## X-squared = 4.41, df = 2, p-value = 0.1103
summary(aov(Usia_bulan ~ Kelompok, data = data_wide)) # usia
## Df Sum Sq Mean Sq F value Pr(>F)
## Kelompok 2 927 463.6 2.39 0.0976 .
## Residuals 87 16875 194.0
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(aov(BB_B0 ~ Kelompok, data = data_wide)) # BB awal
## Df Sum Sq Mean Sq F value Pr(>F)
## Kelompok 2 11.6 5.802 1 0.372
## Residuals 87 504.6 5.800
Interpretasi. Ketiga kelompok relatif setara pada awal pengamatan: jenis kelamin (χ² = 4,41; p = 0,110), usia (F = 2,39; p = 0,098), dan berat badan awal (F = 1,00; p = 0,372), semuanya p > 0,05. Usia rata-rata kelompok Kontrol (41,9 bulan) agak lebih tinggi dibandingkan PMT Biskuit (34,5) dan PMT Lokal (35,9). Karena p usia mendekati batas 0,05, usia dan jenis kelamin tetap dikontrol pada ANCOVA dan LMM (bagian 7 dan 8).
deskriptif <- data_long %>%
group_by(Kelompok, Waktu) %>%
summarise(
n = n(),
mean = mean(BB_kg, na.rm = TRUE),
sd = sd(BB_kg, na.rm = TRUE),
median = median(BB_kg, na.rm = TRUE),
min = min(BB_kg, na.rm = TRUE),
max = max(BB_kg, na.rm = TRUE),
.groups = "drop"
)
knitr::kable(deskriptif, digits = 2,
caption = "Berat badan (kg) per kelompok dan waktu")
| Kelompok | Waktu | n | mean | sd | median | min | max |
|---|---|---|---|---|---|---|---|
| Kontrol | B0 | 30 | 12.97 | 2.36 | 13.00 | 8.3 | 17.4 |
| Kontrol | B1 | 30 | 13.09 | 2.37 | 13.20 | 8.3 | 17.6 |
| Kontrol | B2 | 30 | 13.21 | 2.40 | 13.25 | 8.6 | 17.8 |
| Kontrol | B3 | 30 | 13.31 | 2.42 | 13.45 | 8.8 | 17.8 |
| PMT Biskuit | B0 | 30 | 12.14 | 2.39 | 12.05 | 7.8 | 16.0 |
| PMT Biskuit | B1 | 30 | 12.45 | 2.46 | 12.40 | 7.9 | 16.7 |
| PMT Biskuit | B2 | 30 | 12.73 | 2.47 | 12.60 | 8.3 | 17.1 |
| PMT Biskuit | B3 | 30 | 13.08 | 2.47 | 13.00 | 8.6 | 17.4 |
| PMT Lokal | B0 | 30 | 12.32 | 2.47 | 12.20 | 8.3 | 17.3 |
| PMT Lokal | B1 | 30 | 12.68 | 2.43 | 12.55 | 8.7 | 17.5 |
| PMT Lokal | B2 | 30 | 13.06 | 2.44 | 12.70 | 9.2 | 17.9 |
| PMT Lokal | B3 | 30 | 13.45 | 2.44 | 13.05 | 9.6 | 18.3 |
Rerata berat badan pada bulan 0 sampai bulan 3: Kontrol 12,97 → 13,31 kg; PMT Biskuit 12,14 → 13,08 kg; PMT Lokal 12,32 → 13,45 kg. Simpangan baku sekitar 2,4 kg pada semua sel, yang mencerminkan perbedaan usia dan ukuran tubuh antarbalita.
vars_waktu <- c("BB_B0", "BB_B1", "BB_B2", "BB_B3")
S <- cov(data_wide[, vars_waktu], use = "complete.obs")
R <- cor(data_wide[, vars_waktu], use = "complete.obs")
round(S, 2)
## BB_B0 BB_B1 BB_B2 BB_B3
## BB_B0 5.80 5.79 5.79 5.75
## BB_B1 5.79 5.80 5.81 5.78
## BB_B2 5.79 5.81 5.86 5.84
## BB_B3 5.75 5.78 5.84 5.85
round(R, 3)
## BB_B0 BB_B1 BB_B2 BB_B3
## BB_B0 1.000 0.998 0.993 0.986
## BB_B1 0.998 1.000 0.998 0.993
## BB_B2 0.993 0.998 1.000 0.997
## BB_B3 0.986 0.993 0.997 1.000
pasangan <- combn(vars_waktu, 2)
var_selisih <- apply(
pasangan, 2,
function(p) var(data_wide[[p[1]]] - data_wide[[p[2]]], na.rm = TRUE)
)
names(var_selisih) <- apply(pasangan, 2, paste, collapse = " - ")
round(var_selisih, 3)
## BB_B0 - BB_B1 BB_B0 - BB_B2 BB_B0 - BB_B3 BB_B1 - BB_B2 BB_B1 - BB_B3
## 0.027 0.078 0.158 0.028 0.087
## BB_B2 - BB_B3
## 0.031
Interpretasi. Berat badan antarwaktu berkorelasi sangat tinggi (r = 0,986–0,998), artinya balita yang berat sejak awal tetap berat pada bulan berikutnya. Varians selisih antarpasangan waktu tidak seragam: 0,027 (B0−B1) sampai 0,158 (B0−B3), dan membesar seiring jarak waktu. Ini petunjuk awal bahwa asumsi sferisitas dilanggar, dan akan dikonfirmasi oleh uji Mauchly pada bagian 5 dan 6.
p_profil <- ggplot(
data_long,
aes(x = Waktu_num, y = BB_kg, colour = Kelompok, group = Kelompok)
) +
stat_summary(fun = mean, geom = "line", linewidth = 1) +
stat_summary(fun = mean, geom = "point", size = 2.8) +
stat_summary(fun.data = mean_cl_normal, geom = "errorbar", width = .12) +
scale_x_continuous(breaks = c(0, 1, 2, 3)) +
labs(
x = "Bulan penimbangan", y = "Berat badan (kg)", colour = "Kelompok",
title = "Profil rerata berat badan balita selama 3 bulan"
) +
theme(legend.position = "bottom")
p_profil
Ketiga garis naik, tetapi kemiringannya berbeda: PMT Lokal paling curam, PMT Biskuit di tengah, dan Kontrol paling landai. Garis yang tidak sejajar ini menunjukkan adanya interaksi kelompok × waktu.
p_spag <- ggplot(data_long, aes(x = Waktu_num, y = BB_kg, group = ID)) +
geom_line(alpha = .25) +
stat_summary(
aes(group = Kelompok), fun = mean, geom = "line",
linewidth = 1.3, colour = "firebrick"
) +
facet_wrap(~ Kelompok) +
scale_x_continuous(breaks = c(0, 1, 2, 3)) +
labs(x = "Bulan", y = "Berat badan (kg)",
title = "Lintasan individu dan rerata kelompok")
p_spag
Lintasan individu hampir sejajar satu sama lain (perbedaan terutama pada level awal), dan hampir semua balita naik. Hal ini sejalan dengan ICC yang sangat tinggi pada bagian 8.
Pertanyaan: apakah berat badan balita kelompok PMT
Lokal berubah selama tiga bulan? (Ganti kelompok_fokus
dengan “Kontrol” atau “PMT Biskuit” untuk kelompok lain.)
kelompok_fokus <- "PMT Lokal"
d1 <- data_long %>% filter(Kelompok == kelompok_fokus) %>% droplevels()
d1w <- data_wide %>% filter(Kelompok == kelompok_fokus) %>% droplevels()
# (i) Outlier per waktu
outlier_1way <- d1 %>% group_by(Waktu) %>% identify_outliers(BB_kg)
outlier_1way
# (ii) Normalitas Shapiro-Wilk per waktu
shapiro_1way <- d1 %>% group_by(Waktu) %>% shapiro_test(BB_kg)
shapiro_1way
# Q-Q plot
ggpubr::ggqqplot(d1, "BB_kg", facet.by = "Waktu")
# (iii) Sferisitas (Mauchly) + koreksi GG/HF
aov1_rs <- anova_test(
data = d1, dv = BB_kg, 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 3 87 602.823 4.51e-58 * 0.954
##
## $`Mauchly's Test for Sphericity`
## Effect W p p<.05
## 1 Waktu 0.323 8.07e-06 *
##
## $`Sphericity Corrections`
## Effect GGe DF[GG] p[GG] p[GG]<.05 HFe DF[HF] p[HF]
## 1 Waktu 0.637 1.91, 55.4 7.14e-38 * 0.681 2.04, 59.21 2.6e-40
## p[HF]<.05
## 1 *
get_anova_table(aov1_rs, correction = "auto")
Interpretasi. Tidak ada outlier, dan data normal pada keempat waktu (Shapiro-Wilk p = 0,232–0,353). Uji Mauchly signifikan (W = 0,323; p < 0,001), sehingga sferisitas tidak terpenuhi dan dipakai koreksi Greenhouse-Geisser (ε = 0,637; Huynh-Feldt ε = 0,681).
aov1 <- aov_ez(
id = "ID", dv = "BB_kg", data = d1, within = "Waktu",
anova_table = list(es = c("ges", "pes"), correction = "GG")
)
aov1
## Anova Table (Type 3 tests)
##
## Response: BB_kg
## Effect df MSE F ges pes p.value
## 1 Waktu 1.91, 55.40 0.02 602.82 *** .030 .954 <.001
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '+' 0.1 ' ' 1
##
## Sphericity correction method: GG
summary(aov1)
##
## Univariate Type III Repeated-Measures ANOVA Assuming Sphericity
##
## Sum Sq num Df Error SS den Df F value Pr(>F)
## (Intercept) 19902.2 1 693.01 29 832.83 < 2.2e-16 ***
## Waktu 21.2 3 1.02 87 602.82 < 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.32253 8.0703e-06
##
##
## Greenhouse-Geisser and Huynh-Feldt Corrections
## for Departure from Sphericity
##
## GG eps Pr(>F[GG])
## Waktu 0.63682 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## HF eps Pr(>F[HF])
## Waktu 0.6806248 2.602525e-40
nice(aov1, correction = "GG", es = c("ges", "pes"))
# Ukuran efek tambahan
eta_squared(aov1, partial = TRUE)
omega_squared(aov1, partial = TRUE)
Interpretasi. Berat badan berubah bermakna selama tiga bulan pada kelompok PMT Lokal: F(1,91; 55,40) = 602,82; p < 0,001; η² parsial = 0,954 (efek sangat besar).
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.96635 832.83 1 29 < 2.2e-16 ***
## Waktu 1 0.97729 387.22 3 27 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Uji multivariat (Pillai) tidak membutuhkan sferisitas dan memberi kesimpulan yang sama: F(3, 27) = 387,22; p < 0,001.
em1 <- emmeans(aov1, ~ Waktu)
em1
## Waktu emmean SE df lower.CL upper.CL
## B0 12.3 0.452 29 11.4 13.2
## B1 12.7 0.443 29 11.8 13.6
## B2 13.1 0.446 29 12.2 14.0
## B3 13.4 0.445 29 12.5 14.4
##
## Confidence level used: 0.95
# Semua pasangan waktu
pairs(em1, adjust = "holm")
## contrast estimate SE df t.ratio p.value
## B0 - B1 -0.363 0.0200 29 -18.123 <0.0001
## B0 - B2 -0.743 0.0341 29 -21.777 <0.0001
## B0 - B3 -1.127 0.0346 29 -32.607 <0.0001
## B1 - B2 -0.380 0.0232 29 -16.384 <0.0001
## B1 - B3 -0.763 0.0305 29 -25.022 <0.0001
## B2 - B3 -0.383 0.0215 29 -17.840 <0.0001
##
## P value adjustment: holm method for 6 tests
# Tiap waktu vs baseline B0
contrast(em1, "trt.vs.ctrl", ref = 1, adjust = "holm")
## contrast estimate SE df t.ratio p.value
## B1 - B0 0.363 0.0200 29 18.123 <0.0001
## B2 - B0 0.743 0.0341 29 21.777 <0.0001
## B3 - B0 1.127 0.0346 29 32.607 <0.0001
##
## P value adjustment: holm method for 3 tests
# Tren linear, kuadratik, kubik
contrast(em1, "poly")
## contrast estimate SE df t.ratio p.value
## linear 3.7600 0.1220 29 30.721 <0.0001
## quadratic 0.0200 0.0350 29 0.571 0.5725
## cubic -0.0133 0.0484 29 -0.276 0.7847
Interpretasi. Semua pasangan waktu berbeda bermakna (Holm, p < 0,0001). Dibandingkan bulan 0, berat badan naik 0,36 kg (bulan 1), 0,74 kg (bulan 2), dan 1,13 kg (bulan 3). Polanya linear (kontras linear = 3,76; p < 0,0001), sedangkan komponen kuadratik (p = 0,573) dan kubik (p = 0,785) tidak signifikan, artinya kenaikan berat badan stabil sekitar 0,38 kg per bulan.
friedman_test(d1, BB_kg ~ Waktu | ID)
friedman_effsize(d1, BB_kg ~ Waktu | ID)
d1 %>% wilcox_test(BB_kg ~ Waktu, paired = TRUE, p.adjust.method = "holm")
Uji Friedman memberi kesimpulan yang sama: χ²(3) = 90,0; p < 0,001; Kendall’s W = 1,00 (kesepakatan sempurna: seluruh balita naik berurutan dari bulan ke bulan). Seluruh pasangan Wilcoxon juga signifikan setelah koreksi Holm.
Ini adalah uji utama untuk menjawab: apakah perubahan berat badan dari bulan 0 sampai bulan 3 berbeda antara Kontrol, PMT Biskuit, dan PMT Lokal?
# (i) Outlier per sel
outlier_mixed <- data_long %>% group_by(Kelompok, Waktu) %>% identify_outliers(BB_kg)
outlier_mixed
# (ii) Normalitas per Kelompok x Waktu
shapiro_mixed <- data_long %>% group_by(Kelompok, Waktu) %>% shapiro_test(BB_kg)
shapiro_mixed
ggpubr::ggqqplot(data_long, "BB_kg") + facet_grid(Waktu ~ Kelompok)
# (iii) Homogenitas varians pada tiap waktu: Levene
levene_mixed <- data_long %>% group_by(Waktu) %>% levene_test(BB_kg ~ Kelompok)
levene_mixed
# (iv) Homogenitas matriks kovarians: Box's M
box_m_result <- box_m(data_wide[, vars_waktu], data_wide$Kelompok)
box_m_result
| Asumsi | Hasil | Kesimpulan |
|---|---|---|
| Outlier | Tidak ada | Terpenuhi |
| Normalitas (12 sel) | Shapiro-Wilk p = 0,232–0,409 | Terpenuhi |
| Homogenitas varians (Levene) | p = 0,845; 0,946; 0,950; 0,962 | Terpenuhi |
| Homogenitas kovarians (Box’s M) | M = 34,3; p = 0,0245 | Terpenuhi (ambang uji Box’s M umumnya p < 0,001) |
| Sferisitas (Mauchly) | Lihat 6.2 | Dilanggar, pakai koreksi GG |
aov2 <- aov_ez(
id = "ID", dv = "BB_kg", data = data_long,
between = "Kelompok", within = "Waktu",
anova_table = list(es = c("ges", "pes"), correction = "GG")
)
aov2
## Anova Table (Type 3 tests)
##
## Response: BB_kg
## Effect df MSE F ges pes p.value
## 1 Kelompok 2, 87 23.53 0.38 .009 .009 .687
## 2 Waktu 1.98, 172.38 0.02 803.10 *** .015 .902 <.001
## 3 Kelompok:Waktu 3.96, 172.38 0.02 70.39 *** .003 .618 <.001
## ---
## 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) 59678 1 2046.98 87 2536.4165 <2e-16 ***
## Kelompok 18 2 2046.98 87 0.3776 0.6866
## Waktu 32 3 3.48 261 803.0987 <2e-16 ***
## Kelompok:Waktu 6 6 3.48 261 70.3855 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Mauchly Tests for Sphericity
##
## Test statistic p-value
## Waktu 0.42937 3.1623e-14
## Kelompok:Waktu 0.42937 3.1623e-14
##
##
## Greenhouse-Geisser and Huynh-Feldt Corrections
## for Departure from Sphericity
##
## GG eps Pr(>F[GG])
## Waktu 0.66046 < 2.2e-16 ***
## Kelompok:Waktu 0.66046 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## HF eps Pr(>F[HF])
## Waktu 0.6757837 9.223254e-90
## Kelompok:Waktu 0.6757837 8.423267e-36
nice(aov2, correction = "GG", es = c("ges", "pes"))
# Versi rstatix (hasil setara)
aov2_rs <- anova_test(
data = data_long, dv = BB_kg, wid = ID,
between = Kelompok, within = Waktu, effect.size = "pes", type = 3
)
aov2_rs
## ANOVA Table (type III tests)
##
## $ANOVA
## Effect DFn DFd F p p<.05 pes
## 1 Kelompok 2 87 0.378 6.87e-01 0.009
## 2 Waktu 3 261 803.099 1.97e-131 * 0.902
## 3 Kelompok:Waktu 6 261 70.386 9.55e-52 * 0.618
##
## $`Mauchly's Test for Sphericity`
## Effect W p p<.05
## 1 Waktu 0.429 3.16e-14 *
## 2 Kelompok:Waktu 0.429 3.16e-14 *
##
## $`Sphericity Corrections`
## Effect GGe DF[GG] p[GG] p[GG]<.05 HFe DF[HF]
## 1 Waktu 0.66 1.98, 172.38 8.60e-88 * 0.676 2.03, 176.38
## 2 Kelompok:Waktu 0.66 3.96, 172.38 4.78e-35 * 0.676 4.05, 176.38
## p[HF] p[HF]<.05
## 1 9.22e-90 *
## 2 8.42e-36 *
get_anova_table(aov2_rs, correction = "GG")
| Efek | F (df, GG) | p | η² parsial |
|---|---|---|---|
| Kelompok | 0,38 (2; 87) | 0,687 | 0,009 |
| Waktu | 803,10 (1,98; 172,38) | < 0,001 | 0,902 |
| Kelompok × Waktu | 70,39 (3,96; 172,38) | < 0,001 | 0,618 |
Interpretasi.
eta_squared(aov2, partial = TRUE)
omega_squared(aov2, partial = TRUE)
η² parsial untuk waktu (0,90) dan interaksi (0,62) termasuk besar. Perhatikan bahwa η² generalized (ges) jauh lebih kecil (waktu 0,015; interaksi 0,003) karena variasi berat badan antarbalita sangat besar (ICC 0,97). Jangan membandingkan angka partial dan generalized secara langsung, dan sebutkan jenis ukuran efek yang dilaporkan.
p_interaksi <- afex_plot(
aov2, x = "Waktu", trace = "Kelompok", error = "within",
mapping = c("colour", "shape", "linetype")
) +
labs(
y = "Berat badan (kg)", x = "Waktu",
title = "Interaksi Kelompok x Waktu pada berat badan balita"
) +
theme(legend.position = "bottom")
p_interaksi
Catatan: afex_plot memberi peringatan bahwa
error bar pada desain campuran tidak dapat dipakai untuk
membandingkan seluruh rerata. Gunakan error bar hanya sebagai
gambaran dan andalkan uji lanjut untuk inferensi.
# Rerata marginal waktu dalam tiap kelompok
em2 <- emmeans(aov2, ~ Waktu | Kelompok)
em2
## Kelompok = Kontrol:
## Waktu emmean SE df lower.CL upper.CL
## B0 13.0 0.440 87 12.1 13.8
## B1 13.1 0.442 87 12.2 14.0
## B2 13.2 0.445 87 12.3 14.1
## B3 13.3 0.446 87 12.4 14.2
##
## Kelompok = PMT Biskuit:
## Waktu emmean SE df lower.CL upper.CL
## B0 12.1 0.440 87 11.3 13.0
## B1 12.5 0.442 87 11.6 13.3
## B2 12.7 0.445 87 11.8 13.6
## B3 13.1 0.446 87 12.2 14.0
##
## Kelompok = PMT Lokal:
## Waktu emmean SE df lower.CL upper.CL
## B0 12.3 0.440 87 11.4 13.2
## B1 12.7 0.442 87 11.8 13.6
## B2 13.1 0.445 87 12.2 13.9
## B3 13.4 0.446 87 12.6 14.3
##
## Confidence level used: 0.95
# Efek waktu di dalam masing-masing kelompok
joint_tests(aov2, by = "Kelompok")
# Efek kelompok pada masing-masing waktu
joint_tests(aov2, by = "Waktu")
# Semua pasangan waktu dalam tiap kelompok
pairs(em2, adjust = "holm")
## Kelompok = Kontrol:
## contrast estimate SE df t.ratio p.value
## B0 - B1 -0.117 0.0233 87 -5.014 <0.0001
## B0 - B2 -0.233 0.0330 87 -7.061 <0.0001
## B0 - B3 -0.340 0.0385 87 -8.835 <0.0001
## B1 - B2 -0.117 0.0236 87 -4.937 <0.0001
## B1 - B3 -0.223 0.0338 87 -6.602 <0.0001
## B2 - B3 -0.107 0.0228 87 -4.681 <0.0001
##
## Kelompok = PMT Biskuit:
## contrast estimate SE df t.ratio p.value
## B0 - B1 -0.317 0.0233 87 -13.610 <0.0001
## B0 - B2 -0.597 0.0330 87 -18.056 <0.0001
## B0 - B3 -0.947 0.0385 87 -24.599 <0.0001
## B1 - B2 -0.280 0.0236 87 -11.848 <0.0001
## B1 - B3 -0.630 0.0338 87 -18.625 <0.0001
## B2 - B3 -0.350 0.0228 87 -15.359 <0.0001
##
## Kelompok = PMT Lokal:
## contrast estimate SE df t.ratio p.value
## B0 - B1 -0.363 0.0233 87 -15.615 <0.0001
## B0 - B2 -0.743 0.0330 87 -22.495 <0.0001
## B0 - B3 -1.127 0.0385 87 -29.277 <0.0001
## B1 - B2 -0.380 0.0236 87 -16.080 <0.0001
## B1 - B3 -0.763 0.0338 87 -22.567 <0.0001
## B2 - B3 -0.383 0.0228 87 -16.822 <0.0001
##
## P value adjustment: holm method for 6 tests
# Tiap waktu vs baseline B0
contrast(em2, "trt.vs.ctrl", ref = 1, adjust = "holm")
## Kelompok = Kontrol:
## contrast estimate SE df t.ratio p.value
## B1 - B0 0.117 0.0233 87 5.014 <0.0001
## B2 - B0 0.233 0.0330 87 7.061 <0.0001
## B3 - B0 0.340 0.0385 87 8.835 <0.0001
##
## Kelompok = PMT Biskuit:
## contrast estimate SE df t.ratio p.value
## B1 - B0 0.317 0.0233 87 13.610 <0.0001
## B2 - B0 0.597 0.0330 87 18.056 <0.0001
## B3 - B0 0.947 0.0385 87 24.599 <0.0001
##
## Kelompok = PMT Lokal:
## contrast estimate SE df t.ratio p.value
## B1 - B0 0.363 0.0233 87 15.615 <0.0001
## B2 - B0 0.743 0.0330 87 22.495 <0.0001
## B3 - B0 1.127 0.0385 87 29.277 <0.0001
##
## P value adjustment: holm method for 3 tests
Pada ketiga kelompok, setiap pasangan waktu berbeda bermakna (Holm, p < 0,0001). Kenaikan bulan 3 dibandingkan bulan 0: Kontrol 0,34 kg, PMT Biskuit 0,95 kg, PMT Lokal 1,13 kg.
em2b <- emmeans(aov2, ~ Kelompok | Waktu)
em2b
## Waktu = B0:
## Kelompok emmean SE df lower.CL upper.CL
## Kontrol 13.0 0.440 87 12.1 13.8
## PMT Biskuit 12.1 0.440 87 11.3 13.0
## PMT Lokal 12.3 0.440 87 11.4 13.2
##
## Waktu = B1:
## Kelompok emmean SE df lower.CL upper.CL
## Kontrol 13.1 0.442 87 12.2 14.0
## PMT Biskuit 12.5 0.442 87 11.6 13.3
## PMT Lokal 12.7 0.442 87 11.8 13.6
##
## Waktu = B2:
## Kelompok emmean SE df lower.CL upper.CL
## Kontrol 13.2 0.445 87 12.3 14.1
## PMT Biskuit 12.7 0.445 87 11.8 13.6
## PMT Lokal 13.1 0.445 87 12.2 13.9
##
## Waktu = B3:
## Kelompok emmean SE df lower.CL upper.CL
## Kontrol 13.3 0.446 87 12.4 14.2
## PMT Biskuit 13.1 0.446 87 12.2 14.0
## PMT Lokal 13.4 0.446 87 12.6 14.3
##
## Confidence level used: 0.95
pairs(em2b, adjust = "tukey")
## Waktu = B0:
## contrast estimate SE df t.ratio p.value
## Kontrol - PMT Biskuit 0.837 0.622 87 1.345 0.3741
## Kontrol - PMT Lokal 0.653 0.622 87 1.051 0.5472
## PMT Biskuit - PMT Lokal -0.183 0.622 87 -0.295 0.9532
##
## Waktu = B1:
## contrast estimate SE df t.ratio p.value
## Kontrol - PMT Biskuit 0.637 0.625 87 1.019 0.5672
## Kontrol - PMT Lokal 0.407 0.625 87 0.651 0.7925
## PMT Biskuit - PMT Lokal -0.230 0.625 87 -0.368 0.9281
##
## Waktu = B2:
## contrast estimate SE df t.ratio p.value
## Kontrol - PMT Biskuit 0.473 0.630 87 0.752 0.7335
## Kontrol - PMT Lokal 0.143 0.630 87 0.228 0.9719
## PMT Biskuit - PMT Lokal -0.330 0.630 87 -0.524 0.8598
##
## Waktu = B3:
## contrast estimate SE df t.ratio p.value
## Kontrol - PMT Biskuit 0.230 0.630 87 0.365 0.9293
## Kontrol - PMT Lokal -0.133 0.630 87 -0.212 0.9756
## PMT Biskuit - PMT Lokal -0.363 0.630 87 -0.576 0.8330
##
## P value adjustment: tukey method for comparing a family of 3 estimates
pairs(em2b, adjust = "holm")
## Waktu = B0:
## contrast estimate SE df t.ratio p.value
## Kontrol - PMT Biskuit 0.837 0.622 87 1.345 0.5459
## Kontrol - PMT Lokal 0.653 0.622 87 1.051 0.5927
## PMT Biskuit - PMT Lokal -0.183 0.622 87 -0.295 0.7688
##
## Waktu = B1:
## contrast estimate SE df t.ratio p.value
## Kontrol - PMT Biskuit 0.637 0.625 87 1.019 0.9336
## Kontrol - PMT Lokal 0.407 0.625 87 0.651 1.0000
## PMT Biskuit - PMT Lokal -0.230 0.625 87 -0.368 1.0000
##
## Waktu = B2:
## contrast estimate SE df t.ratio p.value
## Kontrol - PMT Biskuit 0.473 0.630 87 0.752 1.0000
## Kontrol - PMT Lokal 0.143 0.630 87 0.228 1.0000
## PMT Biskuit - PMT Lokal -0.330 0.630 87 -0.524 1.0000
##
## Waktu = B3:
## contrast estimate SE df t.ratio p.value
## Kontrol - PMT Biskuit 0.230 0.630 87 0.365 1.0000
## Kontrol - PMT Lokal -0.133 0.630 87 -0.212 1.0000
## PMT Biskuit - PMT Lokal -0.363 0.630 87 -0.576 1.0000
##
## P value adjustment: holm method for 3 tests
Tidak ada perbedaan bermakna antarkelompok pada B0, B1, B2, maupun B3 (Tukey p = 0,374–0,976). Hasil ini konsisten dengan efek kelompok yang tidak signifikan pada 6.5.
Inilah jawaban langsung atas pertanyaan penelitian: apakah besarnya kenaikan berat badan berbeda antarkelompok?
em_full <- emmeans(aov2, ~ Waktu * Kelompok)
em_full
## Waktu Kelompok emmean SE df lower.CL upper.CL
## B0 Kontrol 13.0 0.440 87 12.1 13.8
## B1 Kontrol 13.1 0.442 87 12.2 14.0
## B2 Kontrol 13.2 0.445 87 12.3 14.1
## B3 Kontrol 13.3 0.446 87 12.4 14.2
## B0 PMT Biskuit 12.1 0.440 87 11.3 13.0
## B1 PMT Biskuit 12.5 0.442 87 11.6 13.3
## B2 PMT Biskuit 12.7 0.445 87 11.8 13.6
## B3 PMT Biskuit 13.1 0.446 87 12.2 14.0
## B0 PMT Lokal 12.3 0.440 87 11.4 13.2
## B1 PMT Lokal 12.7 0.442 87 11.8 13.6
## B2 PMT Lokal 13.1 0.445 87 12.2 13.9
## B3 PMT Lokal 13.4 0.446 87 12.6 14.3
##
## Confidence level used: 0.95
# Pastikan urutan grid: B0-B3 untuk Kontrol, lalu PMT Biskuit, lalu PMT Lokal
stopifnot(
identical(as.character(em_full@grid$Waktu), rep(c("B0", "B1", "B2", "B3"), 3)),
identical(as.character(em_full@grid$Kelompok), rep(kel_lab, each = 4))
)
kc <- c(-1, 0, 0, 1) # kontras B3 - B0
z <- rep(0, 4)
# (i) Kenaikan BB (B3 - B0) di dalam tiap kelompok
contrast(
em_full,
list(
"Kontrol: B3-B0" = c(kc, z, z),
"PMT Biskuit: B3-B0" = c(z, kc, z),
"PMT Lokal: B3-B0" = c(z, z, kc)
),
adjust = "none"
)
## contrast estimate SE df t.ratio p.value
## Kontrol: B3-B0 0.340 0.0385 87 8.835 <0.0001
## PMT Biskuit: B3-B0 0.947 0.0385 87 24.599 <0.0001
## PMT Lokal: B3-B0 1.127 0.0385 87 29.277 <0.0001
# (ii) Perbedaan kenaikan antarkelompok
contrast(
em_full,
list(
"Biskuit - Kontrol" = c(-kc, kc, z),
"Lokal - Kontrol" = c(-kc, z, kc),
"Lokal - Biskuit" = c(z, -kc, kc)
),
adjust = "holm"
)
## contrast estimate SE df t.ratio p.value
## Biskuit - Kontrol 0.607 0.0544 87 11.147 <0.0001
## Lokal - Kontrol 0.787 0.0544 87 14.454 <0.0001
## Lokal - Biskuit 0.180 0.0544 87 3.307 0.0014
##
## P value adjustment: holm method for 3 tests
| Perbandingan kenaikan BB (B3 − B0) | Selisih (kg) | p (Holm) |
|---|---|---|
| PMT Biskuit − Kontrol | 0,607 | < 0,0001 |
| PMT Lokal − Kontrol | 0,787 | < 0,0001 |
| PMT Lokal − PMT Biskuit | 0,180 | 0,0014 |
Interpretasi. Balita yang mendapat PMT Biskuit naik 0,61 kg lebih banyak daripada Kontrol, dan PMT Lokal naik 0,79 kg lebih banyak daripada Kontrol. PMT Lokal juga lebih unggul 0,18 kg dibandingkan PMT Biskuit. Ketiga perbedaan signifikan setelah koreksi Holm.
tren_int <- summary(
contrast(em_full, interaction = c("poly", "pairwise"), adjust = "none")
)
tren_int
names(tren_int)
## [1] "Waktu_poly" "Kelompok_pairwise" "estimate"
## [4] "SE" "df" "t.ratio"
## [7] "p.value"
# Nama kolom dapat berbeda menurut versi emmeans
if ("Waktu_poly" %in% names(tren_int)) {
tren_lin <- subset(tren_int, Waktu_poly == "linear")
} else if ("Waktu.poly" %in% names(tren_int)) {
tren_lin <- subset(tren_int, Waktu.poly == "linear")
} else {
tren_lin <- tren_int
}
if ("p.value" %in% names(tren_lin)) {
tren_lin$p_holm <- p.adjust(tren_lin$p.value, method = "holm")
}
tren_lin
Laju kenaikan linear berbeda bermakna pada ketiga pasangan kelompok (p Holm: Kontrol vs Biskuit < 0,0001; Kontrol vs Lokal < 0,0001; Biskuit vs Lokal = 0,0009). Komponen kuadratik dan kubik tidak signifikan, jadi perbedaan antarkelompok terutama pada kemiringan (kg per bulan), bukan pada bentuk kurva.
Analisis ini menyederhanakan pertanyaan menjadi satu angka per balita (selisih B3 − B0), sehingga hasilnya mudah dibaca dan dilaporkan.
data_wide <- data_wide %>% mutate(Selisih = BB_B3 - BB_B0)
data_wide %>% group_by(Kelompok) %>% get_summary_stats(Selisih, type = "mean_sd")
# Asumsi
data_wide %>% group_by(Kelompok) %>% shapiro_test(Selisih)
levene_test(data_wide, Selisih ~ Kelompok)
# ANOVA satu arah + Tukey
aov_sel <- aov(Selisih ~ Kelompok, data = data_wide)
summary(aov_sel)
## Df Sum Sq Mean Sq F value Pr(>F)
## Kelompok 2 10.193 5.096 114.7 <2e-16 ***
## Residuals 87 3.865 0.044
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
TukeyHSD(aov_sel)
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = Selisih ~ Kelompok, data = data_wide)
##
## $Kelompok
## diff lwr upr p adj
## PMT Biskuit-Kontrol 0.6066667 0.47689442 0.7364389 0.0000000
## PMT Lokal-Kontrol 0.7866667 0.65689442 0.9164389 0.0000000
## PMT Lokal-PMT Biskuit 0.1800000 0.05022775 0.3097722 0.0038808
# Jika asumsi tidak terpenuhi:
# kruskal.test(Selisih ~ Kelompok, data = data_wide)
# Paired t-test B0 vs B3 pada tiap kelompok
data_wide %>%
pivot_longer(c(BB_B0, BB_B3), names_to = "waktu_uji", values_to = "bb_uji") %>%
group_by(Kelompok) %>%
t_test(bb_uji ~ waktu_uji, paired = TRUE)
Interpretasi.
ancova <- lm(BB_B3 ~ BB_B0 + Usia_bulan + Jenis_Kelamin + Kelompok, data = data_wide)
car::Anova(ancova, type = 3)
emmeans(ancova, pairwise ~ Kelompok, adjust = "tukey")
## $emmeans
## Kelompok emmean SE df lower.CL upper.CL
## Kontrol 12.8 0.0393 84 12.7 12.9
## PMT Biskuit 13.4 0.0389 84 13.3 13.5
## PMT Lokal 13.6 0.0387 84 13.5 13.7
##
## Results are averaged over the levels of: Jenis_Kelamin
## Confidence level used: 0.95
##
## $contrasts
## contrast estimate SE df t.ratio p.value
## Kontrol - PMT Biskuit -0.592 0.0565 84 -10.471 <0.0001
## Kontrol - PMT Lokal -0.774 0.0560 84 -13.811 <0.0001
## PMT Biskuit - PMT Lokal -0.182 0.0540 84 -3.364 0.0033
##
## Results are averaged over the levels of: Jenis_Kelamin
## P value adjustment: tukey method for comparing a family of 3 estimates
Setelah berat badan awal, usia, dan jenis kelamin dikontrol, kelompok tetap berpengaruh terhadap berat badan bulan 3 (F(2, 84) = 101,77; p < 0,001). Rerata terkoreksi: Kontrol 12,8 kg; PMT Biskuit 13,4 kg; PMT Lokal 13,6 kg, dan semua pasangan berbeda (Tukey p ≤ 0,0033). Berat badan awal sangat berpengaruh (p < 0,001), usia mendekati signifikan (p = 0,065), dan jenis kelamin tidak berpengaruh (p = 0,955).
LMM tidak mensyaratkan sferisitas, dapat menampung data hilang, dan memungkinkan usia serta jenis kelamin dimasukkan sebagai kovariat.
# Random intercept
lmm1 <- lmer(BB_kg ~ Kelompok * Waktu + (1 | ID), data = data_long, REML = TRUE)
summary(lmm1)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: BB_kg ~ Kelompok * Waktu + (1 | ID)
## Data: data_long
##
## REML criterion at convergence: 193.5
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -2.8798 -0.5037 0.0005 0.5458 3.5407
##
## Random effects:
## Groups Name Variance Std.Dev.
## ID (Intercept) 5.87880 2.4246
## Residual 0.01334 0.1155
## Number of obs: 360, groups: ID, 90
##
## Fixed effects:
## Estimate Std. Error df t value Pr(>|t|)
## (Intercept) 12.875278 0.255650 86.999999 50.363 < 2e-16 ***
## Kelompok1 0.270556 0.361544 87.000014 0.748 0.456
## Kelompok2 -0.273611 0.361544 87.000014 -0.757 0.451
## Waktu1 -0.398611 0.010544 261.000000 -37.805 < 2e-16 ***
## Waktu2 -0.133056 0.010544 261.000000 -12.619 < 2e-16 ***
## Waktu3 0.125833 0.010544 261.000000 11.934 < 2e-16 ***
## Kelompok1:Waktu1 0.226111 0.014911 261.000000 15.164 < 2e-16 ***
## Kelompok2:Waktu1 -0.066389 0.014911 261.000000 -4.452 1.26e-05 ***
## Kelompok1:Waktu2 0.077222 0.014911 261.000000 5.179 4.47e-07 ***
## Kelompok2:Waktu2 -0.015278 0.014911 261.000000 -1.025 0.307
## Kelompok1:Waktu3 -0.065000 0.014911 261.000000 -4.359 1.88e-05 ***
## Kelompok2:Waktu3 0.005833 0.014911 261.000000 0.391 0.696
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of Fixed Effects:
## (Intr) Klmpk1 Klmpk2 Waktu1 Waktu2 Waktu3 Kl1:W1 Kl2:W1 Kl1:W2
## Kelompok1 0.000
## Kelompok2 0.000 -0.500
## Waktu1 0.000 0.000 0.000
## Waktu2 0.000 0.000 0.000 -0.333
## Waktu3 0.000 0.000 0.000 -0.333 -0.333
## Klmpk1:Wkt1 0.000 0.000 0.000 0.000 0.000 0.000
## Klmpk2:Wkt1 0.000 0.000 0.000 0.000 0.000 0.000 -0.500
## Klmpk1:Wkt2 0.000 0.000 0.000 0.000 0.000 0.000 -0.333 0.167
## Klmpk2:Wkt2 0.000 0.000 0.000 0.000 0.000 0.000 0.167 -0.333 -0.500
## Klmpk1:Wkt3 0.000 0.000 0.000 0.000 0.000 0.000 -0.333 0.167 -0.333
## Klmpk2:Wkt3 0.000 0.000 0.000 0.000 0.000 0.000 0.167 -0.333 0.167
## Kl2:W2 Kl1:W3
## Kelompok1
## Kelompok2
## Waktu1
## Waktu2
## Waktu3
## Klmpk1:Wkt1
## Klmpk2:Wkt1
## Klmpk1:Wkt2
## Klmpk2:Wkt2
## Klmpk1:Wkt3 0.167
## Klmpk2:Wkt3 -0.333 -0.500
# Random intercept + random slope
lmm2 <- lmer(
BB_kg ~ Kelompok * Waktu + (1 + Waktu_num | ID),
data = data_long, REML = TRUE,
control = lmerControl(optimizer = "bobyqa")
)
summary(lmm2)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: BB_kg ~ Kelompok * Waktu + (1 + Waktu_num | ID)
## Data: data_long
## Control: lmerControl(optimizer = "bobyqa")
##
## REML criterion at convergence: 136.2
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -2.34694 -0.45022 -0.01438 0.48019 2.26498
##
## Random effects:
## Groups Name Variance Std.Dev. Corr
## ID (Intercept) 5.803933 2.40914
## Waktu_num 0.003834 0.06192 0.15
## Residual 0.006951 0.08337
## Number of obs: 360, groups: ID, 90
##
## Fixed effects:
## Estimate Std. Error df t value Pr(>|t|)
## (Intercept) 12.875278 0.255652 86.998052 50.362 < 2e-16 ***
## Kelompok1 0.270556 0.361547 86.998151 0.748 0.456281
## Kelompok2 -0.273611 0.361547 86.998151 -0.757 0.451227
## Waktu1 -0.398611 0.012400 118.737351 -32.145 < 2e-16 ***
## Waktu2 -0.133056 0.008281 244.689332 -16.068 < 2e-16 ***
## Waktu3 0.125833 0.008281 244.689332 15.196 < 2e-16 ***
## Kelompok1:Waktu1 0.226111 0.017537 118.737353 12.893 < 2e-16 ***
## Kelompok2:Waktu1 -0.066389 0.017537 118.737353 -3.786 0.000242 ***
## Kelompok1:Waktu2 0.077222 0.011711 244.689332 6.594 2.62e-10 ***
## Kelompok2:Waktu2 -0.015278 0.011711 244.689332 -1.305 0.193262
## Kelompok1:Waktu3 -0.065000 0.011711 244.689332 -5.550 7.40e-08 ***
## Kelompok2:Waktu3 0.005833 0.011711 244.689332 0.498 0.618853
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of Fixed Effects:
## (Intr) Klmpk1 Klmpk2 Waktu1 Waktu2 Waktu3 Kl1:W1 Kl2:W1 Kl1:W2
## Kelompok1 0.000
## Kelompok2 0.000 -0.500
## Waktu1 -0.149 0.000 0.000
## Waktu2 -0.075 0.000 0.000 0.123
## Waktu3 0.075 0.000 0.000 -0.499 -0.437
## Klmpk1:Wkt1 0.000 -0.149 0.075 0.000 0.000 0.000
## Klmpk2:Wkt1 0.000 0.075 -0.149 0.000 0.000 0.000 -0.500
## Klmpk1:Wkt2 0.000 -0.075 0.037 0.000 0.000 0.000 0.123 -0.062
## Klmpk2:Wkt2 0.000 0.037 -0.075 0.000 0.000 0.000 -0.062 0.123 -0.500
## Klmpk1:Wkt3 0.000 0.075 -0.037 0.000 0.000 0.000 -0.499 0.250 -0.437
## Klmpk2:Wkt3 0.000 -0.037 0.075 0.000 0.000 0.000 0.250 -0.499 0.218
## Kl2:W2 Kl1:W3
## Kelompok1
## Kelompok2
## Waktu1
## Waktu2
## Waktu3
## Klmpk1:Wkt1
## Klmpk2:Wkt1
## Klmpk1:Wkt2
## Klmpk2:Wkt2
## Klmpk1:Wkt3 0.218
## Klmpk2:Wkt3 -0.437 -0.500
# Bandingkan struktur random effect
anova(lmm1, lmm2, refit = FALSE)
# Uji efek tetap
anova(lmm2, ddf = "Kenward-Roger")
# ICC
performance::icc(lmm1)
Interpretasi.
lmm3 <- lmer(
BB_kg ~ Kelompok * Waktu_num + Usia_bulan + Jenis_Kelamin + (1 | ID),
data = data_long, REML = TRUE
)
anova(lmm3, ddf = "Kenward-Roger")
summary(lmm3)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula: BB_kg ~ Kelompok * Waktu_num + Usia_bulan + Jenis_Kelamin + (1 |
## ID)
## Data: data_long
##
## REML criterion at convergence: 1
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -2.8760 -0.5403 -0.0130 0.5250 3.6046
##
## Random effects:
## Groups Name Variance Std.Dev.
## ID (Intercept) 0.88462 0.9405
## Residual 0.01315 0.1147
## Number of obs: 360, groups: ID, 90
##
## Fixed effects:
## Estimate Std. Error df t value Pr(>|t|)
## (Intercept) 6.429612 0.290550 85.132562 22.129 < 2e-16 ***
## Kelompok1 -0.175643 0.147694 86.033282 -1.189 0.238
## Kelompok2 0.110239 0.143289 86.098344 0.769 0.444
## Waktu_num 0.267222 0.005406 267.000000 49.430 < 2e-16 ***
## Usia_bulan 0.161073 0.007267 85.000002 22.166 < 2e-16 ***
## Jenis_Kelamin1 0.137981 0.102668 85.000000 1.344 0.183
## Kelompok1:Waktu_num -0.153556 0.007645 267.000000 -20.085 < 2e-16 ***
## Kelompok2:Waktu_num 0.044778 0.007645 267.000000 5.857 1.38e-08 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of Fixed Effects:
## (Intr) Klmpk1 Klmpk2 Wkt_nm Us_bln Jns_K1 Kl1:W_
## Kelompok1 0.186
## Kelompok2 -0.129 -0.523
## Waktu_num -0.028 0.000 0.000
## Usia_bulan -0.939 -0.207 0.142 0.000
## Jenis_Klmn1 -0.095 0.203 -0.103 0.000 0.059
## Klmpk1:Wkt_ 0.000 -0.078 0.040 0.000 0.000 0.000
## Klmpk2:Wkt_ 0.000 0.039 -0.080 0.000 0.000 0.000 -0.500
# Laju kenaikan BB per bulan (kg/bulan) tiap kelompok dan perbandingannya
emtrends(lmm3, pairwise ~ Kelompok, var = "Waktu_num", adjust = "tukey")
## $emtrends
## Kelompok Waktu_num.trend SE df lower.CL upper.CL
## Kontrol 0.114 0.00936 267 0.0952 0.132
## PMT Biskuit 0.312 0.00936 267 0.2936 0.330
## PMT Lokal 0.376 0.00936 267 0.3576 0.394
##
## Results are averaged over the levels of: Jenis_Kelamin
## Degrees-of-freedom method: kenward-roger
## Confidence level used: 0.95
##
## $contrasts
## contrast estimate SE df t.ratio p.value
## Kontrol - PMT Biskuit -0.198 0.0132 267 -14.977 <0.0001
## Kontrol - PMT Lokal -0.262 0.0132 267 -19.810 <0.0001
## PMT Biskuit - PMT Lokal -0.064 0.0132 267 -4.833 <0.0001
##
## Results are averaged over the levels of: Jenis_Kelamin
## Degrees-of-freedom method: kenward-roger
## P value adjustment: tukey method for comparing a family of 3 estimates
Interpretasi.
par(mfrow = c(1, 3))
qqnorm(resid(lmm2), main = "Q-Q residual")
qqline(resid(lmm2))
qqnorm(ranef(lmm2)$ID[, 1], main = "Q-Q random intercept")
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))
shapiro.test(resid(lmm2))
##
## Shapiro-Wilk normality test
##
## data: resid(lmm2)
## W = 0.9941, p-value = 0.177
Residual berdistribusi normal (Shapiro-Wilk W = 0,994; p = 0,177) dan titik-titik pada Q-Q plot mengikuti garis lurus, sehingga asumsi LMM terpenuhi.
Bagian ini tidak mengubah data utama. Sebanyak 30 nilai (di luar bulan 0) dihapus secara acak untuk menunjukkan bahwa LMM tetap dapat dipakai.
set.seed(2026)
data_miss <- data_long
idx_miss <- sample(which(data_miss$Waktu != "B0"), 30, replace = FALSE)
data_miss$BB_kg[idx_miss] <- NA
lmm_miss <- lmer(
BB_kg ~ Kelompok * Waktu + (1 + Waktu_num | ID),
data = data_miss, REML = TRUE, na.action = na.exclude,
control = lmerControl(optimizer = "bobyqa")
)
anova(lmm_miss, ddf = "Kenward-Roger")
# Jumlah balita yang kehilangan >= 1 nilai (akan dibuang seluruhnya oleh RM ANOVA)
n_distinct(data_miss$ID[is.na(data_miss$BB_kg)])
## [1] 28
Dengan 30 nilai hilang, 28 dari 90 balita (31%) kehilangan sedikitnya satu penimbangan. RM ANOVA akan membuang seluruh 28 balita tersebut, sedangkan LMM tetap memakai semua data yang ada. Hasil LMM tetap konsisten dengan analisis lengkap (interaksi F(6; 185,2) = 32,43; p < 0,001).
Analisis mixed ANOVA menunjukkan interaksi kelompok × waktu yang signifikan setelah koreksi Greenhouse-Geisser, F(3,96; 172,38) = 70,39; p < 0,001; η² parsial = 0,618, yang berarti pola perubahan berat badan selama tiga bulan berbeda antarkelompok. Kenaikan berat badan tertinggi terdapat pada kelompok PMT Lokal (1,13 kg), diikuti PMT Biskuit (0,95 kg) dan Kontrol (0,34 kg); ketiga kelompok berbeda bermakna (uji kontras, koreksi Holm, p ≤ 0,0014).
Chunk ini menyimpan tabel dan grafik ke folder
hasil_laporan (dibuat otomatis) di samping file
.Rmd.
dir.create("hasil_laporan", showWarnings = FALSE)
f <- function(x) file.path("hasil_laporan", x)
# Data format LONG dan WIDE (CSV)
write.csv(data_long, f("dataset_PMT_balita_LONG.csv"), row.names = FALSE)
write.csv(data_wide, f("dataset_PMT_balita_WIDE.csv"), row.names = FALSE)
# Tabel hasil
write.csv(deskriptif, f("hasil_01_statistik_deskriptif_BB.csv"), row.names = FALSE)
write.csv(shapiro_mixed, f("hasil_02_shapiro_Kelompok_x_Waktu.csv"), row.names = FALSE)
write.csv(levene_mixed, f("hasil_03_levene_per_Waktu.csv"), row.names = FALSE)
hasil_anova <- as.data.frame(nice(aov2, correction = "GG", es = c("ges", "pes")))
write.csv(hasil_anova, f("hasil_04_mixed_ANOVA_GG.csv"), row.names = FALSE)
posthoc_group <- as.data.frame(pairs(em2b, adjust = "holm"))
write.csv(posthoc_group, f("hasil_05_posthoc_antar_kelompok_Holm.csv"), row.names = FALSE)
posthoc_time <- as.data.frame(pairs(em2, adjust = "holm"))
write.csv(posthoc_time, f("hasil_06_posthoc_dalam_kelompok_Holm.csv"), row.names = FALSE)
emm_group_time <- as.data.frame(em2b)
write.csv(emm_group_time, f("hasil_07_estimated_marginal_means.csv"), row.names = FALSE)
# Grafik
ggsave(f("grafik_01_profile_BB.png"), p_profil, width = 8, height = 5.5, dpi = 300)
ggsave(f("grafik_02_spaghetti_BB.png"), p_spag, width = 9, height = 6, dpi = 300)
ggsave(f("grafik_03_interaksi_Kelompok_x_Waktu.png"), p_interaksi, width = 8, height = 5.5, dpi = 300)
cat("Jumlah balita :", n_distinct(data_long$ID), "\n")
## Jumlah balita : 90
cat("Kelompok :", n_distinct(data_long$Kelompok), "\n")
## Kelompok : 3
cat("Waktu penimbangan:", n_distinct(data_long$Waktu), "\n")
## Waktu penimbangan: 4
cat("Outcome : BB_kg\n")
## Outcome : BB_kg
cat("File hasil tersimpan di:", file.path(getwd(), "hasil_laporan"), "\n")
## File hasil tersimpan di: C:/Users/ASUS/Downloads/hasil_laporan
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] ggpubr_1.0.0 performance_0.18.2 lmerTest_3.2-1 effectsize_1.0.3
## [5] car_3.1-5 carData_3.0-6 rstatix_1.1.0 emmeans_2.0.4
## [9] afex_1.5-1 lme4_2.0-6 Matrix_1.7-5 ggplot2_4.0.3
## [13] tidyr_1.3.2 dplyr_1.2.1 readxl_1.5.0.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 bayestestR_0.19.0 digest_0.6.39
## [7] rpart_4.1.27 estimability_2.0.0 lifecycle_1.0.5
## [10] cluster_2.1.8.2 magrittr_2.0.5 compiler_4.6.1
## [13] rlang_1.3.0 Hmisc_5.3-0 sass_0.4.10
## [16] tools_4.6.1 yaml_2.3.12 data.table_1.18.6.1
## [19] knitr_1.52 ggsignif_0.6.4 labeling_0.4.3
## [22] htmlwidgets_1.6.4 plyr_1.8.9 RColorBrewer_1.1-3
## [25] abind_1.4-8 withr_3.0.3 foreign_0.8-91
## [28] purrr_1.2.2 numDeriv_2016.8-1.1 nnet_7.3-20
## [31] grid_4.6.1 datawizard_1.4.0 colorspace_2.1-3
## [34] scales_1.4.0 MASS_7.3-65 insight_1.5.4
## [37] cli_3.6.6 mvtnorm_1.4-2 rmarkdown_2.32
## [40] ragg_1.5.2 reformulas_0.4.4 generics_0.1.4
## [43] otel_0.2.0 rstudioapi_0.19.0 reshape2_1.4.5
## [46] parameters_0.29.3 minqa_1.2.8 cachem_1.1.0
## [49] stringr_1.6.0 splines_4.6.1 parallel_4.6.1
## [52] cellranger_1.1.0 base64enc_0.1-6 vctrs_0.7.3
## [55] boot_1.3-32 jsonlite_2.0.0 pbkrtest_0.5.5
## [58] htmlTable_2.5.0 Formula_1.2-6 systemfonts_1.3.2
## [61] jquerylib_0.1.4 glue_1.8.1 nloptr_2.2.1
## [64] stringi_1.8.9 gtable_0.3.6 tibble_3.3.1
## [67] pillar_1.11.1 htmltools_0.5.9 R6_2.6.1
## [70] textshaping_1.0.5 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] checkmate_2.3.4 gridExtra_2.3.1 nlme_3.1-169
## [82] xfun_0.61 pkgconfig_2.0.3