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

Table of contents

  • lnRR
    • Assay type
    • Lifestage of exposure
    • Control condition
    • Induced behaviour
  • Patched figure
  • lnVR
    • Overall effect
    • Outcome type
    • Assay type
    • Lifestage of exposure
    • Music genre
    • Experimental design
  • Overall effects figure
  • Outcome type figure

Code for figures

Note

Variable definitions:

Variable Name Definition
ES_ID Unique row identifier for each \(\text{lnRR}\) effect size.
Study_ID Identifier for the primary research paper.
Cohort_ID Identifier for specific experimental groups within a study.
Outcome_type The behavioral construct measured. Levels: Anxiety, Depression, both, unclear.
Lifestage_exposure Animal’s developmental stage during music exposure. Levels: Adolescent, Juvenile, Young adult, Adult, Mixed, Unclear.
Sex Sex of the subjects. Levels: Male, Female.
Strain Specific animal strain or species used.
Meta_genre Categorization of the music stimulus. Levels: Western Art Music / Orchestral, Popular Contemporary Music, Traditional Music / Folk / World, Mixed, Unclear.
Music_exposure_duration Total time subjects were exposed to music. Levels: Acute, short, medium, long.
Experimental_design Study’s methodological setup. Levels: Posttest-Only Control Group, Randomized Block, Factorial, Repeated Measures.
Induced behavior Whether the tested behavior was innate or experimentally induced.
Relative_timing When music was administered relative to the behavioral test. Levels: before, concurrent, both, not specified.
Experimental_procedures Identifies non-treatment controls for interventions. Levels: sham, none.
Control_condition Description of the control group condition. Levels: white noise, ambient noise
Assay_type Type of behavioral test used.
Overall_rob Overall risk of bias assessment for each study. Levels: 2 (low), 1 (moderate), 0 (high).
NoteData Filtering & Matrix Alignment

We exclude moderator levels with fewer than five effect sizes (k < 5) to avoid unstable estimates and misleading contrasts.

filter_low_k <- function(dat, V, moderator, min_k = 5) {
  moderator <- rlang::ensym(moderator)
  k_counts <- dat %>%
    dplyr::count(!!moderator, name = "k_es")
  keep_levels <- k_counts %>% dplyr::filter(k_es >= min_k) %>% dplyr::pull(!!moderator)
  dat_f <- dat %>% dplyr::filter((!!moderator) %in% keep_levels)
  idx <- which(dat[[rlang::as_string(moderator)]] %in% keep_levels)
  V_f <- V[idx, idx]
  list(data = dat_f, V = V_f, keep = keep_levels, k = k_counts)
}

lnRR

ma_all <- rma.mv(yi = lnRR,
                  V = VCV, 
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db )

overall_lnRR<-orchard_plot(ma_all,
              group = "Study_ID",
              xlab = "lnRR", 
              flip=T,trunk.size = 0.3,     
              branch.size = 2,alpha = 0.3) +
              scale_x_discrete(labels = c("Overall effect")) +
            scale_colour_brewer(palette = "Paired") +
            scale_fill_brewer(palette = "Dark2")+
                scale_y_continuous(breaks = seq(-4, 2, by = 1),
                                   minor_breaks = seq(-4, 2, by = 0.5 ))
overall_lnRR

f <- filter_low_k(db, VCV,Outcome_type, min_k = 5)
db_filtered <- f$data
VCV_filtered <- f$V



mOT <- rma.mv(yi = lnRR,
                  V = VCV_filtered, 
              mods = ~ Outcome_type,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db_filtered)


orchard_outcomelnRR<-orchard_plot(mOT,
             mod = "Outcome_type",
              group = "Study_ID",
              xlab = "lnRR", 
             flip = T,trunk.size = 0.3,     
              branch.size = 2, alpha = 0.3) +
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_y_continuous(breaks = seq(-4, 2, by = 1),
                                                                    minor_breaks = seq(-4, 2, by = 0.5 ))
orchard_outcomelnRR

Assay type

f <- filter_low_k(db, VCV,Assay_type, min_k = 5)
db_filtered <- f$data
VCV_filtered <- f$V



mAT <- rma.mv(yi = lnRR,
                  V = VCV_filtered, 
              mods = ~ Assay_type-1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID, 
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db_filtered)


assay_labels <- c(
  "Elevated Plus Maze" = "EPM",
  "Open Field Test" = "OFT",
  "Light-Dark Box" = "LDB",
  "Forced Swim Test (FST)" = "FST",
  "Tail Suspension Test (TST)" = "TST",
  "Sucrose Preference Test (SPT)" = "SPT"
)

orchard_assay <- orchard_plot(
  mAT,legend.pos = "top.left",
  mod   = "Assay_type",
  group = "Study_ID",
  xlab  = expression(lnRR),trunk.size = 0.3,     
              branch.size = 2, alpha = 0.3
) +
  scale_colour_brewer(palette = "Set1") +
  scale_fill_brewer(palette = "Dark2") +
  scale_x_discrete(labels = assay_labels) +scale_y_continuous(limits = c(-4,2),breaks = seq(-4, 2, by = 1),
                                                                    minor_breaks = seq(-4, 2, by = 0.5 ))+
  labs(
    x = NULL,
    y = expression(lnRR),
    subtitle = "Behavioural assay"
  ) +
  theme(
    plot.subtitle = element_text(size = 10, face = "bold"),
    axis.title.y = element_text(face = "bold"),legend.direction = "vertical"
  )

orchard_assay

Lifestage of exposure

mLE <- rma.mv(yi = lnRR,
                  V = VCV, 
              mods = ~ Lifestage_exposure-1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)



lifestage_labels <- c(
  "Adolescent"  = "Adol.",
  "Adult"       = "Adult",
  "Juvenile"    = "Juv.",
  "Young adult" = "Y. adult",
  "Mixed"       = "Mixed",
  "Unclear"     = "Unclear"
)


orchard_lifestage<-orchard_plot(mLE,legend.pos = "top.left",
             mod = "Lifestage_exposure",
              group = "Study_ID",
               xlab = expression(lnRR),trunk.size = 0.3,     
              branch.size = 2,alpha=0.3 ,
             flip = T) +
  scale_x_discrete(labels = lifestage_labels)+
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_x_discrete(labels = assay_labels) +scale_y_continuous(limits = c(-4,2),breaks = seq(-4, 2, by = 1),
                                                                    minor_breaks = seq(-4, 2, by = 0.5 ))+  theme(legend.direction = "vertical")
orchard_lifestage

Control condition

mCC <- rma.mv(yi = lnRR,
                  V = VCV, 
              mods = ~ Control_conditions,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID, 
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)



control_labels <- c(
  "ambiet sound" = "Ambient sound",
  "white noise"  = "White noise"
)
orchard_control<-orchard_plot(mCC,
             mod = "Control_conditions",legend.pos = "top.left",
              group = "Study_ID",
              xlab = expression(lnRR),trunk.size = 0.3,     
              branch.size = 2,alpha=0.3) +
  scale_x_discrete(labels = control_labels) +
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_x_discrete(labels = assay_labels) +scale_y_continuous(limits = c(-4,2),breaks = seq(-4, 2, by = 1),
                                                                    minor_breaks = seq(-4, 2, by = 0.5 ))+  theme(legend.direction = "vertical")
orchard_control

Induced behaviour

mIB <- rma.mv(yi = lnRR,
                  V = VCV, 
              mods = ~ `Induced behaviour`,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID, 
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)


orchard_behaviour<-orchard_plot(mIB,
             mod = "Induced behaviour",legend.pos = "top.left",
              group = "Study_ID",
              xlab = expression(lnRR), trunk.size = 0.3,     
              branch.size = 2,alpha=0.3,
              flip = T)+
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_y_continuous(limits = c(-4,2),breaks = seq(-4, 2, by = 1),
                                                                    minor_breaks = seq(-4, 2, by = 0.5 ))+  theme(legend.direction = "vertical")
orchard_behaviour

Patched figure

orchard_assay     <- orchard_assay     + labs(subtitle = "Behavioral assay")
orchard_lifestage <- orchard_lifestage + labs(subtitle = "Age at exposure")
orchard_control   <- orchard_control   + labs(subtitle = "Control condition")
orchard_behaviour <- orchard_behaviour + labs(subtitle = "Behavioral mechanism")

# 2) Hide legends on all but ONE panel (choose bottom-right here)
orchard_assay     <- orchard_assay     + theme(legend.position = "none")
orchard_lifestage <- orchard_lifestage + theme(legend.position = "none")

orchard_control   <- orchard_control   + theme(legend.position = "none")
# orchard_behaviour keeps the legend

# 3) Patch with your layout
patched_figure <- (orchard_assay + orchard_lifestage  ) /
                 (orchard_control   + orchard_behaviour) +
  plot_annotation(tag_levels = "A") &
  theme(
    plot.tag = element_text(size = 12, face = "bold"),
    plot.subtitle = element_text(size = 10, face = "bold")
  )

# 4) Enforce identical lnRR scale and a single y-axis label across ALL panels
patched_figure <- patched_figure &
  scale_y_continuous(
    limits = c(-4, 2),                       
    breaks = seq(-4, 2, by = 1),
    minor_breaks = seq(-4, 2, by = 0.5)
  ) &
  labs(y = expression(lnRR), x = NULL)

ggsave(filename = here("..","Plots", "patched_lnRR.pdf"),
       plot = patched_figure,
       dpi = 300, device = cairo_pdf,width = 10,  
       height = 10)

ggsave(filename = here("..","Plots", "patched_lnRR.jpg"),
       plot = patched_figure,
       width = 10,  
       height = 10,  
       dpi = 300)

lnVR

db <- readr::read_csv(here("..","data","db_effect_sizes.csv")) %>%
  mutate(across(c(C_n, Ex_n, C_mean, Ex_mean, C_SE, Ex_SE, C_SD, Ex_SD, lnVR, lnVR_var), as.numeric)) %>%
  mutate(across(where(is.character), as.factor)) %>% filter(!is.na(lnVR),  !is.na(lnVR_var))


# CALCULATE VCV USING lnVR VARIANCE
VCV <- metafor::vcalc(
  vi = lnVR_var,       
  cluster = Cohort_ID, # The clustering variable (Study_ID + Ex_ID)
  obs = ES_ID,         # The unique observation/effect size ID
  rho = 0.5,           # Assumed correlation between outcomes from the same cohort
  data = db 
)

Overall effect

mOV <- rma.mv(yi = lnVR, 
                  V = VCV, 
              mods = ~ 1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)


orchard_overalllnVR<-orchard_plot(mOV,
               group = "Study_ID",
               xlab = "lnVR", 
              flip = T,trunk.size = 0.3,     
              branch.size = 2, alpha = 0.3) + 
              scale_x_discrete(labels = c("Overall effect"))+
            scale_colour_brewer(palette = "Dark2") +
            scale_fill_brewer(palette = "Paired")+scale_y_continuous(limits = c(-4,11),breaks = seq(-4, 10, by = 2),
                                                                    minor_breaks = seq(-4, 11, by = 1 ))
orchard_overalllnVR

Outcome type

f <- filter_low_k(db, VCV,Outcome_type, min_k = 5)
db_filtered <- f$data
VCV_filtered <- f$V

mOT2 <- rma.mv(yi = lnVR, 
                  V = VCV_filtered, 
              mods = ~ Outcome_type,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db_filtered)



orchard_outcome_lnVR<-orchard_plot(mOT2,
              mod = "Outcome_type",
               group = "Study_ID",
               xlab = "lnVR", 
              flip = T,trunk.size = 0.3,alpha = 0.3,     
              branch.size = 2) +
            scale_colour_brewer(palette = "Dark2") +
            scale_fill_brewer(palette = "Set1")+scale_y_continuous(limits = c(-4,11),breaks = seq(-4, 10, by = 2),
                                                                    minor_breaks = seq(-4, 11, by = 1 ))
orchard_outcome_lnVR

Assay type

#| label: filter_assay_type_lnVR
f <- filter_low_k(db, VCV, Assay_type, min_k = 5)
db_filtered <- f$data
VCV_filtered <- f$V


mAT <- rma.mv(yi = lnVR,
                  V = VCV_filtered, 
              mods = ~ Assay_type-1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID, 
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db_filtered)


assay_labels <- c(
  "Elevated Plus Maze" = "EPM",
  "Open Field Test" = "OFT",
  "Light-Dark Box" = "LDB",
  "Forced Swim Test (FST)" = "FST",
  "Tail Suspension Test (TST)" = "TST",
  "Sucrose Preference Test (SPT)" = "SPT"
)

orchard_assay <- orchard_plot(
  mAT,legend.pos = "top.left",
  mod   = "Assay_type",
  group = "Study_ID",
  xlab  = expression(lnVR) ,trunk.size = 0.3,     
              branch.size = 2,alpha = 0.3
) +
  scale_colour_brewer(palette = "Set1") +
  scale_fill_brewer(palette = "Dark2") +
  scale_x_discrete(labels = assay_labels)+
  scale_y_continuous(limits = c(-4,11),breaks = seq(-4, 11, by = 2),
                     minor_breaks = seq(-4, 11, by = 1 ))+  
  labs(
    x = NULL,
    y = expression(lnVR),
    subtitle = "Behavioural assay"
  ) +
  theme(
    plot.subtitle = element_text(size = 10, face = "bold"),
    axis.title.y = element_text(face = "bold"),legend.direction = "vertical")
  

orchard_assay

Lifestage of exposure

mLE <- rma.mv(yi = lnVR, 
                  V = VCV, 
              mods = ~ Lifestage_exposure-1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)



lifestage_labels <- c(
  "Adolescent"  = "Adol.",
  "Adult"       = "Adult",
  "Juvenile"    = "Juv.",
  "Young adult" = "Y. adult",
  "Mixed"       = "Mixed",
  "Unclear"     = "Unclear"
)


orchard_lifestage<-orchard_plot(mLE,legend.pos = "top.left",
              mod = "Lifestage_exposure",
               group = "Study_ID",
                xlab = expression(lnVR), trunk.size = 0.3,     
              branch.size = 2 ,alpha = 0.3
              ) + scale_x_discrete(labels = lifestage_labels)+
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_y_continuous(limits=c(-4,11),breaks = seq(-4, 11, by = 2),
                                                                    minor_breaks = seq(-4, 11, by = 1 ))+ 
  theme(legend.direction = "vertical")
orchard_lifestage

Music genre

Meta_genre

mMG <- rma.mv(yi = lnVR,
                  V = VCV, 
              mods = ~ Meta_genre-1 ,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)



genre_labels <- c(
  "Mixed"  = "Mixed",
  "Popular Contemporary Music"       = "Contemporary",
  "Traditional Music / Folk / World"    = "World",
  "Western Art Music / Orchestral" = "Orchestral"
)





orchard_genre<-orchard_plot(mMG,legend.pos = "top.left",
             mod = "Meta_genre",
              group = "Study_ID",
             xlab = "lnVR", 
             flip = T,trunk.size = 0.3,     
              branch.size = 2,alpha = 0.3) +  
  scale_x_discrete(labels = genre_labels)+
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_y_continuous(limits=c(-4,11),breaks = seq(-4, 11, by = 2),
                                                                    minor_breaks = seq(-4, 11, by = 1 ))+ 
  theme(legend.direction = "vertical")
orchard_genre

Experimental design

mEXD <- rma.mv(yi = lnVR,
                  V = VCV, 
              mods = ~ Experimental_design-1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID, 
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)



design_labels<-c(
  "Factorial"="Factorial",
  "Posttest-Only Control Group"="Posttest",
  "Repeated Measures"="Repeated"
  
)


orchard_design<-orchard_plot(mEXD,legend.pos = "top.left",
             mod = "Experimental_design",
              group = "Study_ID",
              xlab = expression(lnVR), 
              flip=T,
             trunk.size = 0.3,     
              branch.size = 2,alpha = 0.3 ) +
  scale_x_discrete(labels = design_labels) +
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_y_continuous(limits = c(-4,11),breaks = seq(-4, 11, by = 2),
                                                                    minor_breaks = seq(-4, 11, by = 1 ))+ 
  theme(legend.direction = "vertical")
orchard_design

orchard_assay     <- orchard_assay     + labs(subtitle = "Behavioral assay")
orchard_lifestage <- orchard_lifestage + labs(subtitle = "Age at exposure")

orchard_genre   <- orchard_genre  + labs(subtitle = "Music meta-genre")
orchard_design <-orchard_design + labs(subtitle = "Experimental design")




# 2) Hide legends on all but ONE panel 
orchard_assay    <- orchard_assay    + theme(legend.position = "none")
orchard_lifestage <- orchard_lifestage + theme(legend.position = "none")
orchard_genre   <- orchard_genre+ theme(legend.position = "none")

# orchard_behaviour keeps the legend

# 3) Patch with your layout
patched_figure <- ( orchard_assay+orchard_lifestage) /
                 (orchard_genre + orchard_design) +
  plot_annotation(tag_levels = "A") &
  theme(
    plot.tag = element_text(size = 12, face = "bold"),
    plot.subtitle = element_text(size = 10, face = "bold")
   )
  

# 4) Enforce identical lnRR scale and a single y-axis label across ALL panels
patched_figure <- patched_figure &
  scale_y_continuous(
    limits = c(-4, 11),                       
    breaks = seq(-4, 11, by = 2),
    minor_breaks = seq(-4, 11, by = 1)
  ) &
  labs(y = expression(lnVR), x = NULL)

ggsave(filename = here("..","Plots", "patched_lnVR.pdf"),
       plot = patched_figure,
       dpi = 300, device = cairo_pdf,width = 10,  
       height = 10)

ggsave(filename = here("..","Plots", "patched_lnVR.jpg"),
       plot = patched_figure,
       width = 10,  
       height = 10,  
       dpi = 300)

Overall effects figure

fig2<-(overall_lnRR/orchard_overalllnVR)

fig2<-fig2+ 
  plot_annotation(
    title = '',
    tag_levels = 'A'
  )

ggsave(filename = here("..","Plots", "overall_effects.pdf"),
       plot = fig2,
       dpi = 300, device = cairo_pdf)

ggsave(filename = here("..","Plots", "overall_effects.jpg"),
       plot = fig2,
       width = 7,  
       height = 9,  
       dpi = 300)

Outcome type figure

fig3<-(orchard_outcomelnRR/orchard_outcome_lnVR)

fig3<-fig3+ 
  plot_annotation(
    title = '',
    tag_levels = 'A'
  )

ggsave(filename = here("..","Plots", "outcome_effects.pdf"),
       plot = fig3,
       dpi = 300, device = cairo_pdf)

ggsave(filename = here("..","Plots", "outcome_effects.jpg"),
       plot = fig3,
       width = 7,  
       height = 9,  
       dpi = 300)
Leave-one-out
Alluvial plot
Source Code
---
title: "Code for figures"
---

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


db <- readr::read_csv(here("..","data","db_effect_sizes.csv")) %>%
mutate(across(c(C_n, Ex_n, C_mean, Ex_mean, C_SE, Ex_SE, C_SD, Ex_SD, lnRR, lnRR_var), as.numeric))%>%
  mutate(across(where(is.character), as.factor)) %>%
  mutate(
    Control_conditions = as.character(Control_conditions),
    Control_conditions = dplyr::recode(
      Control_conditions,
      "ambiet sound" = "Ambient sound",
      "white noise"  = "White noise"
    ),
    Control_conditions = factor(Control_conditions)
  )



VCV <- metafor::vcalc(
  vi = lnRR_var,
  cluster = Cohort_ID, # The clustering variable (Study_ID + Ex_ID)
  obs = ES_ID,         # The unique observation/effect size ID
  rho = 0.5,           # Assumed correlation between outcomes from the same cohort
  data = db 
)
```

::: callout-note
Variable definitions:

| Variable Name | Definition |
|:-----------------------------------|:-----------------------------------|
| **`ES_ID`** | Unique row identifier for each $\text{lnRR}$ effect size. |
| **`Study_ID`** | Identifier for the primary research paper. |
| **`Cohort_ID`** | Identifier for specific experimental groups within a study. |
| **`Outcome_type`** | The behavioral construct measured. Levels: **Anxiety**, **Depression**, **both**, **unclear**. |
| **`Lifestage_exposure`** | Animal's developmental stage during music exposure. Levels: **Adolescent**, **Juvenile**, **Young adult**, **Adult**, **Mixed**, **Unclear**. |
| **`Sex`** | Sex of the subjects. Levels: **Male**, **Female**. |
| **`Strain`** | Specific animal strain or species used. |
| **`Meta_genre`** | Categorization of the music stimulus. Levels: **Western Art Music / Orchestral**, **Popular Contemporary Music**, **Traditional Music / Folk / World**, **Mixed**, **Unclear**. |
| **`Music_exposure_duration`** | Total time subjects were exposed to music. Levels: **Acute**, **short**, **medium**, **long**. |
| **`Experimental_design`** | Study's methodological setup. Levels: **Posttest-Only Control Group**, **Randomized Block**, **Factorial**, **Repeated Measures**. |
| **`Induced behavior`** | Whether the tested behavior was innate or experimentally induced. |
| **`Relative_timing`** | When music was administered relative to the behavioral test. Levels: **before**, **concurrent**, **both**, **not specified**. |
| **`Experimental_procedures`** | Identifies non-treatment controls for interventions. Levels: **sham**, **none**. |
| **`Control_condition`** | Description of the control group condition. Levels: **white noise**, **ambient noise** |
| **`Assay_type`** | Type of behavioral test used. |
| **`Overall_rob`** | Overall risk of bias assessment for each study. Levels: **2 (low)**, **1 (moderate)**, **0 (high)**. |
:::

::: {.callout-note icon="true"}
## Data Filtering & Matrix Alignment

We exclude moderator levels with fewer than five effect sizes (k \< 5) to avoid unstable estimates and misleading contrasts.

```{r}
#| label: filter_low_k
#| eval: TRUE

filter_low_k <- function(dat, V, moderator, min_k = 5) {
  moderator <- rlang::ensym(moderator)
  k_counts <- dat %>%
    dplyr::count(!!moderator, name = "k_es")
  keep_levels <- k_counts %>% dplyr::filter(k_es >= min_k) %>% dplyr::pull(!!moderator)
  dat_f <- dat %>% dplyr::filter((!!moderator) %in% keep_levels)
  idx <- which(dat[[rlang::as_string(moderator)]] %in% keep_levels)
  V_f <- V[idx, idx]
  list(data = dat_f, V = V_f, keep = keep_levels, k = k_counts)
}

```
:::
# lnRR

```{r}
#| label: overall_lnRR

ma_all <- rma.mv(yi = lnRR,
                  V = VCV, 
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db )

overall_lnRR<-orchard_plot(ma_all,
              group = "Study_ID",
              xlab = "lnRR", 
              flip=T,trunk.size = 0.3,     
              branch.size = 2,alpha = 0.3) +
              scale_x_discrete(labels = c("Overall effect")) +
            scale_colour_brewer(palette = "Paired") +
            scale_fill_brewer(palette = "Dark2")+
                scale_y_continuous(breaks = seq(-4, 2, by = 1),
                                   minor_breaks = seq(-4, 2, by = 0.5 ))
overall_lnRR
```

```{r}
#| label: model_outcome_type_lnRR

f <- filter_low_k(db, VCV,Outcome_type, min_k = 5)
db_filtered <- f$data
VCV_filtered <- f$V



mOT <- rma.mv(yi = lnRR,
                  V = VCV_filtered, 
              mods = ~ Outcome_type,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db_filtered)


orchard_outcomelnRR<-orchard_plot(mOT,
             mod = "Outcome_type",
              group = "Study_ID",
              xlab = "lnRR", 
             flip = T,trunk.size = 0.3,     
              branch.size = 2, alpha = 0.3) +
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_y_continuous(breaks = seq(-4, 2, by = 1),
                                                                    minor_breaks = seq(-4, 2, by = 0.5 ))
orchard_outcomelnRR
```

## Assay type

```{r}
#| label: filter_assay_type_lnRR
f <- filter_low_k(db, VCV,Assay_type, min_k = 5)
db_filtered <- f$data
VCV_filtered <- f$V



mAT <- rma.mv(yi = lnRR,
                  V = VCV_filtered, 
              mods = ~ Assay_type-1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID, 
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db_filtered)


assay_labels <- c(
  "Elevated Plus Maze" = "EPM",
  "Open Field Test" = "OFT",
  "Light-Dark Box" = "LDB",
  "Forced Swim Test (FST)" = "FST",
  "Tail Suspension Test (TST)" = "TST",
  "Sucrose Preference Test (SPT)" = "SPT"
)

orchard_assay <- orchard_plot(
  mAT,legend.pos = "top.left",
  mod   = "Assay_type",
  group = "Study_ID",
  xlab  = expression(lnRR),trunk.size = 0.3,     
              branch.size = 2, alpha = 0.3
) +
  scale_colour_brewer(palette = "Set1") +
  scale_fill_brewer(palette = "Dark2") +
  scale_x_discrete(labels = assay_labels) +scale_y_continuous(limits = c(-4,2),breaks = seq(-4, 2, by = 1),
                                                                    minor_breaks = seq(-4, 2, by = 0.5 ))+
  labs(
    x = NULL,
    y = expression(lnRR),
    subtitle = "Behavioural assay"
  ) +
  theme(
    plot.subtitle = element_text(size = 10, face = "bold"),
    axis.title.y = element_text(face = "bold"),legend.direction = "vertical"
  )

orchard_assay


```

## Lifestage of exposure

```{r}
#| label: model_lifestage_exp_lnRR

mLE <- rma.mv(yi = lnRR,
                  V = VCV, 
              mods = ~ Lifestage_exposure-1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)



lifestage_labels <- c(
  "Adolescent"  = "Adol.",
  "Adult"       = "Adult",
  "Juvenile"    = "Juv.",
  "Young adult" = "Y. adult",
  "Mixed"       = "Mixed",
  "Unclear"     = "Unclear"
)


orchard_lifestage<-orchard_plot(mLE,legend.pos = "top.left",
             mod = "Lifestage_exposure",
              group = "Study_ID",
               xlab = expression(lnRR),trunk.size = 0.3,     
              branch.size = 2,alpha=0.3 ,
             flip = T) +
  scale_x_discrete(labels = lifestage_labels)+
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_x_discrete(labels = assay_labels) +scale_y_continuous(limits = c(-4,2),breaks = seq(-4, 2, by = 1),
                                                                    minor_breaks = seq(-4, 2, by = 0.5 ))+  theme(legend.direction = "vertical")
orchard_lifestage
```

## Control condition

```{r}
#| label: model_control_condition_lnRR

mCC <- rma.mv(yi = lnRR,
                  V = VCV, 
              mods = ~ Control_conditions,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID, 
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)



control_labels <- c(
  "ambiet sound" = "Ambient sound",
  "white noise"  = "White noise"
)
orchard_control<-orchard_plot(mCC,
             mod = "Control_conditions",legend.pos = "top.left",
              group = "Study_ID",
              xlab = expression(lnRR),trunk.size = 0.3,     
              branch.size = 2,alpha=0.3) +
  scale_x_discrete(labels = control_labels) +
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_x_discrete(labels = assay_labels) +scale_y_continuous(limits = c(-4,2),breaks = seq(-4, 2, by = 1),
                                                                    minor_breaks = seq(-4, 2, by = 0.5 ))+  theme(legend.direction = "vertical")
orchard_control
```

## Induced behaviour

```{r}
#| label: model_induced_behaviour_lnRR

mIB <- rma.mv(yi = lnRR,
                  V = VCV, 
              mods = ~ `Induced behaviour`,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID, 
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)


orchard_behaviour<-orchard_plot(mIB,
             mod = "Induced behaviour",legend.pos = "top.left",
              group = "Study_ID",
              xlab = expression(lnRR), trunk.size = 0.3,     
              branch.size = 2,alpha=0.3,
              flip = T)+
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_y_continuous(limits = c(-4,2),breaks = seq(-4, 2, by = 1),
                                                                    minor_breaks = seq(-4, 2, by = 0.5 ))+  theme(legend.direction = "vertical")
orchard_behaviour
```

# Patched figure

```{r}
#| label: patched_figure_lnRR
#| fold: TRUE
orchard_assay     <- orchard_assay     + labs(subtitle = "Behavioral assay")
orchard_lifestage <- orchard_lifestage + labs(subtitle = "Age at exposure")
orchard_control   <- orchard_control   + labs(subtitle = "Control condition")
orchard_behaviour <- orchard_behaviour + labs(subtitle = "Behavioral mechanism")

# 2) Hide legends on all but ONE panel (choose bottom-right here)
orchard_assay     <- orchard_assay     + theme(legend.position = "none")
orchard_lifestage <- orchard_lifestage + theme(legend.position = "none")

orchard_control   <- orchard_control   + theme(legend.position = "none")
# orchard_behaviour keeps the legend

# 3) Patch with your layout
patched_figure <- (orchard_assay + orchard_lifestage  ) /
                 (orchard_control   + orchard_behaviour) +
  plot_annotation(tag_levels = "A") &
  theme(
    plot.tag = element_text(size = 12, face = "bold"),
    plot.subtitle = element_text(size = 10, face = "bold")
  )

# 4) Enforce identical lnRR scale and a single y-axis label across ALL panels
patched_figure <- patched_figure &
  scale_y_continuous(
    limits = c(-4, 2),                       
    breaks = seq(-4, 2, by = 1),
    minor_breaks = seq(-4, 2, by = 0.5)
  ) &
  labs(y = expression(lnRR), x = NULL)


```
::: card
```{r}
#| echo: false
patched_figure
```
:::



```{r}
#| label: save-figure_behaviour_lnRR
#| eval: false
#| fold: TRUE
ggsave(filename = here("..","Plots", "patched_lnRR.pdf"),
       plot = patched_figure,
       dpi = 300, device = cairo_pdf,width = 10,  
       height = 10)

ggsave(filename = here("..","Plots", "patched_lnRR.jpg"),
       plot = patched_figure,
       width = 10,  
       height = 10,  
       dpi = 300)

```

# lnVR

```{r}
db <- readr::read_csv(here("..","data","db_effect_sizes.csv")) %>%
  mutate(across(c(C_n, Ex_n, C_mean, Ex_mean, C_SE, Ex_SE, C_SD, Ex_SD, lnVR, lnVR_var), as.numeric)) %>%
  mutate(across(where(is.character), as.factor)) %>% filter(!is.na(lnVR),  !is.na(lnVR_var))


# CALCULATE VCV USING lnVR VARIANCE
VCV <- metafor::vcalc(
  vi = lnVR_var,       
  cluster = Cohort_ID, # The clustering variable (Study_ID + Ex_ID)
  obs = ES_ID,         # The unique observation/effect size ID
  rho = 0.5,           # Assumed correlation between outcomes from the same cohort
  data = db 
)
```

## Overall effect

```{r}
#| label: model_overall_lnVR

mOV <- rma.mv(yi = lnVR, 
                  V = VCV, 
              mods = ~ 1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)


orchard_overalllnVR<-orchard_plot(mOV,
               group = "Study_ID",
               xlab = "lnVR", 
              flip = T,trunk.size = 0.3,     
              branch.size = 2, alpha = 0.3) + 
              scale_x_discrete(labels = c("Overall effect"))+
            scale_colour_brewer(palette = "Dark2") +
            scale_fill_brewer(palette = "Paired")+scale_y_continuous(limits = c(-4,11),breaks = seq(-4, 10, by = 2),
                                                                    minor_breaks = seq(-4, 11, by = 1 ))
orchard_overalllnVR
```

## Outcome type

```{r}
#| label: model_outcome_type_lnVR


f <- filter_low_k(db, VCV,Outcome_type, min_k = 5)
db_filtered <- f$data
VCV_filtered <- f$V

mOT2 <- rma.mv(yi = lnVR, 
                  V = VCV_filtered, 
              mods = ~ Outcome_type,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db_filtered)



orchard_outcome_lnVR<-orchard_plot(mOT2,
              mod = "Outcome_type",
               group = "Study_ID",
               xlab = "lnVR", 
              flip = T,trunk.size = 0.3,alpha = 0.3,     
              branch.size = 2) +
            scale_colour_brewer(palette = "Dark2") +
            scale_fill_brewer(palette = "Set1")+scale_y_continuous(limits = c(-4,11),breaks = seq(-4, 10, by = 2),
                                                                    minor_breaks = seq(-4, 11, by = 1 ))
orchard_outcome_lnVR
```

## Assay type

```{r}
#| label: filter_assay_type_lnVR
f <- filter_low_k(db, VCV, Assay_type, min_k = 5)
db_filtered <- f$data
VCV_filtered <- f$V


mAT <- rma.mv(yi = lnVR,
                  V = VCV_filtered, 
              mods = ~ Assay_type-1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID, 
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db_filtered)


assay_labels <- c(
  "Elevated Plus Maze" = "EPM",
  "Open Field Test" = "OFT",
  "Light-Dark Box" = "LDB",
  "Forced Swim Test (FST)" = "FST",
  "Tail Suspension Test (TST)" = "TST",
  "Sucrose Preference Test (SPT)" = "SPT"
)

orchard_assay <- orchard_plot(
  mAT,legend.pos = "top.left",
  mod   = "Assay_type",
  group = "Study_ID",
  xlab  = expression(lnVR) ,trunk.size = 0.3,     
              branch.size = 2,alpha = 0.3
) +
  scale_colour_brewer(palette = "Set1") +
  scale_fill_brewer(palette = "Dark2") +
  scale_x_discrete(labels = assay_labels)+
  scale_y_continuous(limits = c(-4,11),breaks = seq(-4, 11, by = 2),
                     minor_breaks = seq(-4, 11, by = 1 ))+  
  labs(
    x = NULL,
    y = expression(lnVR),
    subtitle = "Behavioural assay"
  ) +
  theme(
    plot.subtitle = element_text(size = 10, face = "bold"),
    axis.title.y = element_text(face = "bold"),legend.direction = "vertical")
  

orchard_assay


```

## Lifestage of exposure

```{r}
#| label: model_lifestage_exp_lnVR

mLE <- rma.mv(yi = lnVR, 
                  V = VCV, 
              mods = ~ Lifestage_exposure-1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)



lifestage_labels <- c(
  "Adolescent"  = "Adol.",
  "Adult"       = "Adult",
  "Juvenile"    = "Juv.",
  "Young adult" = "Y. adult",
  "Mixed"       = "Mixed",
  "Unclear"     = "Unclear"
)


orchard_lifestage<-orchard_plot(mLE,legend.pos = "top.left",
              mod = "Lifestage_exposure",
               group = "Study_ID",
                xlab = expression(lnVR), trunk.size = 0.3,     
              branch.size = 2 ,alpha = 0.3
              ) + scale_x_discrete(labels = lifestage_labels)+
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_y_continuous(limits=c(-4,11),breaks = seq(-4, 11, by = 2),
                                                                    minor_breaks = seq(-4, 11, by = 1 ))+ 
  theme(legend.direction = "vertical")
orchard_lifestage
```

## Music genre

Meta_genre

```{r}
#| label: model_music_genre_lnVR

mMG <- rma.mv(yi = lnVR,
                  V = VCV, 
              mods = ~ Meta_genre-1 ,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID,
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)



genre_labels <- c(
  "Mixed"  = "Mixed",
  "Popular Contemporary Music"       = "Contemporary",
  "Traditional Music / Folk / World"    = "World",
  "Western Art Music / Orchestral" = "Orchestral"
)





orchard_genre<-orchard_plot(mMG,legend.pos = "top.left",
             mod = "Meta_genre",
              group = "Study_ID",
             xlab = "lnVR", 
             flip = T,trunk.size = 0.3,     
              branch.size = 2,alpha = 0.3) +  
  scale_x_discrete(labels = genre_labels)+
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_y_continuous(limits=c(-4,11),breaks = seq(-4, 11, by = 2),
                                                                    minor_breaks = seq(-4, 11, by = 1 ))+ 
  theme(legend.direction = "vertical")
orchard_genre
```

## Experimental design

```{r}
#| label: model_experimental_design_lnVR

mEXD <- rma.mv(yi = lnVR,
                  V = VCV, 
              mods = ~ Experimental_design-1,
                  random = list(~1 | Study_ID,
                                ~1 | ES_ID, 
                                ~1 | Strain),
                  test = "t",
                  method = "REML", 
                  sparse = TRUE,
                  data = db)



design_labels<-c(
  "Factorial"="Factorial",
  "Posttest-Only Control Group"="Posttest",
  "Repeated Measures"="Repeated"
  
)


orchard_design<-orchard_plot(mEXD,legend.pos = "top.left",
             mod = "Experimental_design",
              group = "Study_ID",
              xlab = expression(lnVR), 
              flip=T,
             trunk.size = 0.3,     
              branch.size = 2,alpha = 0.3 ) +
  scale_x_discrete(labels = design_labels) +
            scale_colour_brewer(palette = "Set1") +
            scale_fill_brewer(palette = "Dark2")+scale_y_continuous(limits = c(-4,11),breaks = seq(-4, 11, by = 2),
                                                                    minor_breaks = seq(-4, 11, by = 1 ))+ 
  theme(legend.direction = "vertical")
orchard_design
```

```{r}
#| label: patched_figure_lnVR
#| fold: TRUE
orchard_assay     <- orchard_assay     + labs(subtitle = "Behavioral assay")
orchard_lifestage <- orchard_lifestage + labs(subtitle = "Age at exposure")

orchard_genre   <- orchard_genre  + labs(subtitle = "Music meta-genre")
orchard_design <-orchard_design + labs(subtitle = "Experimental design")




# 2) Hide legends on all but ONE panel 
orchard_assay    <- orchard_assay    + theme(legend.position = "none")
orchard_lifestage <- orchard_lifestage + theme(legend.position = "none")
orchard_genre   <- orchard_genre+ theme(legend.position = "none")

# orchard_behaviour keeps the legend

# 3) Patch with your layout
patched_figure <- ( orchard_assay+orchard_lifestage) /
                 (orchard_genre + orchard_design) +
  plot_annotation(tag_levels = "A") &
  theme(
    plot.tag = element_text(size = 12, face = "bold"),
    plot.subtitle = element_text(size = 10, face = "bold")
   )
  

# 4) Enforce identical lnRR scale and a single y-axis label across ALL panels
patched_figure <- patched_figure &
  scale_y_continuous(
    limits = c(-4, 11),                       
    breaks = seq(-4, 11, by = 2),
    minor_breaks = seq(-4, 11, by = 1)
  ) &
  labs(y = expression(lnVR), x = NULL)



```

::: card
```{r}
#| echo: false
patched_figure
```
:::


```{r}
#| label: save-figure_patched_lnVR
#| eval: false
#| fold: TRUE
ggsave(filename = here("..","Plots", "patched_lnVR.pdf"),
       plot = patched_figure,
       dpi = 300, device = cairo_pdf,width = 10,  
       height = 10)

ggsave(filename = here("..","Plots", "patched_lnVR.jpg"),
       plot = patched_figure,
       width = 10,  
       height = 10,  
       dpi = 300)

```

# Overall effects figure

```{r}
fig2<-(overall_lnRR/orchard_overalllnVR)

fig2<-fig2+ 
  plot_annotation(
    title = '',
    tag_levels = 'A'
  )

```


::: card
```{r}
#| echo: false
fig2
```
:::








```{r}
#| label: save-figure_overall
#| eval: false

ggsave(filename = here("..","Plots", "overall_effects.pdf"),
       plot = fig2,
       dpi = 300, device = cairo_pdf)

ggsave(filename = here("..","Plots", "overall_effects.jpg"),
       plot = fig2,
       width = 7,  
       height = 9,  
       dpi = 300)

```

# Outcome type figure

```{r}
fig3<-(orchard_outcomelnRR/orchard_outcome_lnVR)

fig3<-fig3+ 
  plot_annotation(
    title = '',
    tag_levels = 'A'
  )

```




::: card
```{r}
#| echo: false
fig3
```
:::



```{r}
#| label: save-figure_outcomes
#| eval: false

ggsave(filename = here("..","Plots", "outcome_effects.pdf"),
       plot = fig3,
       dpi = 300, device = cairo_pdf)

ggsave(filename = here("..","Plots", "outcome_effects.jpg"),
       plot = fig3,
       width = 7,  
       height = 9,  
       dpi = 300)

```