---
title: "Meta_HRV-Pollution_Sample-Code"
output: html_document
date: "2026-05-11"
---

# [FIXED EFFECTS MODEL]{.underline}

# [EXPLANATION]{.underline}

-   The following code is used to run Fixed-Effects (FE)meta-analysis.
    -   Assumes that the true outcomes are equal (homogeneous) across studies

## STEPS

1.  **Load packages.**
2.  **Run meta-analysis.**
3.  **Run meta-regression.**
4.  **Check/interpret:**
    1.  **MODEL.**
        1.  Meta-analysis: check estimate (pooled effect size) & CI.
        2.  Meta-regression: check moderator significance.
    2.  **PERMUATION TEST.** Validates model robustness using slightly different data (spare data not req.) Compare new CI to original
    3.  **GENERATE REPORT.** methods & results summary
        1.  **NOTE:** Doesn't work with mods, OR multivariate models.
    4.  **PLOTS**. Used to visualize heterogeneity.
        -   Forest: check distribution.

        -   Funnel: asymmetry (gaps in data) = publication bias.

        -   Bubble: check model fit/direction. Bubble size=effect size weight.
    5.  **TEST MODEL FIT.** Use the log-likelihood ratio test (LRT) to see if the addition of a moderator (Full model) results in a better fit than the original [Reduced] model. Check:
        1.  p value.
            1.  NOTE: divide by 2. Value provided is 2-tailed, but LRT 1-tailed.
        2.  AICc (AIC corrected from small samples). Lower=better model fit.
        3.  R\^2: variance explained.

------------------------------------------------------------------------

# [PACKAGES]{.underline}

```{r}

#META-ANALYSIS
##See: https://www.metafor-project.org/doku.php/tips
suppressWarnings({
library(metafor)
})
```

------------------------------------------------------------------------

# [META-ANALYSIS (FE)]{.underline}

## MODEL

```{r META: FIXED EFFECTS (FE)}

#FIXED EFFECTS (FE) MODEL
FE <- rma(data= Data, yi = SMD_X, vi = SMD_v, mods=, method="FE", test = "knha", slab=paste(k))

#MODEL SUMMARY
summary(FE)
```

## PERMUTATION TEST

```{r FE Permutation}

#PERMUTATION TEST
suppressWarnings({
permutest(FE)
})
```

## PLOTS

```{r FE PLOTS}

#FOREST
plot(FE)
```

## GENERATE REPORT

```{r FE REPORT}

#GENERATE REPORT
reporter(FE)
```

------------------------------------------------------------------------

# [META-REGRESSION (FE.Reg)]{.underline}

## MODEL

```{r META-REGRESSION: FIXED EFFECTS (FE.reg)}

#FIXED EFFECTS REGRESSION (FE.Reg) MODEL
##Continuous moderators are centered using 'scale(mod, scale=FALSE)'
FE.reg <- 
rma(data= Data, yi = SMD_X, vi = SMD_v, 
mods= ~ExpoHr-1,
method="FE", test = "knha", slab=paste(k))

#MODEL SUMMARY
summary(FE.reg)
```

## PERMUTATION TEST

```{r FE.reg Permutation}

#PERMUTATION TEST 
suppressWarnings({
permutest(FE.reg)
})
```

## PLOTS

```{r FE.reg PLOTS}

#FOREST 
plot(FE.reg)

#BUBBLE
regplot(FE.reg)
```

## TEST MODEL FIT

```{r FE model fit}

#FIT STATS
fitstats(FE, FE.reg)
```

------------------------------------------------------------------------

------------------------------------------------------------------------

------------------------------------------------------------------------

# [RANDOM EFFECTS MODEL]{.underline}

# [EXPLANATION]{.underline}

-   The following code is used to run Random Effects (RE) using DerSIMONIAN-LAIRD.
    -   Potential heterogeneity in the true outcomes is accounted for by adding a random effect for each study to the model.
    -   τ2 denotes the amount of variance (or “heterogeneity”) in the true outcomes

## STEPS

1.  **Load packages.**

2.  **Run meta-analysis.**

3.  **Run-regression.**

4.  **Check/interpret:**

    1.  **MODEL.**

        1.  Meta-analysis: check estimate (pooled effect size) & CI.
        2.  Meta-regression: check moderator significance.
        3.  Prediction interval. extract.

    2.  **PERMUATION TEST.** Validates model robustness using slightly different data (spare data not req.) Compare new CI to original

    3.  **PLOTS**. Used to visualize heterogeneity.

        -   Forest: check distribution.

        -   Funnel: asymmetry (gaps in data) = publication bias.

        -   Bubble: check model fit/direction. Bubble size=effect size weight.

    4.  **TEST MODEL FIT.** Use the log-likelihood ratio test (LRT) to see if the addition of a moderator (Full model) results in a better fit than the original [Reduced] model. Check:

        1.  p value.
            1.  NOTE: divide by 2. Value provided is 2-tailed, but LRT 1-tailed.
        2.  AICc (AIC corrected from small samples). Lower=better model fit.
        3.  R\^2: variance explained.

    5.  **SAVE OUTCOMES.** Used to test assumptions & for plots.

------------------------------------------------------------------------

# [PACKAGES]{.underline}

```{r}

#META-ANALYSIS
##See: https://www.metafor-project.org/doku.php/tips
library(metafor)

```

------------------------------------------------------------------------

# [META-ANALYSIS (DL)]{.underline}

```{r META: RANDOM (DL)}

#DerSIMONIAN-LAIRD (DL) MODEL
DL <- 
rma(data=Data, yi=SMD_X, vi=SMD_v, 
method="DL", test="knha", 
slab=paste(k))
summary(DL)

##PREDICTION INTERVAL
DL.PI <- predict (DL)
DL.PI
```

## PERMUTATION TEST

```{r DL Permutation}

#PERMUTATION TEST  
DL.perm <- permutest(DL)
DL.perm
```

## PLOTS

```{r DL PLOTS}

#FOREST  
plot(DL)  
```

## GENERATE REPORT

```{r RE REPORT}

#GENERATE REPORT 
reporter(DL)
```

------------------------------------------------------------------------

# [META-REGGRESSION (DL.Reg)]{.underline}

```{r META-REGRESSION: DL.reg}

#DerSIMONIAN-LAIRD (DL) MODEL
##Continuous moderators are centered using 'scale(mod, scale=FALSE)'
DL.reg <- 
rma(data=Data, yi=SMD_X, vi=SMD_v, 
mods=~ExpoHr-1, 
method="DL", test="knha", 
slab=paste(k))
summary(DL.reg)

##PREDICTION INTERVAL
DL.reg.PI <- predict (DL.reg,  addx=TRUE, digits=3)
DL.reg.PI
```

## PERMUTATION

```{r DL.reg Permutation}

#PERMUTATION TEST  
DL.reg.perm <- permutest(DL.reg)
DL.reg.perm
```

## PLOTS

```{r DL.reg PLOTS}

#FOREST  
plot(DL.reg)  

#BUBBLE
regplot(DL.reg)
```

## TEST MODEL FIT

```{r DL model fit}

#LRT: FULL MODEL (ORIGINAL) VS. REDUCED 
anova(DL, DL.reg, rec)
```

------------------------------------------------------------------------

# SAVE OUTCOMES

```{r DL OUTCOMES}

#EXPLANATION: save predict values, error and weights to test assumptions.


#CI
Out.k$DL.reg.lb <- predict.rma(DL.reg)$ci.lb
Out.k$DL.reg.ub <- predict.rma(DL.reg)$ci.ub

#RANDOM ERROR
Out.k$DL.reg.ranef <- ranef(DL.reg)$pred

#Best Linear Unbiased Predictions (BLUPs) 
##study-specific true effect sizes by combining fixed- and random- effects contributions.
##Only works with UVA.
Out.k$DL.reg.blup <- blup(DL.reg)$pred

##WEIGHTS
Out.k$DL.reg.wt <- weights(DL.reg)
```

------------------------------------------------------------------------

------------------------------------------------------------------------

------------------------------------------------------------------------

# [MULTI-LEVEL MODEL]{.underline}

# [EXPLANATION]{.underline}

-   The following code is used to run Multi-Level (ML) meta-analysis.
    -   Accounts for multiple effects (k) clustered within a study (u).

## STEPS

1.  **Load packages.**

2.  **Run meta-analysis.**

3.  **Run-regression.**

4.  **Check/interpret:**

    1.  **MODEL.**

        1.  Meta-analysis: check estimate (pooled effect size) & CI.
        2.  Meta-regression: check moderator significance.
        3.  Prediction interval. extract.

    2.  **PERMUATION TEST.** Validates model robustness using slightly different data (spare data not req.) Compare new CI to original

    3.  **PLOTS**. Used to visualize heterogeneity.

        -   Forest: check distribution.

        -   Funnel: asymmetry (gaps in data) = publication bias.

        -   Bubble: check model fit/direction. Bubble size=effect size weight.

        -   3-Level Variance. plots variance at each level (sampling, effect size(k), study(u)). Extract 'Total Variance'.

    4.  **3-LEVEL MODEL FIT.** Tests whether a 3-level model is necessary by fixing between-study or within-study [(sigma2=c(o, NA)] variance.

        1.  BETWEEN-STUDY. Extract sigma2=c(NA, 0) & check model fit (LRT).
        2.  WITHIN-STUDY.

    5.  **TEST MODEL FIT.** Use the log-likelihood ratio test (LRT) to see if the addition of a moderator (Full model) results in a better fit than the original [Reduced] model. Check:

        1.  p value.
            1.  NOTE: divide by 2. Value provided is 2-tailed, but LRT 1-tailed.
        2.  AICc (AIC corrected from small samples). Lower=better model fit.
        3.  R\^2: variance explained.

    6.  **SAVE OUTCOMES.** Used to test assumptions & for plots.

------------------------------------------------------------------------

# PACKAGES

```{r}

#META-ANALYSIS
##See: https://www.metafor-project.org/doku.php/tips
library(metafor)

##META-ANALYSIS
#Used here To calculate variance at each level.
##See: https://dmetar.protectlab.org/
library(dmetar)

#COMBINE PLOTS
library(patchwork)   # install.packages("patchwork") if needed

```

------------------------------------------------------------------------

# [META-ANALYSIS (ML)]{.underline}

## MODEL

```{r META: MULTI-LEVEL(ML)}

#MULTIVARIATE (MV) MODEL
ML <- 
rma.mv(data= Data, yi=SMD_X, V=SMD_v, 
random= ~1|u/k, 
test="knha", slab=k)
summary(ML, digits=3)

#SUMMARY
summary(ML, digits=3) 

##PREDICTION INTERVAL
ML.PI<-predict (ML)
ML.PI
```

## PLOTS

```{r PLOT: ML}

##FOREST
forest.rma (ML)

#3-LEVEL VARIANCE
plot(var.comp(ML))
```

## 3-LEVEL MODEL FIT: BETWEEN-STUDY

```{r BETWEEN-STUDY VARIANCE: ML}

#RUN MODEL
##Fix between-study variance [(sigma2=c(NA, 0)].
ML.A3 <- 
rma.mv(data=Data, yi= SMD_X, V=SMD_v, 
random=list(~1|u, ~1|k), sigma2=c(NA,0), 
test="knha", slab=k)

#SUMMARY
summary(ML.A3)

#LRT: FULL MODEL (ORIGINAL) VS. REDUCED 
anova(ML, ML.A3, refit=TRUE)
```

## 3-LEVEL MODEL FIT: WITHIN-STUDY

```{r WITHIN-STUDY VARIANCE: ML}

#RUN MODEL
##Fix within-study variance [sigma2=c(0, NA)].
ML.A2 <- 
rma.mv(data=Data, yi=SMD_X, V=SMD_v, 
random=list(~1|u, ~1|k), sigma2=c(0,NA), 
test="knha", slab=k)

#SUMMARY
summary(ML.A2 )

#LRT: FULL MODEL (ORIGINAL) VS. REDUCED 
anova(ML,ML.A2, refit=TRUE )
```

------------------------------------------------------------------------

# [META-REGRESSION (ML.reg)]{.underline}

## MODEL

```{r REGRESSION: ML }

#MULTIVARIATE (MV) MODEL 
##Continuous moderators are centered using 'scale(mod, scale=FALSE)'
ML.reg <- 
rma.mv(data= Data, yi=SMD_X, V=SMD_v, 
mods=~ExpoHr-1, 
random= ~1 | u/k, 
struct=, test="knha", slab=k) 

#SUMMARY
summary(ML.reg, digits=3) 

##PREDICTION INTERVAL 
ML.reg.PI<-predict (ML.reg) 
ML.reg.PI

#GGplot
resid_df <- data.frame(
  Fitted    = fitted(ML.reg),
  Residuals = residuals(ML.reg),
  StdResid  = rstandard(ML.reg)$z,
  Study     = Data$k          # your study-level label
)

ggplot(resid_df, aes(x = Fitted, y = StdResid, label = Study)) +
  geom_hline(yintercept = 0,  linetype = "dashed", color = "red") +
  geom_hline(yintercept = c(-2, 2), linetype = "dotted", color = "grey50") +
  geom_point(size = 3, color = "steelblue", alpha = 0.8) +
  ggrepel::geom_text_repel(size = 3) +
  labs(x     = "Fitted Values",
       y     = "Standardized Residuals",
       title = "Residuals vs. Fitted — Linear Model") +
  theme_bw()
```

## PLOTS

```{r PLOTS: ML.reg }

#FOREST 
forest.rma (ML.reg) 

#BUBBLE 
regplot(ML.reg, lty=2, refline=, grid="grey", xlab="Exposure Hr") 

#VARIANCE 
plot(var.comp(ML.reg))
```

## TEST MODEL FIT (META VS REGRESSION)

```{r MODEL FIT: ML}

#FIT STATS
fitstats(ML, ML.reg)
```

## 3-LEVEL MODEL FIT: BETWEEN-STUDY

```{r BETWEEN-STUDY VARIANCE: ML.reg }

#RUN MODEL 
ML.reg.A3 <- rma.mv(data=Data, yi= SMD_X, V=SMD_v, random=list(~1|u, ~1|k), sigma2=c(NA,0), test="knha", slab=k)  

#SUMMARY 
summary(ML.A3)  

#LRT: FULL MODEL (ORIGINAL) VS. REDUCED 
anova(ML.reg, ML.reg.A3, refit=TRUE)
```

## 3-LEVEL MODEL FIT: WITHIN-STUDY

```{r WITHIN-STUDY VARIANCE: ML.reg }

#RUN MODEL
ML.reg.A2 <- rma.mv(data=Data, yi=SMD_X, V=SMD_v, random=list(~1|u, ~1|k), sigma2=c(0,NA), test="knha", slab=k)

#SUMMARY
summary(ML.reg.A2 )

#LRT: FULL MODEL (ORIGINAL) VS. REDUCED 
anova(ML.reg, ML.reg.A2, refit=TRUE)
```

------------------------------------------------------------------------

# SAVE OUTCOMES

-   Save predict values, error and weights to test assumptions.

```{r SAVE OUTCOMES: ML.reg}

##PREDICTED
Out.k$ML.reg.pred <- predict.rma(ML.reg)$pred

##CI
Out.k$ML.reg.lb <- predict.rma(ML.reg)$ci.lb
Out.k$ML.reg.ub <- predict.rma(ML.reg)$ci.ub

##RANDOM ERROR
Out.k$ML.reg.ranef <- ranef(ML.reg)$pred

##WEIGHTS
Out.k$ML.reg.wt <- weights(ML.reg)
```
