## ----setup--------------------------------------------------------------------
#| include: false
library(forrest)


## ----sim-data-----------------------------------------------------------------
set.seed(2024)
n_studies <- 10

meta_dat <- data.frame(
  study    = c(
    "Anderson et al. (2014)",
    "Bauer et al. (2015)",
    "Chen et al. (2016)",
    "Di et al. (2017)",
    "Evans et al. (2018)",
    "Fuentes et al. (2019)",
    "Garcia et al. (2020)",
    "Hart et al. (2021)",
    "Ibrahim et al. (2022)",
    "Jensen et al. (2023)"
  ),
  region   = c(
    "North America", "Europe", "Asia", "North America", "Europe",
    "Latin America", "Europe", "North America", "Asia", "Europe"
  ),
  n        = c(4520, 2810, 8340, 12600, 3950, 1870, 5430, 9100, 6780, 3210),
  events   = c( 612,  390, 1120,  1950,  540,  255,  770, 1380, 980,  450),
  # Log odds ratios and SEs (simulated around a true log-OR of 0.08 per
  # 10 µg/m³ increase in PM2.5)
  log_or   = c(0.062, 0.091, 0.078, 0.095, 0.055, 0.110, 0.083,
               0.072, 0.088, 0.068),
  se       = c(0.028, 0.035, 0.022, 0.019, 0.031, 0.042, 0.026,
               0.021, 0.024, 0.033),
  is_sum   = FALSE
)

# Back-transform to OR scale
meta_dat$or    <- exp(meta_dat$log_or)
meta_dat$lower <- exp(meta_dat$log_or - 1.96 * meta_dat$se)
meta_dat$upper <- exp(meta_dat$log_or + 1.96 * meta_dat$se)

# Inverse-variance weights for the fixed-effect pooled estimate
meta_dat$weight <- 1 / meta_dat$se^2

# Fixed-effect pooled log-OR and SE
pooled_log_or <- with(meta_dat,
  sum(weight * log_or) / sum(weight)
)
pooled_se     <- sqrt(1 / sum(meta_dat$weight))

# Append pooled row
pooled_row <- data.frame(
  study    = "Pooled (fixed-effect)",
  region   = NA_character_,
  n        = sum(meta_dat$n),
  events   = sum(meta_dat$events),
  log_or   = pooled_log_or,
  se       = pooled_se,
  or       = exp(pooled_log_or),
  lower    = exp(pooled_log_or - 1.96 * pooled_se),
  upper    = exp(pooled_log_or + 1.96 * pooled_se),
  weight   = NA_real_,
  is_sum   = TRUE
)
meta_all <- rbind(meta_dat, pooled_row)

# Formatted text for the right-hand table columns
meta_all$or_text <- ifelse(
  meta_all$is_sum,
  sprintf("%.2f (%.2f\u2013%.2f)", meta_all$or, meta_all$lower, meta_all$upper),
  sprintf("%.2f (%.2f\u2013%.2f)", meta_all$or, meta_all$lower, meta_all$upper)
)
meta_all$n_text      <- formatC(meta_all$n,      format = "d", big.mark = ",")
meta_all$events_text <- formatC(meta_all$events, format = "d", big.mark = ",")


## ----basic-meta---------------------------------------------------------------
forrest(
  meta_all,
  estimate   = "or",
  lower      = "lower",
  upper      = "upper",
  label      = "study",
  is_summary = "is_sum",
  weight     = "weight",
  log_scale  = TRUE,
  ref_line   = 1,
  xlab       = "OR for hypertension per 10 \u03bcg/m\u00b3 PM2.5 (95% CI)"
)


## ----meta-cols----------------------------------------------------------------
#| fig-width: 11
forrest(
  meta_all,
  estimate   = "or",
  lower      = "lower",
  upper      = "upper",
  label      = "study",
  is_summary = "is_sum",
  weight     = "weight",
  log_scale  = TRUE,
  ref_line   = 1,
  header     = "Study",
  cols       = c(
    "N"            = "n_text",
    "Events"       = "events_text",
    "OR (95% CI)"  = "or_text"
  ),
  widths     = c(3.5, 3.5, 1.2, 1.2, 2.2),
  xlab       = "OR for hypertension per 10 \u03bcg/m\u00b3 PM2.5 (95% CI)"
)


## ----meta-region--------------------------------------------------------------
#| fig-height: 6
# Exclude pooled row for the subgroup plot; add region-level pooled rows
study_dat <- meta_all[!meta_all$is_sum, ]

forrest(
  study_dat,
  estimate   = "or",
  lower      = "lower",
  upper      = "upper",
  label      = "study",
  group      = "region",
  log_scale  = TRUE,
  ref_line   = 1,
  legend_pos = "topright",
  xlab       = "OR for hypertension per 10 \u03bcg/m\u00b3 PM2.5 (95% CI)"
)


## ----meta-structured----------------------------------------------------------
#| fig-height: 10
# Helper: compute fixed-effect pooled row for a subset
pool_region <- function(dat, region_label) {
  w   <- 1 / dat$se^2
  lp  <- sum(w * dat$log_or) / sum(w)
  se  <- sqrt(1 / sum(w))
  data.frame(
    study    = sprintf("%s (pooled)", region_label),
    region   = region_label,
    n        = sum(dat$n),
    events   = sum(dat$events),
    log_or   = lp,
    se       = se,
    or       = exp(lp),
    lower    = exp(lp - 1.96 * se),
    upper    = exp(lp + 1.96 * se),
    weight   = sum(w),
    is_sum   = TRUE
  )
}

# Build data: just the study rows and region-level pooled rows.
# No manual header or spacer rows needed.
regions    <- c("Asia", "Europe", "Latin America", "North America")
structured <- do.call(rbind, lapply(regions, function(r) {
  sub <- meta_dat[meta_dat$region == r, ]
  rbind(sub, pool_region(sub, r))
}))

structured$or_text <- sprintf(
  "%.2f (%.2f\u2013%.2f)",
  structured$or, structured$lower, structured$upper
)

forrest(
  structured,
  estimate   = "or",
  lower      = "lower",
  upper      = "upper",
  label      = "study",
  section    = "region",
  is_summary = "is_sum",
  weight     = "weight",
  log_scale  = TRUE,
  ref_line   = 1,
  header     = "Study",
  cols       = c("OR (95% CI)" = "or_text"),
  widths     = c(4, 3.5, 2.5),
  stripe     = TRUE,
  xlab       = "OR for hypertension per 10 \u03bcg/m\u00b3 PM2.5 (95% CI)"
)


## ----meta-multi-exposure------------------------------------------------------
#| fig-height: 7
#| fig-width: 10
# Simulate effect estimates for three air-quality exposures across 6 studies
set.seed(99)
studies6 <- c("Anderson (2018)", "Bauer (2019)", "Chen (2020)",
               "Di (2021)", "Evans (2022)", "Fuentes (2023)")

make_exp <- function(true_lor, n = 6) {
  se  <- runif(n, 0.025, 0.045)
  lor <- rnorm(n, true_lor, 0.02)
  data.frame(
    or    = exp(lor),
    lower = exp(lor - 1.96 * se),
    upper = exp(lor + 1.96 * se)
  )
}

pm25  <- make_exp(0.08)
no2   <- make_exp(0.05)
noise <- make_exp(0.03)

multi_exp <- data.frame(
  study    = rep(studies6, 3),
  exposure = rep(c("PM2.5", "NO2", "Noise"), each = 6),
  or       = c(pm25$or,    no2$or,    noise$or),
  lower    = c(pm25$lower, no2$lower, noise$lower),
  upper    = c(pm25$upper, no2$upper, noise$upper)
)

# Formatted OR text per row
multi_exp$or_text <- sprintf("%.2f (%.2f\u2013%.2f)",
                              multi_exp$or,
                              multi_exp$lower,
                              multi_exp$upper)

forrest(
  multi_exp,
  estimate   = "or",
  lower      = "lower",
  upper      = "upper",
  label      = "study",
  group      = "exposure",
  dodge      = TRUE,
  log_scale  = TRUE,
  ref_line   = 1,
  legend_pos = "topright",
  header     = "Study",
  xlab       = "OR for hypertension (95% CI)"
)


## ----save---------------------------------------------------------------------
#| eval: false
# save_forrest(
#   file   = "meta_analysis_forest.pdf",
#   plot   = function() {
#     forrest(
#       meta_all,
#       estimate   = "or",
#       lower      = "lower",
#       upper      = "upper",
#       label      = "study",
#       is_summary = "is_sum",
#       weight     = "weight",
#       log_scale  = TRUE,
#       ref_line   = 1,
#       xlab       = "OR for hypertension per 10 \u03bcg/m\u00b3 PM2.5 (95% CI)"
#     )
#   },
#   width  = 10,
#   height = 6
# )

