tests/testthat/test-calc-stem.R

box::use(
  testthat[
    expect_equal,
    expect_true,
    test_that
  ],
  artma / calc / methods / stem[
    weighted_mean,
    weighted_mean_squared,
    variance_b,
    variance_0,
    stem
  ]
)

# weighted_mean -------------------------------------------------------------

test_that("weighted_mean is the running inverse-variance weighted mean", {
  # Equal weights (se = 1, sigma = 0): cumulative arithmetic mean.
  expect_equal(weighted_mean(c(1, 2, 3), c(1, 1, 1), 0), c(1, 1.5, 2))
})

test_that("weighted_mean honours unequal precisions", {
  beta <- c(1, 3)
  se <- c(1, 2)
  sigma <- 0
  weights <- 1 / se^2
  expected <- c(beta[1], sum(beta * weights) / sum(weights))
  expect_equal(weighted_mean(beta, se, sigma), expected)
})

# variance_b ----------------------------------------------------------------

test_that("variance_b is the reciprocal cumulative precision", {
  expect_equal(variance_b(c(1, 1, 1), 0), 1 / cumsum(c(1, 1, 1)))
  expect_equal(variance_b(c(1, 1, 1), 0), c(1, 0.5, 1 / 3))
})

# variance_0 ----------------------------------------------------------------

test_that("variance_0 returns zero when dispersion matches sampling error", {
  # sum(w * (beta - mean)^2) = q - 1 degrees of freedom cancels the numerator.
  expect_equal(variance_0(3, c(1, 2, 3), c(1, 1, 1), 2), 0)
})

test_that("variance_0 matches its closed form with excess dispersion", {
  beta <- c(0, 2, 4)
  se <- c(1, 1, 1)
  mean_val <- 2
  # weights = 1; numerator = sum((beta-mean)^2) - (n-1) = 8 - 2 = 6
  # denominator = sum(w) - sum(w^2)/sum(w) = 3 - 1 = 2 -> 3
  expect_equal(variance_0(3, beta, se, mean_val), 3)
})

# weighted_mean_squared ------------------------------------------------------

test_that("weighted_mean_squared matches the outer-product reference", {
  set.seed(7)
  n <- 25
  se <- runif(n, 0.05, 0.5)
  beta <- 0.3 + rnorm(n, 0, se)
  sigma <- 0.1

  weights <- 1 / (se^2 + sigma^2)
  weighted_beta <- beta * weights
  submatrix_sums <- function(m) {
    vapply(seq_len(nrow(m)), function(i) sum(m[1:i, 1:i]), numeric(1))
  }
  numerator <- submatrix_sums(outer(weighted_beta, weighted_beta)) - cumsum(weighted_beta^2)
  denominator <- submatrix_sums(outer(weights, weights)) - cumsum(weights^2)
  expected <- numerator / denominator
  expected[1] <- 0

  expect_equal(weighted_mean_squared(beta, se, sigma), expected)
})

# stem ----------------------------------------------------------------------

test_that("stem recovers a homogeneous true effect", {
  set.seed(5)
  n <- 40
  se <- runif(n, 0.05, 0.5)
  effect <- 0.3 + rnorm(n, 0, se)

  out <- stem(effect, se, c(1e-4, 100))

  expect_true(is.matrix(out$estimates))
  expect_equal(unname(out$estimates[1, "estimate"]), 0.3, tolerance = 0.1)
  expect_true(out$estimates[1, "se"] > 0)
  # The stem uses a subset of at least the three most precise studies.
  expect_true(out$estimates[1, "n_stem"] >= 3)
  expect_true(out$estimates[1, "n_stem"] <= n)
})

test_that("stem is deterministic for identical input", {
  set.seed(5)
  n <- 40
  se <- runif(n, 0.05, 0.5)
  effect <- 0.3 + rnorm(n, 0, se)

  expect_equal(stem(effect, se, c(1e-4, 100)), stem(effect, se, c(1e-4, 100)))
})

test_that("stem MSE table rows are internally aligned", {
  set.seed(5)
  n <- 40
  se <- runif(n, 0.05, 0.5)
  effect <- 0.3 + rnorm(n, 0, se)

  out <- stem(effect, se, c(1e-4, 100))
  mse_table <- out$MSE

  # One row per candidate stem size (n_stem = 3..n), no trailing NA row, and
  # bias_squared must be the same row's MSE minus variance. The previous code
  # shifted bias_squared one row up.
  expect_equal(nrow(mse_table), n - 2)
  expect_true(all(is.finite(mse_table)))
  expect_equal(
    unname(mse_table[, "bias_squared"]),
    unname(mse_table[, "MSE"] - mse_table[, "variance"])
  )
})

Try the artma package in your browser

Any scripts or data that you put into this service are public.

artma documentation built on Oct. 27, 2026, 9:06 a.m.