Nothing
box::use(
testthat[
expect_equal,
expect_error,
expect_false,
expect_gt,
expect_named,
expect_null,
expect_true,
test_that
]
)
box::use(
artma / data / column_recognition[recognize_columns],
artma / data / derivation[
derive_pcc_columns,
derive_pcc_from_t_dof,
describe_wide_format,
detect_tdf_derivation,
detect_wide_format,
find_dof_column,
find_horizon_families,
looks_like_dof,
role_name_is_canonical
],
artma / data / interactive_mapping[confirm_derivation],
artma / data / method_requirements[resolve_hard_required_colnames]
)
# --- Degrees-of-freedom column detection ------------------------------------
test_that("looks_like_dof accepts varying positive whole numbers", {
expect_true(looks_like_dof(c(12, 45, 137, 422, 89)))
})
test_that("looks_like_dof rejects fractions, flags and zero-based counts", {
expect_false(looks_like_dof(c(1.5, 2.25, 3.75, 10.1, 44.4)))
expect_false(looks_like_dof(c(1, 2, 1, 2, 1, 2))) # two distinct values: a flag
expect_false(looks_like_dof(c(0, 1, 5, 12, 30))) # a count starting at zero
expect_false(looks_like_dof(c(NA, NA, NA, 40, 55), n_rows = 20)) # 25% coverage
})
test_that("find_dof_column requires both an anchored name and plausible values", {
df <- data.frame(
t = stats::rnorm(30),
df = sample(20:400, 30),
dfe = sample(20:400, 30), # not an anchored degrees-of-freedom name
stringsAsFactors = FALSE
)
expect_equal(find_dof_column(df), "df")
# The name alone is never enough: a 0/1 flag called "dof" is not a count.
flagged <- data.frame(dof = rep(c(0, 1), 15))
expect_null(find_dof_column(flagged))
expect_null(find_dof_column(df, exclude = "df"))
})
# --- The partial-correlation formulas ---------------------------------------
test_that("derive_pcc_from_t_dof matches the closed-form conversion", {
t_values <- c(2, -3.5, 0, 7.25)
dof <- c(50, 120, 30, 800)
derived <- derive_pcc_from_t_dof(t_values, dof)
expect_equal(derived$effect, t_values / sqrt(t_values^2 + dof))
expect_equal(derived$se, sqrt((1 - derived$effect^2) / dof))
})
test_that("the derived pair reproduces the t-statistic it came from", {
t_values <- c(1.9, -4.2, 0.4, 11)
dof <- c(64, 300, 45, 1200)
derived <- derive_pcc_from_t_dof(t_values, dof)
expect_equal(derived$effect / derived$se, t_values)
})
test_that("non-positive degrees of freedom yield NA rather than a complex number", {
derived <- derive_pcc_from_t_dof(c(2, 2, 2), c(100, 0, -5))
expect_true(is.finite(derived$effect[1]))
expect_true(all(is.na(derived$effect[2:3])))
expect_true(all(is.na(derived$se[2:3])))
})
# --- Choosing the derived route ---------------------------------------------
#' A dataset of the family this route exists for: coefficients reported in
#' incomparable units, with the t-statistic and degrees of freedom that make
#' them comparable sitting alongside under suffixed, non-canonical names.
make_incomparable_units_df <- function(n = 120, seed = 11) {
withr::with_seed(seed, {
dof <- sample(30:900, n, replace = TRUE)
t_values <- round(stats::rnorm(n, 1.4, 2.2), 3)
scale <- rep(c(1, 1000, 1e6), length.out = n)
se <- round(abs(stats::rnorm(n, 0.4, 0.1)) * scale, 6)
data.frame(
Study = rep(sprintf("Author%02d", seq_len(n / 4)), each = 4),
COEF_L = round(t_values * se, 6),
SE_L = se,
TSTAT_L = t_values,
DF = dof,
Sample = dof + 4L,
stringsAsFactors = FALSE
)
})
}
test_that("detect_tdf_derivation displaces a non-canonical effect/se pair", {
df <- make_incomparable_units_df()
mapping <- list(effect = "COEF_L", se = "SE_L", t_stat = "TSTAT_L", study_id = "Study")
derivation <- detect_tdf_derivation(df, mapping)
expect_true(is.list(derivation))
expect_equal(derivation$t_stat, "TSTAT_L")
expect_equal(derivation$dof, "DF")
expect_equal(unlist(derivation$replaces), c(effect = "COEF_L", se = "SE_L"))
expect_gt(derivation$coverage, 0.9)
})
test_that("detect_tdf_derivation leaves a canonically named pair alone", {
df <- make_incomparable_units_df()
names(df)[names(df) == "COEF_L"] <- "estimate"
names(df)[names(df) == "SE_L"] <- "se"
mapping <- list(effect = "estimate", se = "se", t_stat = "TSTAT_L", study_id = "Study")
expect_null(detect_tdf_derivation(df, mapping))
})
test_that("detect_tdf_derivation declines when the effect is already the partial correlation", {
df <- make_incomparable_units_df()
derived <- derive_pcc_from_t_dof(df$TSTAT_L, df$DF)
df$COEF_L <- derived$effect
df$SE_L <- derived$se
mapping <- list(effect = "COEF_L", se = "SE_L", t_stat = "TSTAT_L", study_id = "Study")
expect_null(detect_tdf_derivation(df, mapping))
})
test_that("detect_tdf_derivation needs both a t-statistic and a df companion", {
df <- make_incomparable_units_df()
mapping <- list(effect = "COEF_L", se = "SE_L", study_id = "Study")
expect_null(detect_tdf_derivation(df, mapping))
no_dof <- df[, setdiff(names(df), "DF"), drop = FALSE]
expect_null(detect_tdf_derivation(no_dof, list(
effect = "COEF_L", se = "SE_L", t_stat = "TSTAT_L", study_id = "Study"
)))
})
test_that("role_name_is_canonical separates exact names from suffixed ones", {
expect_true(role_name_is_canonical("se", "se"))
expect_true(role_name_is_canonical("Estimate", "effect"))
expect_false(role_name_is_canonical("SE_L", "se"))
expect_false(role_name_is_canonical("COEF_L", "effect"))
})
test_that("recognize_columns declines the suffixed pair and reports the route", {
withr::local_options(list("artma.verbose" = 1))
df <- make_incomparable_units_df()
mapping <- recognize_columns(df)
derivation <- attr(mapping, "derivation")
expect_false("effect" %in% names(mapping))
expect_false("se" %in% names(mapping))
expect_equal(derivation$t_stat, "TSTAT_L")
expect_equal(derivation$dof, "DF")
expect_true(grepl("TSTAT_L", attr(mapping, "declined")$effect$reason, fixed = TRUE))
})
# --- Wide-format sheets ------------------------------------------------------
#' An impulse-response sheet: one row per model, one column per horizon, and no
#' long-format effect/se pair anywhere.
make_wide_format_df <- function(n = 60, seed = 7) {
withr::with_seed(seed, {
horizons <- c(3, 6, 12, 18, 36)
df <- data.frame(
idstudy = rep(seq_len(n / 4), each = 4),
study = rep(sprintf("Author%02d (200%d)", seq_len(n / 4), seq_len(n / 4) %% 10), each = 4),
nobs = sample(80:900, n, replace = TRUE),
lags = sample(1:8, n, replace = TRUE),
stringsAsFactors = FALSE
)
for (h in horizons) {
df[[sprintf("m%d_res", h)]] <- round(stats::rnorm(n, 0.3, 0.4), 4)
df[[sprintf("SE%d", h)]] <- round(abs(stats::rnorm(n, 0.2, 0.05)), 4)
df[[sprintf("band%d_u", h)]] <- round(stats::rnorm(n, 0.8, 0.4), 4)
}
df
})
}
test_that("find_horizon_families groups the response columns by stem", {
families <- find_horizon_families(make_wide_format_df())
expect_true(all(c("m#_res", "se#", "band#_u") %in% names(families)))
expect_equal(families[["m#_res"]]$horizons, c(3, 6, 12, 18, 36))
expect_equal(families[["se#"]]$columns, c("SE3", "SE6", "SE12", "SE18", "SE36"))
})
test_that("detect_wide_format fires only when no long-format pair was mapped", {
df <- make_wide_format_df()
detection <- detect_wide_format(df, list(study_id = "study", n_obs = "nobs"))
expect_true(is.list(detection))
expect_true(length(detection$families) >= 3)
expect_null(detect_wide_format(df, list(effect = "m3_res", se = "SE3")))
})
test_that("detect_wide_format ignores a long-format frame with numeric suffixes", {
withr::local_seed(3)
n <- 80
df <- data.frame(
study = rep(sprintf("Author%02d", seq_len(n / 4)), each = 4),
effect = round(stats::rnorm(n, 0.2, 0.3), 4),
se = round(abs(stats::rnorm(n, 0.15, 0.04)), 4),
nobs = sample(50:900, n, replace = TRUE),
lag1 = sample(0:1, n, replace = TRUE),
lag2 = sample(0:1, n, replace = TRUE),
lag3 = sample(0:1, n, replace = TRUE),
stringsAsFactors = FALSE
)
expect_null(detect_wide_format(df, list(study_id = "study")))
})
test_that("describe_wide_format names the layout and its families", {
detection <- detect_wide_format(make_wide_format_df(), list())
described <- describe_wide_format(detection)
expect_true(grepl("wide format", described, fixed = TRUE))
expect_true(grepl("SE3", described, fixed = TRUE))
})
test_that("recognize_columns attaches the wide-format decline to effect and se", {
withr::local_options(list("artma.verbose" = 1))
mapping <- recognize_columns(make_wide_format_df())
declined <- attr(mapping, "declined")
expect_false("effect" %in% names(mapping))
expect_false("se" %in% names(mapping))
expect_equal(declined$effect$layout, "wide_format")
expect_equal(declined$se$layout, "wide_format")
expect_true(grepl("wide format", declined$effect$reason, fixed = TRUE))
})
# --- Confirming the route ----------------------------------------------------
sample_derivation <- function() {
list(
t_stat = "TSTAT_L",
dof = "DF",
coverage = 0.95,
replaces = list(effect = "COEF_L", se = "SE_L")
)
}
test_that("a non-interactive session takes the derived route without asking", {
result <- confirm_derivation(
mapping = list(study_id = "Study"),
derivation = sample_derivation(),
select_fn = function(choices, prompt) stop("must not prompt"),
is_interactive = FALSE
)
expect_equal(result$mapping$t_stat, "TSTAT_L")
expect_equal(result$mapping$reg_dof, "DF")
expect_equal(result$derivation$dof, "DF")
})
test_that("confirming the route maps the two inputs", {
withr::local_options(list("artma.verbose" = 1))
result <- confirm_derivation(
mapping = list(study_id = "Study"),
derivation = sample_derivation(),
select_fn = function(choices, prompt) choices[1],
is_interactive = TRUE
)
expect_equal(result$mapping$t_stat, "TSTAT_L")
expect_equal(result$mapping$reg_dof, "DF")
expect_false(is.null(result$derivation))
})
test_that("refusing the route restores the columns it displaced", {
withr::local_options(list("artma.verbose" = 1))
result <- confirm_derivation(
mapping = list(study_id = "Study"),
derivation = sample_derivation(),
select_fn = function(choices, prompt) choices[2],
is_interactive = TRUE
)
expect_null(result$derivation)
expect_equal(result$mapping$effect, "COEF_L")
expect_equal(result$mapping$se, "SE_L")
expect_false("reg_dof" %in% names(result$mapping))
})
test_that("choosing neither leaves effect and se unmapped", {
withr::local_options(list("artma.verbose" = 1))
result <- confirm_derivation(
mapping = list(study_id = "Study"),
derivation = sample_derivation(),
select_fn = function(choices, prompt) choices[length(choices)],
is_interactive = TRUE
)
expect_null(result$derivation)
expect_false("effect" %in% names(result$mapping))
expect_false("se" %in% names(result$mapping))
})
test_that("confirm_derivation is a no-op without a proposal", {
mapping <- list(effect = "e", se = "se")
result <- confirm_derivation(mapping, derivation = NULL, is_interactive = TRUE)
expect_equal(result$mapping, mapping)
expect_null(result$derivation)
})
# --- The compute-phase step --------------------------------------------------
test_that("derive_pcc_columns is a no-op unless the option is set", {
df <- data.frame(t_stat = c(2, 3), reg_dof = c(100, 200))
withr::local_options(list("artma.data.derive_pcc" = FALSE))
expect_equal(derive_pcc_columns(df), df)
})
test_that("derive_pcc_columns writes effect and se from t_stat and reg_dof", {
withr::local_options(list(
"artma.data.derive_pcc" = TRUE,
"artma.verbose" = 1
))
df <- data.frame(
study_id = c("A", "B", "C"),
t_stat = c(2, -3, 5),
reg_dof = c(100, 200, 50)
)
result <- derive_pcc_columns(df)
expected <- derive_pcc_from_t_dof(df$t_stat, df$reg_dof)
expect_named(result, c("study_id", "t_stat", "reg_dof", "effect", "se"))
expect_equal(result$effect, expected$effect)
expect_equal(result$se, expected$se)
})
test_that("derive_pcc_columns coerces character inputs read from a spreadsheet", {
withr::local_options(list(
"artma.data.derive_pcc" = TRUE,
"artma.verbose" = 1
))
df <- data.frame(
t_stat = c("2", "-3"),
reg_dof = c("100", "200"),
stringsAsFactors = FALSE
)
result <- derive_pcc_columns(df)
expect_equal(result$effect, derive_pcc_from_t_dof(c(2, -3), c(100, 200))$effect)
})
test_that("derive_pcc_columns aborts when an input column is missing", {
withr::local_options(list("artma.data.derive_pcc" = TRUE))
expect_error(
derive_pcc_columns(data.frame(t_stat = c(2, 3))),
"reg_dof"
)
})
test_that("the derived route swaps effect and se for its inputs in the required set", {
withr::local_options(list("artma.data.derive_pcc" = TRUE))
required <- resolve_hard_required_colnames()
expect_false(any(c("effect", "se") %in% required))
expect_true(all(c("study_id", "t_stat", "reg_dof") %in% required))
})
# --- End to end through the three pipeline phases -----------------------------
test_that("the pipeline prepares a file that carries only (t, df), not effect/se", {
box::use(
artma / data / index[configure_data, compute_data_impl, persist_data, prime_raw_df],
artma / data / read[read_data],
testing / fixtures / index[FIXTURES]
)
FIXTURES$local_cli_silence()
fixture_dir <- withr::local_tempdir()
n <- 80
raw <- withr::with_seed(5, {
dof <- sample(40:600, n, replace = TRUE)
data.frame(
Study = rep(sprintf("Author%02d (20%02d)", seq_len(n / 4), seq_len(n / 4)), each = 4),
TSTAT_L = round(stats::rnorm(n, 1.2, 2), 4),
DF = dof,
Sample = dof + 4L,
Region = rep(c("EU", "US"), length.out = n),
stringsAsFactors = FALSE
)
})
source_path <- file.path(fixture_dir, "tdf-data.csv")
utils::write.csv(raw, source_path, row.names = FALSE)
options_dir <- file.path(fixture_dir, "options")
dir.create(options_dir)
artma::options_create(
options_file_name = "tdf.yaml",
options_dir = options_dir,
user_input = list(
"data.source_path" = source_path,
"data.derive_pcc" = TRUE,
"data.na_handling" = "remove",
"data.reconcile_mode" = "auto",
"data.columns" = list(
study_id = list(source_name = "Study"),
t_stat = list(source_name = "TSTAT_L"),
reg_dof = list(source_name = "DF"),
n_obs = list(source_name = "Sample")
),
"calc.se_zero_handling" = "ignore",
"cache.use_cache" = FALSE,
"verbose" = 1L
),
should_validate = TRUE,
should_overwrite = TRUE
)
withr::local_options(artma::options_load(
options_file_name = "tdf.yaml",
options_dir = options_dir,
load_with_prefix = TRUE,
should_validate = TRUE,
should_add_temp_options = TRUE,
should_return = TRUE
))
df_raw <- read_data()
prime_raw_df(df_raw)
configure_data(df_raw)
df <- compute_data_impl()
persist_data(df)
expect_true(all(c("effect", "se") %in% names(df)))
expect_true(all(is.finite(df$effect)))
expect_true(all(df$se > 0))
# r = t / sqrt(t^2 + df) never leaves [-1, 1], and t is recovered from the pair
expect_true(all(abs(df$effect) < 1))
expect_equal(df$t_stat, df$effect / df$se, tolerance = 1e-8)
})
test_that("a displaced role stops being offered as a provisional candidate", {
withr::local_options(list("artma.verbose" = 1))
wide <- recognize_columns(make_wide_format_df())
expect_false(any(c("effect", "se") %in% names(attr(wide, "provisional"))))
derived <- recognize_columns(make_incomparable_units_df())
expect_false(any(c("effect", "se") %in% names(attr(derived, "provisional"))))
})
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.