Music exposure reduces anxiety- and depression-like behavior in rodents: a systematic review and multilevel meta-analysis
  • OSF
  • Paper
  1. Overall effects
  • Online supplementary
  • Effect size calculation
  • Overall effects
  • Uni-moderators
  • Publication bias
  • Leave-one-out
  • Code for figures
  • Alluvial plot

Table of contents

  • Variance–covariance matrices (within-cohort dependence)
  • Multilevel intercept-only meta-analytic models

Overall effects

db <- readr::read_csv(here("..","data","db_effect_sizes.csv")) %>%
  mutate(across(where(is.character), as.factor)) %>%
  mutate(across(c(lnRR, lnRR_var, lnVR, lnVR_var, lnCVR, lnCVR_var), as.numeric))

After estimating effect sizes, we fit three multilevel, intercept-only meta-analytic models to estimate the overall mean effect of music exposure on:

  • the mean outcome (lnRR),

  • the absolute variability (lnVR),

  • the relative variability (lnCVR).

Because many studies contribute multiple, non-independent outcomes (often measured on the same cohort), we use rma.mv() in metafor with a cohort-level variance–covariance (VCV) matrix built using vcalc().

Note

Variable definitions:

Variable Name Definition
ES_ID Unique row identifier for each effect size.
Study_ID Identifier for the primary research paper.
Cohort_ID Identifier for specific experimental groups within a study (combination of Study_ID and Experiment_ID).
lnRR, lnRR_var Log response ratio and its sample variance.
lnVR, lnVR_var Log variability ratio and its sample variance.
lnCVR, lnCVR_var Log coefficient of variation ratio and its sample variance.

Variance–covariance matrices (within-cohort dependence)

We assume that multiple outcomes measured on the same cohort have correlated sampling errors. We therefore construct a VCV matrix with an assumed within-cohort correlation of r = 0.5 when correlations are not reported.

Multilevel intercept-only meta-analytic models

The code below fits the three models using the same specification:

  • Random intercepts for Study (Study_ID)

  • Random intercepts for Effect size (ES_ID)

  • Random intercepts for Strain (Strain)

  • REML estimation and test = "t"

fit_overall_mv <- function(dat, yi, vi, rho = 0.5) {
  
  dat2 <- dat %>%
    filter(!is.na(.data[[yi]]), !is.na(.data[[vi]]))
  
  VCV <- metafor::vcalc(
    vi = dat2[[vi]],
    cluster = dat2$Cohort_ID,
    obs = dat2$ES_ID,
    rho = rho,
    data = dat2
  )
  
  mod <- metafor::rma.mv(
    yi = dat2[[yi]],
    V  = VCV,
    random = list(
      ~1 | Study_ID,
      ~1 | ES_ID,
      ~1 | Strain
    ),
    test = "t",
    method = "REML",
    sparse = TRUE,
    data = dat2
  )
  
  list(data = dat2, VCV = VCV, model = mod)
}

res_lnRR  <- fit_overall_mv(db, yi = "lnRR",  vi = "lnRR_var",  rho = 0.5)
res_lnVR  <- fit_overall_mv(db, yi = "lnVR",  vi = "lnVR_var",  rho = 0.5)
res_lnCVR <- fit_overall_mv(db, yi = "lnCVR", vi = "lnCVR_var", rho = 0.5)

lnRR

summary(res_lnRR$model)

Multivariate Meta-Analysis Model (k = 298; method: REML)

   logLik   Deviance        AIC        BIC       AICc   
-242.8851   485.7702   493.7702   508.5452   493.9072   

Variance Components:

            estim    sqrt  nlvls  fixed    factor 
sigma^2.1  0.0494  0.2223     20     no  Study_ID 
sigma^2.2  0.2302  0.4798    298     no     ES_ID 
sigma^2.3  0.0000  0.0001      6     no    Strain 

Test for Heterogeneity:
Q(df = 297) = 5799.5331, p-val < .0001

Model Results:

estimate      se     tval   df    pval    ci.lb    ci.ub     
 -0.2120  0.0662  -3.2046  297  0.0015  -0.3422  -0.0818  ** 

---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
orchaRd::i2_ml(res_lnRR$model)
    I2_Total  I2_Study_ID     I2_ES_ID    I2_Strain 
9.652114e+01 1.706309e+01 7.945805e+01 1.332707e-06 
p_lnRR <- orchard_plot(
  res_lnRR$model,
  group = "Study_ID",
  xlab = "lnRR",
  flip = TRUE
) +
  scale_x_discrete(labels = "Overall effect")
p_lnRR

lnVR

summary(res_lnVR$model)

Multivariate Meta-Analysis Model (k = 222; method: REML)

   logLik   Deviance        AIC        BIC       AICc   
-348.0396   696.0792   704.0792   717.6718   704.2644   

Variance Components:

            estim    sqrt  nlvls  fixed    factor 
sigma^2.1  0.0843  0.2903     16     no  Study_ID 
sigma^2.2  1.2242  1.1064    222     no     ES_ID 
sigma^2.3  0.0000  0.0001      6     no    Strain 

Test for Heterogeneity:
Q(df = 221) = 4392.5021, p-val < .0001

Model Results:

estimate      se    tval   df    pval    ci.lb   ci.ub    
  0.1346  0.1283  1.0490  221  0.2953  -0.1183  0.3875    

---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
orchaRd::i2_ml(res_lnVR$model)
    I2_Total  I2_Study_ID     I2_ES_ID    I2_Strain 
9.227147e+01 5.944073e+00 8.632740e+01 2.435885e-07 
p_lnVR <- orchard_plot(
  res_lnVR$model,
  group = "Study_ID",
  xlab = "lnVR",
  flip = TRUE
) +
  scale_x_discrete(labels = "Overall effect")
p_lnVR

lnCVR

summary(res_lnCVR$model)

Multivariate Meta-Analysis Model (k = 222; method: REML)

   logLik   Deviance        AIC        BIC       AICc   
-283.6139   567.2277   575.2277   588.8204   575.4129   

Variance Components:

            estim    sqrt  nlvls  fixed    factor 
sigma^2.1  0.0899  0.2999     16     no  Study_ID 
sigma^2.2  0.5904  0.7684    222     no     ES_ID 
sigma^2.3  0.0000  0.0000      6     no    Strain 

Test for Heterogeneity:
Q(df = 221) = 1588.4494, p-val < .0001

Model Results:

estimate      se    tval   df    pval    ci.lb   ci.ub    
  0.0169  0.1211  0.1397  221  0.8891  -0.2218  0.2556    

---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
orchaRd::i2_ml(res_lnCVR$model)
    I2_Total  I2_Study_ID     I2_ES_ID    I2_Strain 
8.231935e+01 1.087996e+01 7.143938e+01 1.188813e-07 
p_lnCVR <- orchard_plot(
  res_lnCVR$model,
  group = "Study_ID",
  xlab = "lnCVR",
  flip = TRUE
) +
  scale_x_discrete(labels = "Overall effect")
p_lnCVR

Note
sessionInfo()
R version 4.5.2 (2025-10-31 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_Guernsey.utf8  LC_CTYPE=English_Guernsey.utf8   
[3] LC_MONETARY=English_Guernsey.utf8 LC_NUMERIC=C                     
[5] LC_TIME=English_Guernsey.utf8    

time zone: America/Edmonton
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] orchaRd_2.1.3       metafor_4.8-0       numDeriv_2016.8-1.1
 [4] metadat_1.4-0       Matrix_1.7-4        patchwork_1.3.2    
 [7] lubridate_1.9.4     forcats_1.0.1       stringr_1.6.0      
[10] dplyr_1.1.4         purrr_1.2.1         readr_2.1.6        
[13] tidyr_1.3.2         tibble_3.3.1        ggplot2_4.0.1      
[16] tidyverse_2.0.0     knitr_1.51          here_1.0.2         
[19] dtplyr_1.3.2        DT_0.34.0          

loaded via a namespace (and not attached):
 [1] beeswarm_0.4.0      gtable_0.3.6        xfun_0.56          
 [4] htmlwidgets_1.6.4   lattice_0.22-7      mathjaxr_2.0-0     
 [7] tzdb_0.5.0          vctrs_0.7.1         tools_4.5.2        
[10] generics_0.1.4      parallel_4.5.2      sandwich_3.1-1     
[13] pacman_0.5.1        pkgconfig_2.0.3     data.table_1.18.2.1
[16] RColorBrewer_1.1-3  S7_0.2.1            lifecycle_1.0.5    
[19] compiler_4.5.2      farver_2.1.2        codetools_0.2-20   
[22] vipor_0.4.7         htmltools_0.5.9     yaml_2.3.12        
[25] pillar_1.11.1       crayon_1.5.3        MASS_7.3-65        
[28] multcomp_1.4-29     nlme_3.1-168        tidyselect_1.2.1   
[31] digest_0.6.39       mvtnorm_1.3-3       stringi_1.8.7      
[34] labeling_0.4.3      splines_4.5.2       latex2exp_0.9.8    
[37] rprojroot_2.1.1     fastmap_1.2.0       grid_4.5.2         
[40] cli_3.6.5           magrittr_2.0.4      survival_3.8-6     
[43] TH.data_1.1-5       withr_3.0.2         scales_1.4.0       
[46] bit64_4.6.0-1       ggbeeswarm_0.7.3    timechange_0.3.0   
[49] estimability_1.5.1  rmarkdown_2.30      emmeans_2.0.1      
[52] bit_4.6.0           otel_0.2.0          zoo_1.8-15         
[55] hms_1.1.4           coda_0.19-4.1       evaluate_1.0.5     
[58] rlang_1.1.7         xtable_1.8-4        glue_1.8.0         
[61] rstudioapi_0.18.0   vroom_1.7.0         jsonlite_2.0.0     
[64] R6_2.6.1           
Effect size calculation
Uni-moderators
Source Code
---
title: "Overall effects"
---

```{r}
#| label: setup
#| include: false
pacman::p_load(
               DT,
               dtplyr,
               here, 
               knitr,
               tidyverse,
               patchwork,
               metafor,
               orchaRd
               )
```


```{r}
#| label: load_effectsize_db
#| message: false
#| warning: false

db <- readr::read_csv(here("..","data","db_effect_sizes.csv")) %>%
  mutate(across(where(is.character), as.factor)) %>%
  mutate(across(c(lnRR, lnRR_var, lnVR, lnVR_var, lnCVR, lnCVR_var), as.numeric))
```

After estimating effect sizes, we fit three multilevel, intercept-only meta-analytic models to estimate the overall mean effect of music exposure on:

- the mean outcome (lnRR),

- the absolute variability (lnVR),

- the relative variability (lnCVR).

Because many studies contribute multiple, non-independent outcomes (often measured on the same cohort), we use `rma.mv()` in `metafor` with a cohort-level variance–covariance (VCV) matrix built using `vcalc()`.





::: callout-note
Variable definitions:

| Variable Name | Definition |
|:-----------------------------------|:-----------------------------------|
| **`ES_ID`** | Unique row identifier for each effect size. |
| **`Study_ID`** | Identifier for the primary research paper. |
| **`Cohort_ID`** | Identifier for specific experimental groups within a study (combination of Study_ID and Experiment_ID). |
|**`lnRR`**, **`lnRR_var`** | Log response ratio and its sample variance.|
|**`lnVR`**, **`lnVR_var`** | Log variability ratio and its sample variance.|
|**`lnCVR`**, **`lnCVR_var`** | Log coefficient of variation ratio and its sample variance.|



:::

## Variance–covariance matrices (within-cohort dependence)

We assume that multiple outcomes measured on the same cohort have correlated sampling errors. We therefore construct a VCV matrix with an assumed within-cohort correlation of r = 0.5 when correlations are not reported.


## Multilevel intercept-only meta-analytic models



The code below fits the three models using the same specification:

- Random intercepts for Study (`Study_ID`)

- Random intercepts for Effect size (`ES_ID`)

- Random intercepts for Strain (`Strain`)

- REML estimation and `test = "t"`



```{r}
#| label: overall_models_all_three
#| message: false
#| warning: false

fit_overall_mv <- function(dat, yi, vi, rho = 0.5) {
  
  dat2 <- dat %>%
    filter(!is.na(.data[[yi]]), !is.na(.data[[vi]]))
  
  VCV <- metafor::vcalc(
    vi = dat2[[vi]],
    cluster = dat2$Cohort_ID,
    obs = dat2$ES_ID,
    rho = rho,
    data = dat2
  )
  
  mod <- metafor::rma.mv(
    yi = dat2[[yi]],
    V  = VCV,
    random = list(
      ~1 | Study_ID,
      ~1 | ES_ID,
      ~1 | Strain
    ),
    test = "t",
    method = "REML",
    sparse = TRUE,
    data = dat2
  )
  
  list(data = dat2, VCV = VCV, model = mod)
}

res_lnRR  <- fit_overall_mv(db, yi = "lnRR",  vi = "lnRR_var",  rho = 0.5)
res_lnVR  <- fit_overall_mv(db, yi = "lnVR",  vi = "lnVR_var",  rho = 0.5)
res_lnCVR <- fit_overall_mv(db, yi = "lnCVR", vi = "lnCVR_var", rho = 0.5)





```



:::::::: columns
:::: {.column width="49%"}
::: panel-text
## lnRR
:::
```{r}
#| label: summary_lnRR
summary(res_lnRR$model)
```
```{r}
#| label: i2_lnRR
orchaRd::i2_ml(res_lnRR$model)
```
::: card
```{r}
#| label: orchard_lnRR
p_lnRR <- orchard_plot(
  res_lnRR$model,
  group = "Study_ID",
  xlab = "lnRR",
  flip = TRUE
) +
  scale_x_discrete(labels = "Overall effect")
p_lnRR
```
:::

::::

::: {.column width="2%"}
:::


:::: {.column width="49%"}
::: panel-text
## lnVR
:::
```{r}
#| label: summary_lnVR
summary(res_lnVR$model)
```
```{r}
#| label: i2_lnVR
orchaRd::i2_ml(res_lnVR$model)
```
::: card
```{r}
#| label: orchard_lnVR
p_lnVR <- orchard_plot(
  res_lnVR$model,
  group = "Study_ID",
  xlab = "lnVR",
  flip = TRUE
) +
  scale_x_discrete(labels = "Overall effect")
p_lnVR
```
:::

:::

::::



:::::::: columns
:::: {.column width="25%"}
::::
:::: {.column width="50%"}

::: panel-text
## lnCVR
:::
```{r}
#| label: summary_lnCVR
summary(res_lnCVR$model)
```

```{r}
#| label: i2_lnCVR
orchaRd::i2_ml(res_lnCVR$model)
```
::: card
```{r}
#| label: orchard_lnCVR
p_lnCVR <- orchard_plot(
  res_lnCVR$model,
  group = "Study_ID",
  xlab = "lnCVR",
  flip = TRUE
) +
  scale_x_discrete(labels = "Overall effect")
p_lnCVR
```
:::

:::
:::: {.column width="25%"}
::::

::::::::







::: callout-note

```{r}
sessionInfo()
```

:::