Nothing
# Binomial risk-difference / relative-risk models (binomial.rrw)
## ---------------------------------------------------------------------------
## Identical()
## ---------------------------------------------------------------------------
test_that("Identical: numerical equality with default tolerance", {
testthat::expect_true(Identical(1, 1))
testthat::expect_true(Identical(1 + 1e-12, 1))
testthat::expect_false(Identical(1 + 1e-3, 1))
## vector input
res <- Identical(c(1, 2, 1 + 1e-12), 1)
testthat::expect_equal(res, c(TRUE, FALSE, TRUE))
## custom tolerance
testthat::expect_true(Identical(1.001, 1, tolerance = 1e-2))
})
## ---------------------------------------------------------------------------
## RD_OP: round-trip check
## ---------------------------------------------------------------------------
test_that("RD_OP recovers (p0, p1) from (rd, op)", {
RD_OP <- lava:::RD_OP
p0 <- c(0.2, 0.4, 0.6)
p1 <- c(0.3, 0.5, 0.8)
rd <- p1 - p0
## op is the odds *product*: (p0/(1-p0)) * (p1/(1-p1))
op <- (p0 / (1 - p0)) * (p1 / (1 - p1))
out <- RD_OP(rd, op)
testthat::expect_equal(dim(out), c(3L, 2L))
testthat::expect_equal(out[, 1], p0, tolerance = 1e-8)
testthat::expect_equal(out[, 2], p1, tolerance = 1e-8)
})
test_that("RD_OP handles op == 1 (no association) branch", {
RD_OP <- lava:::RD_OP
rd <- c(0.1, -0.2)
op <- c(1, 1)
out <- RD_OP(rd, op)
## When op == 1: p0 = 0.5*(1 - rd), p1 = p0 + rd
testthat::expect_equal(out[, 1], 0.5 * (1 - rd), tolerance = 1e-10)
testthat::expect_equal(out[, 2], 0.5 * (1 - rd) + rd, tolerance = 1e-10)
testthat::expect_true(all(is.finite(out)))
})
## ---------------------------------------------------------------------------
## RR_OP: round-trip check
## ---------------------------------------------------------------------------
test_that("RR_OP recovers (p0, p1) from (rr, op)", {
RR_OP <- lava:::RR_OP
p0 <- c(0.2, 0.3, 0.5)
p1 <- c(0.3, 0.45, 0.6)
rr <- p1 / p0
## op is the odds *product*: (p0/(1-p0)) * (p1/(1-p1))
op <- (p0 / (1 - p0)) * (p1 / (1 - p1))
out <- RR_OP(rr, op)
testthat::expect_equal(dim(out), c(3L, 2L))
testthat::expect_equal(out[, 1], p0, tolerance = 1e-8)
testthat::expect_equal(out[, 2], p1, tolerance = 1e-8)
})
test_that("RR_OP handles op == 1 (no association) branch", {
RR_OP <- lava:::RR_OP
rr <- c(1.5, 2)
op <- c(1, 1)
out <- RR_OP(rr, op)
## When op == 1: p0 = 1/(1+rr), p1 = p0 * rr
testthat::expect_equal(out[, 1], 1 / (1 + rr), tolerance = 1e-10)
testthat::expect_equal(out[, 2], rr / (1 + rr), tolerance = 1e-10)
testthat::expect_true(all(is.finite(out)))
})
## ---------------------------------------------------------------------------
## binomial.rd model modifier
## ---------------------------------------------------------------------------
test_that("binomial.rd installs multiple-input simulation node", {
m <- lvm()
regression(m) <- z ~ x
regression(m) <- lp ~ x
regression(m) <- op ~ x
m <- binomial.rd(m, response = "y", exposure = "z",
target.model = "lp", nuisance.model = "op")
mi <- m$attributes$multiple.inputs
testthat::expect_true(!is.null(mi))
testthat::expect_true("y" %in% names(mi))
testthat::expect_equal(mi$y$input, c("z", "lp", "op"))
testthat::expect_true(grepl("risk-difference", mi$y$type, fixed = TRUE))
## exposure should now have a binomial distribution attached
testthat::expect_false(is.null(distribution(m)[["z"]]))
})
test_that("binomial.rd accepts formula interface", {
m1 <- lvm()
regression(m1) <- z ~ x
regression(m1) <- lp ~ x
regression(m1) <- op ~ x
m1 <- binomial.rd(m1, response = "y", exposure = "z",
target.model = "lp", nuisance.model = "op")
m2 <- lvm()
regression(m2) <- z ~ x
regression(m2) <- lp ~ x
regression(m2) <- op ~ x
m2 <- binomial.rd(m2, y ~ z | lp | op)
testthat::expect_equal(m1$attributes$multiple.inputs$y$input,
m2$attributes$multiple.inputs$y$input)
testthat::expect_equal(m1$attributes$multiple.inputs$y$type,
m2$attributes$multiple.inputs$y$type)
})
## ---------------------------------------------------------------------------
## binomial.rr model modifier
## ---------------------------------------------------------------------------
test_that("binomial.rr installs multiple-input simulation node", {
m <- lvm()
regression(m) <- z ~ x
regression(m) <- lp ~ x
regression(m) <- op ~ x
m <- binomial.rr(m, response = "y", exposure = "z",
target.model = "lp", nuisance.model = "op")
mi <- m$attributes$multiple.inputs
testthat::expect_true("y" %in% names(mi))
testthat::expect_equal(mi$y$input, c("z", "lp", "op"))
testthat::expect_true(grepl("relative-risk", mi$y$type, fixed = TRUE))
})
## ---------------------------------------------------------------------------
## End-to-end simulation: numerical correctness
## ---------------------------------------------------------------------------
test_that("binomial.rd simulation produces target risk difference", {
testthat::skip_on_cran()
## Build model where lp is constant (intercept-only) so the implied RD
## is tanh(lp_const). We set lp via constrain().
m <- lvm()
regression(m) <- z ~ x
distribution(m, ~x) <- normal.lvm()
distribution(m, ~lp) <- normal.lvm(mean = 0, sd = 0) ## degenerate -> 0
distribution(m, ~op) <- normal.lvm(mean = 0, sd = 0) ## op = exp(0) = 1
intercept(m, ~lp) <- 0.4 ## constant linear predictor for RD
intercept(m, ~op) <- 0 ## odds product = 1
m <- binomial.rd(m, response = "y", exposure = "z",
target.model = "lp", nuisance.model = "op")
set.seed(123)
d <- sim(m, n = 8000)
testthat::expect_true(all(d$y %in% c(0, 1)))
rd_emp <- mean(d$y[d$z == 1]) - mean(d$y[d$z == 0])
target <- tanh(0.4)
## SE roughly sqrt(p*(1-p)/n_per_group) ~ 0.01; allow generous 3x
testthat::expect_lt(abs(rd_emp - target), 0.04)
})
test_that("binomial.rr simulation produces target relative risk", {
testthat::skip_on_cran()
m <- lvm()
regression(m) <- z ~ x
distribution(m, ~x) <- normal.lvm()
distribution(m, ~lp) <- normal.lvm(mean = 0, sd = 0)
distribution(m, ~op) <- normal.lvm(mean = 0, sd = 0)
intercept(m, ~lp) <- log(1.5) ## constant log-RR
intercept(m, ~op) <- 0
m <- binomial.rr(m, response = "y", exposure = "z",
target.model = "lp", nuisance.model = "op")
set.seed(456)
d <- sim(m, n = 8000)
testthat::expect_true(all(d$y %in% c(0, 1)))
p1 <- mean(d$y[d$z == 1])
p0 <- mean(d$y[d$z == 0])
rr_emp <- p1 / p0
testthat::expect_lt(abs(log(rr_emp) - log(1.5)), 0.10)
})
## ---------------------------------------------------------------------------
## Bonus: riskcomp (lives in R/assoc.R but logically related)
## ---------------------------------------------------------------------------
test_that("riskcomp computes risk difference / risk ratio for two probabilities", {
p <- c(0.4, 0.6)
## Default op="/", type=1: val[1]/val[2]
testthat::expect_equal(riskcomp(p[1], p[2]), p[1] / p[2])
## Explicit Diff (op="-")
testthat::expect_equal(Diff(p[1], p[2]), p[1] - p[2])
## Explicit Ratio (op="/")
testthat::expect_equal(Ratio(p[1], p[2]), p[1] / p[2])
## type=2 swaps operand order
testthat::expect_equal(riskcomp(p[1], p[2], type = 2), p[2] / p[1])
})
test_that("riskcomp with scale=odds gives odds ratio", {
p <- c(0.4, 0.6)
o <- p / (1 - p)
testthat::expect_equal(riskcomp(p[1], p[2], scale = odds), o[1] / o[2])
## type=2 swaps order
testthat::expect_equal(riskcomp(p[1], p[2], scale = odds, type = 2),
o[2] / o[1])
})
## ---------------------------------------------------------------------------
## Regression: simulate_binomial_* errors clearly on out-of-range probabilities
## ---------------------------------------------------------------------------
test_that("simulate_binomial_rd errors when implied probability is out of [0,1]", {
sim_rd <- lava:::simulate_binomial_rd
## NaN linear predictor propagates to NaN probabilities -> guard triggers
data <- data.frame(z = c(0, 1), lp = c(NaN, NaN), op = c(0, 0))
testthat::expect_error(sim_rd(NULL, data, inputs = c("z", "lp", "op")),
regexp = "outside")
})
test_that("simulate_binomial_rr errors when implied probability is out of [0,1]", {
sim_rr <- lava:::simulate_binomial_rr
data <- data.frame(z = c(0, 1), lp = c(NaN, NaN), op = c(0, 0))
testthat::expect_error(sim_rr(NULL, data, inputs = c("z", "lp", "op")),
regexp = "outside")
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.