Nothing
# tests/testthat/test-bma.R
test_that("bma returns a well-formed object on a tiny model space (K=2)", {
set.seed(123)
# --- tiny data: K=2 regressors, M=2 => MS=2^2=4 models
n <- 12
x1 <- rnorm(n)
x2 <- rnorm(n)
y <- 1 + 0.5 * x1 - 0.25 * x2 + rnorm(n, sd = 0.2)
data <- cbind(y = y, x1 = x1, x2 = x2)
ms <- model_space(data, M = 2, g = "None", HC = FALSE)
out <- bma(ms, EMS = 1, dilution = 0, Narrative = 0, round = 10)
# --- list shape
expect_type(out, "list")
expect_length(out, 15)
# --- extracted scalars
expect_equal(out[[6]], 2) # K
expect_equal(out[[7]], 4) # MS
expect_equal(out[[8]], 1) # EMS we passed
expect_equal(out[[9]], 0) # dilution flag
# --- names of regressors
expect_equal(out[[5]], c("x1", "x2"))
# --- main tables dimensions: (K+1) rows, 7 columns
uniform_table <- out[[1]]
random_table <- out[[2]]
expect_true(is.matrix(uniform_table))
expect_true(is.matrix(random_table))
expect_equal(dim(uniform_table), c(3, 7))
expect_equal(dim(random_table), c(3, 7))
# row/col names present and correct
expect_equal(rownames(uniform_table), c("CONST", "x1", "x2"))
expect_equal(colnames(uniform_table), c("PIP","PM","PSD","PMcon","PSDcon","P(+)","PSC"))
# intercept PIP is NA by construction
expect_true(is.na(uniform_table["CONST","PIP"]))
expect_true(is.na(random_table["CONST","PIP"]))
# PIPs for slopes in [0,1]
expect_true(all(uniform_table[c("x1","x2"),"PIP"] >= 0))
expect_true(all(uniform_table[c("x1","x2"),"PIP"] <= 1))
expect_true(all(random_table[c("x1","x2"),"PIP"] >= 0))
expect_true(all(random_table[c("x1","x2"),"PIP"] <= 1))
# PSC and P(+) are probabilities in [0,1]
expect_true(all(uniform_table[,"PSC"] >= 0 & uniform_table[,"PSC"] <= 1))
expect_true(all(random_table[,"PSC"] >= 0 & random_table[,"PSC"] <= 1))
expect_true(all(uniform_table[,"P(+)"] >= 0 & uniform_table[,"P(+)"] <= 1))
expect_true(all(random_table[,"P(+)"] >= 0 & random_table[,"P(+)"] <= 1))
# --- EBA object has K+1 rows and required columns
eba_object <- out[[3]]
expect_true(is.data.frame(eba_object))
expect_equal(nrow(eba_object), 3)
expect_true(all(c("Variable","Lower bound","Minimum","Mean","Maximum","Upper bound","EBA test","%(+)") %in% names(eba_object)))
# --- pms_table has 2 rows (Binomial, Binomial-beta) and 2 cols
pms_table <- out[[4]]
expect_true(is.matrix(pms_table))
expect_equal(dim(pms_table), c(2, 2))
expect_equal(rownames(pms_table), c("Binomial","Binomial-beta"))
expect_equal(colnames(pms_table), c("Prior model size","Posterior model size"))
# --- for_model_pmp must have 4 columns: prior_u, prior_r, pmp_u, pmp_r
for_model_pmp <- out[[12]]
expect_equal(dim(for_model_pmp), c(4, 4))
# posterior model probabilities sum to 1
expect_equal(sum(for_model_pmp[, 3]), 1, tolerance = 1e-10)
expect_equal(sum(for_model_pmp[, 4]), 1, tolerance = 1e-10)
# --- for_model_sizes must have M+1 rows (=3) and 4 cols
for_model_sizes <- out[[13]]
expect_equal(dim(for_model_sizes), c(3, 4))
# prior size probs sum to 1
expect_equal(sum(for_model_sizes[, 1]), 1, tolerance = 1e-10)
expect_equal(sum(for_model_sizes[, 2]), 1, tolerance = 1e-10)
# posterior size probs sum to 1
expect_equal(sum(for_model_sizes[, 3]), 1, tolerance = 1e-10)
expect_equal(sum(for_model_sizes[, 4]), 1, tolerance = 1e-10)
# --- for_jointness has MS rows and (K + 2) cols: Reg_ID + pmp_u + pmp_r
for_jointness <- out[[10]]
expect_equal(dim(for_jointness), c(4, 4))
# --- for_best_models has MS rows and (K + (K+1) + (K+1) + 2 + 2) cols
# = K Reg_ID + betas(K+1) + SE(K+1) + DF + R2 + pmp_u + pmp_r
# = K + (K+1) + (K+1) + 4 = 2 + 3 + 3 + 4 = 12
for_best_models <- out[[11]]
expect_equal(nrow(for_best_models), 4)
expect_equal(ncol(for_best_models), 12)
# --- alphas length is MS (stored as MS x 1 matrix)
alphas <- out[[14]]
expect_true(is.matrix(alphas))
expect_equal(dim(alphas), c(4, 1))
})
test_that("bma Narrative prior downweights models with multiple substitutes (tiny K=2)", {
set.seed(1)
n <- 10
x1 <- rnorm(n)
x2 <- rnorm(n)
y <- 1 + x1 + rnorm(n, sd = 0.5)
data <- cbind(y = y, x1 = x1, x2 = x2)
ms <- model_space(data, M = 2, g = "None", HC = FALSE)
# Nar_vec groups both regressors together => group 1 contains x1 and x2
Nar_vec <- c(1L, 1L)
# Without narrative (baseline)
out0 <- bma(ms, EMS = 1, Narrative = 0, dilution = 0, round = 12)
# With narrative, scalar p < 1 (penalize having both x1 and x2 together)
out1 <- bma(ms, EMS = 1, Narrative = 1, p = 0.5, Nar_vec = Nar_vec, dilution = 0, round = 12)
pmp0 <- out0[[12]][, 3] # pmp_uniform
pmp1 <- out1[[12]][, 3]
# Model with both regressors included is the one with rowSums(Reg_ID)==2.
Reg_ID <- ms[[2]][, 1:2, drop = FALSE]
idx_both <- which(rowSums(Reg_ID) == 2)
# Narrative prior should not increase probability of the "both included" model
# (it should be penalized relative to other models)
expect_true(pmp1[idx_both] <= pmp0[idx_both] + 1e-12)
# Posterior probabilities still sum to 1
expect_equal(sum(pmp1), 1, tolerance = 1e-10)
})
test_that("bma errors when both dilution and Narrative are enabled", {
set.seed(2)
n <- 8
x1 <- rnorm(n)
x2 <- rnorm(n)
y <- 1 + rnorm(n)
data <- cbind(y = y, x1 = x1, x2 = x2)
ms <- model_space(data, M = 2, g = "None", HC = FALSE)
expect_error(
bma(ms, dilution = 1, Narrative = 1, Nar_vec = c(1L,1L), p = 0.7),
"CANNOT CHOOSE BOTH"
)
})
test_that("bma errors when Narrative=1 but Nar_vec missing or wrong length", {
set.seed(3)
n <- 8
x1 <- rnorm(n)
x2 <- rnorm(n)
y <- 1 + rnorm(n)
data <- cbind(y = y, x1 = x1, x2 = x2)
ms <- model_space(data, M = 2, g = "None", HC = FALSE)
expect_error(
bma(ms, Narrative = 1, p = 0.7, Nar_vec = NULL),
"Nar_vec"
)
expect_error(
bma(ms, Narrative = 1, p = 0.7, Nar_vec = c(1L)),
"Nar_vec should have K elements"
)
})
test_that("bma errors when Narrative=1 and p is a vector with wrong length vs groups", {
set.seed(4)
n <- 10
x1 <- rnorm(n)
x2 <- rnorm(n)
y <- 1 + rnorm(n)
data <- cbind(y = y, x1 = x1, x2 = x2)
ms <- model_space(data, M = 2, g = "None", HC = FALSE)
# Two groups implied (1 and 2), but p length mismatch
Nar_vec <- c(1L, 2L)
expect_error(
bma(ms, Narrative = 1, Nar_vec = Nar_vec, p = c(0.7, 0.5, 0.2)),
"p is misspecified"
)
})
test_that("bma works and is internally consistent on MS=16 (K=4, full model space)", {
set.seed(202)
n <- 25
x1 <- rnorm(n)
x2 <- rnorm(n)
x3 <- rnorm(n)
x4 <- rnorm(n)
# build y with some signal
y <- 1 + 0.7*x1 - 0.5*x3 + rnorm(n, sd = 0.4)
data <- cbind(y = y, x1 = x1, x2 = x2, x3 = x3, x4 = x4)
# full model space: K=4, M=4 => MS=16
ms <- model_space(data, M = 4, g = "None", HC = FALSE)
out <- bma(ms, EMS = 2, dilution = 0, Narrative = 0, round = 12)
# --- basic extracted scalars
expect_equal(out[[6]], 4) # K
expect_equal(out[[7]], 16) # MS
expect_equal(out[[8]], 2) # EMS
# --- Tables are (K+1) x 7
uniform_table <- out[[1]]
random_table <- out[[2]]
expect_equal(dim(uniform_table), c(5, 7))
expect_equal(dim(random_table), c(5, 7))
expect_equal(rownames(uniform_table), c("CONST","x1","x2","x3","x4"))
expect_equal(colnames(uniform_table), c("PIP","PM","PSD","PMcon","PSDcon","P(+)","PSC"))
# --- Posterior model probabilities sum to 1
for_model_pmp <- out[[12]]
expect_equal(dim(for_model_pmp), c(16, 4))
expect_equal(sum(for_model_pmp[, 3]), 1, tolerance = 1e-10) # pmp_uniform
expect_equal(sum(for_model_pmp[, 4]), 1, tolerance = 1e-10) # pmp_random
# --- Posterior model size probabilities sum to 1 and have length M+1 = 5
for_model_sizes <- out[[13]]
expect_equal(dim(for_model_sizes), c(5, 4))
expect_equal(sum(for_model_sizes[, 1]), 1, tolerance = 1e-10) # prior uniform sizes
expect_equal(sum(for_model_sizes[, 2]), 1, tolerance = 1e-10) # prior random sizes
expect_equal(sum(for_model_sizes[, 3]), 1, tolerance = 1e-10) # post uniform sizes
expect_equal(sum(for_model_sizes[, 4]), 1, tolerance = 1e-10) # post random sizes
# --- PIP range checks (slopes only)
expect_true(all(uniform_table[c("x1","x2","x3","x4"), "PIP"] >= 0))
expect_true(all(uniform_table[c("x1","x2","x3","x4"), "PIP"] <= 1))
expect_true(all(random_table[c("x1","x2","x3","x4"), "PIP"] >= 0))
expect_true(all(random_table[c("x1","x2","x3","x4"), "PIP"] <= 1))
# --- PSC and P(+) are probabilities
expect_true(all(uniform_table[, "PSC"] >= 0 & uniform_table[, "PSC"] <= 1))
expect_true(all(random_table[, "PSC"] >= 0 & random_table[, "PSC"] <= 1))
expect_true(all(uniform_table[, "P(+)"] >= 0 & uniform_table[, "P(+)"] <= 1))
expect_true(all(random_table[, "P(+)"] >= 0 & random_table[, "P(+)"] <= 1))
# --- Jointness table dimensions: MS rows, K + 2 cols
for_jointness <- out[[10]]
expect_equal(dim(for_jointness), c(16, 6))
# --- for_best_models dimensions: MS rows, K + (K+1) + (K+1) + 4 cols = 4 + 5 + 5 + 4 = 18
for_best_models <- out[[11]]
expect_equal(nrow(for_best_models), 16)
expect_equal(ncol(for_best_models), 18)
# --- EBA object has K+1 rows and required columns
eba_object <- out[[3]]
expect_true(is.data.frame(eba_object))
expect_equal(nrow(eba_object), 5)
expect_true(all(c("Variable","Lower bound","Minimum","Mean","Maximum","Upper bound","EBA test","%(+)") %in% names(eba_object)))
# --- Consistency identity check (slopes only):
# PM ≈ PIP * PMcon (in your code: con_PM = PM / PIP)
# Use tolerance because of rounding (round=12 reduces this problem).
for (v in c("x1","x2","x3","x4")) {
pip <- uniform_table[v, "PIP"]
pm <- uniform_table[v, "PM"]
pmc <- uniform_table[v, "PMcon"]
# skip if pip is extremely small (avoid 0 * Inf style numerical artefacts)
if (is.finite(pip) && pip > 1e-12) {
expect_equal(pm, pip * pmc, tolerance = 1e-7)
}
}
})
test_that("bma Narrative prior penalizes multi-substitute models in MS=16 space", {
set.seed(303)
n <- 25
X <- replicate(4, rnorm(n))
colnames(X) <- paste0("x", 1:4)
y <- 1 + 0.8*X[,1] + rnorm(n, sd = 0.5)
data <- cbind(y = y, X)
ms <- model_space(data, M = 4, g = "None", HC = FALSE)
# Two groups: {x1,x2} are substitutes (group 1), {x3,x4} substitutes (group 2)
Nar_vec <- c(1L, 1L, 2L, 2L)
out0 <- bma(ms, EMS = 2, Narrative = 0, dilution = 0, round = 12)
out1 <- bma(ms, EMS = 2, Narrative = 1, p = c(0.6, 0.5), Nar_vec = Nar_vec, dilution = 0, round = 12)
pmp0 <- out0[[12]][, 3] # pmp_uniform
pmp1 <- out1[[12]][, 3]
Reg_ID <- ms[[2]][, 1:4, drop = FALSE]
# Models that include BOTH x1 and x2 (two substitutes in group 1) should be penalized
idx_two_in_g1 <- which(Reg_ID[,1] == 1L & Reg_ID[,2] == 1L)
# At least one of these models should have (weakly) lower posterior after narrative penalty
expect_true(any(pmp1[idx_two_in_g1] <= pmp0[idx_two_in_g1] + 1e-12))
# Still a valid posterior distribution
expect_equal(sum(pmp1), 1, tolerance = 1e-10)
})
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.