## ----include = FALSE----------------------------------------------------------
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
if (!"package:drmTMB" %in% search()) {
  library(drmTMB)
}

## -----------------------------------------------------------------------------
set.seed(101)

K <- 30
mu_true <- 0.40 # pooled effect
tau_true <- 0.30 # between-study SD

# Known sampling variances: larger studies (smaller vi) and smaller studies.
vi <- runif(K, 0.02, 0.10)

# True study effects scatter around mu_true with SD tau_true.
theta_i <- rnorm(K, mean = mu_true, sd = tau_true)

# Observed effect sizes: each true effect seen with its known sampling error.
yi <- rnorm(K, mean = theta_i, sd = sqrt(vi))

dat <- data.frame(study = factor(seq_len(K)), yi = yi, vi = vi)
head(dat)

## -----------------------------------------------------------------------------
fit <- drmTMB(
  bf(yi ~ 1 + meta_V(V = vi), sigma ~ 1),
  family = gaussian(),
  data = dat
)

summary(fit)

## -----------------------------------------------------------------------------
is_converged(fit)                 # optimizer convergence
is_converged(fit, include_hessian = TRUE) # also requires a positive-definite Hessian

## -----------------------------------------------------------------------------
diagnostics <- check_drm(fit)
diagnostics[, c("check", "status", "value", "message")]

## -----------------------------------------------------------------------------
mu_hat <- coef(fit, "mu")[["(Intercept)"]]
mu_hat

confint(fit, parm = "mu:(Intercept)")[, c("parm", "lower", "upper")]

## -----------------------------------------------------------------------------
tau_hat <- sigma(fit)[1]
c(tau = unname(tau_hat), tau_squared = unname(tau_hat^2))

## -----------------------------------------------------------------------------
w <- 1 / dat$vi
v_typical <- ((K - 1) * sum(w)) / (sum(w)^2 - sum(w^2))
I2 <- tau_hat^2 / (tau_hat^2 + v_typical)

c(
  tau_squared = unname(tau_hat^2),
  typical_v = v_typical,
  I2_percent = unname(100 * I2)
)

## -----------------------------------------------------------------------------
if (requireNamespace("metafor", quietly = TRUE)) {
  rma_fit <- metafor::rma(yi = yi, vi = vi, method = "ML", data = dat)

  comparison <- data.frame(
    quantity = c("pooled mu", "tau^2", "I^2 (%)"),
    drmTMB = c(mu_hat, tau_hat^2, 100 * I2),
    metafor = c(as.numeric(rma_fit$beta), rma_fit$tau2, rma_fit$I2)
  )
  print(comparison, row.names = FALSE, digits = 4)
}

## -----------------------------------------------------------------------------
fit_reml <- drmTMB(
  bf(yi ~ 1 + meta_V(V = vi), sigma ~ 1),
  family = gaussian(),
  data = dat,
  REML = TRUE
)

data.frame(
  estimator = c("ML", "REML"),
  pooled_mu = c(coef(fit, "mu")[[1]], coef(fit_reml, "mu")[[1]]),
  tau = c(sigma(fit)[1], sigma(fit_reml)[1]),
  tau_squared = c(sigma(fit)[1]^2, sigma(fit_reml)[1]^2)
)

## -----------------------------------------------------------------------------
set.seed(202)
dat$dose <- scale(runif(K, 1, 10))[, 1] # a study-level moderator
# Give the effect size a genuine dependence on the moderator.
dat$yi <- dat$yi + 0.25 * dat$dose

fit_mr <- drmTMB(
  bf(yi ~ 1 + dose + meta_V(V = vi), sigma ~ 1),
  family = gaussian(),
  data = dat
)

coef(fit_mr, "mu")

## -----------------------------------------------------------------------------
c(
  residual_tau_no_moderator = unname(sigma(fit)[1]),
  residual_tau_with_moderator = unname(sigma(fit_mr)[1])
)

## ----layered-meta-syntax, eval=FALSE------------------------------------------
# # Study-level location-SD regression (LSS): z_study is constant within study.
# drmTMB(
#   bf(
#     yi ~ x + (1 | study) + meta_V(V = V),
#     sigma ~ z,
#     sd(study) ~ z_study
#   ),
#   family = gaussian(), data = dat
# )
# 
# # Nested effect-level location-SD regression (LSSS): effect is nested in study
# # and has repeated rows.
# drmTMB(
#   bf(
#     yi ~ x + (1 | study) + (1 | effect) + meta_V(V = V),
#     sigma ~ z,
#     sd(study) ~ z_study,
#     sd(effect) ~ z_effect
#   ),
#   family = gaussian(), data = dat
# )

## -----------------------------------------------------------------------------
set.seed(303)
n_dense <- 8
dat_dense <- data.frame(
  yi = 0.25 + 0.10 * seq_len(n_dense) + stats::rnorm(n_dense, sd = 0.04),
  x = seq_len(n_dense)
)
V_dense <- 0.012 * outer(
  seq_len(n_dense), seq_len(n_dense),
  function(i, j) 0.55^abs(i - j)
)

# A useful preflight: V is numeric, n by n, symmetric, and PSD.
stopifnot(
  is.numeric(V_dense),
  identical(dim(V_dense), c(nrow(dat_dense), nrow(dat_dense))),
  isTRUE(all.equal(V_dense, t(V_dense))),
  min(eigen(V_dense, symmetric = TRUE, only.values = TRUE)$values) >= 0
)

fit_dense <- drmTMB(
  bf(yi ~ x + meta_V(V = V_dense), sigma ~ 1),
  family = gaussian(),
  data = dat_dense
)
check_drm(fit_dense)

