A biomarker workflow often ranks all markers, chooses a candidate pool, searches many panels, and then reports cross-validation for the selected panel. That procedure leaks outcome information because the assessment observations have already influenced feature selection.
detectPanel places the following operations inside each
outer analysis set:
The outer assessment set is used only for prediction.
library(detectPanel)
set.seed(9)
n <- 36
p <- 12
y <- rep(c("Control", "Case"), each = n / 2)
counts <- matrix(
rpois(p * n, 50), nrow = p,
dimnames = list(paste0("m", seq_len(p)), paste0("s", seq_len(n)))
)
counts[1:3, y == "Case"] <- counts[1:3, y == "Case"] + 60
meta <- data.frame(group = y, row.names = colnames(counts))
fit <- discover_panel(
counts, meta,
outcome = "group",
positive = "Case",
candidate_n = 7,
min_mean = 5,
min_median = 2,
min_detection = 0.3,
min_group_detection = 0.2,
min_auc = 0.55,
outer_v = 3,
outer_repeats = 1,
inner_v = 3,
inner_repeats = 1
)
fit$nested$outer_summary
#> requested_splits valid_splits failed_splits mean_outer_AUC sd_outer_AUC
#> 1 3 3 0 1 0
#> median_outer_AUC minimum_outer_AUC maximum_outer_AUC pooled_sample_level_AUC
#> 1 1 1 1 1
fit$nested$feature_frequency
#> feature count frequency
#> 1 m1 3 1
#> 2 m2 3 1
#> 3 m3 3 1The nested predictions estimate internal generalization performance.
After that assessment, discover_panel() refits the most
frequently selected exact panel on all samples. This final model is
useful for locked prediction on a new cohort, but its own training AUC
is not an independent validation result.
By default, a training split fails when fewer than
panel_size markers satisfy the prespecified thresholds.
This makes threshold violations visible. During exploratory work,
allow_fallback = TRUE may be used to fill the pool with the
highest-ranked finite markers. The used_fallback field must
then be reviewed and reported.