Runs a set of named predicates over a data frame and returns them as a
tibble of results. Rules passed as hard abort on failure; rules passed
as soft warn. Everything is evaluated before anything is signalled, so
one failing rule never hides the others.
Value
A tibble::tibble with one row per rule and columns rule,
level ("hard" or "soft"), passed (logical) and message
(NA for a passing rule), hard rules first. When a hard rule fails,
aborts with condition class pr_validate_failed, carrying that same
tibble in the condition's results field. Soft failures warn with
condition class pr_validate_soft_failed.
Details
The two tiers encode a distinction most validation code loses: some
properties are structural (a duplicated key, a negative duration, an
unknown condition code) and make every downstream number wrong, while
others are worth knowing about but not worth stopping for (an unbalanced
cell, an unusually short recording). Collapsing both into stopifnot()
means either the pipeline halts for cosmetic reasons or the structural
checks get commented out.
A rule is a function of the data frame returning either:
a logical vector —
TRUEthroughout passes; anyFALSEorNAfails, and the message counts the failing elements. This lets a rule be written per row (function(d) d$n_frames > 0) or as a single claim (function(d) anyDuplicated(d$ID) == 0L).a character vector — an empty one passes, and any strings are taken as the problem description. Use this when the rule can say what is wrong, not merely that something is.
A rule that throws is recorded as a failure carrying its error message rather than propagated: a validation run should report on a broken rule, not die inside it.
See also
pr_validate_dataset() for the trial-level homogeneity checks.
Other provenance functions:
pr_design_gaps(),
pr_factor_contract(),
pr_lock_table(),
pr_verify_lock()
Examples
design <- data.frame(
ID = c("ID001", "ID001", "ID003"),
Mode = c("MH", "MS", "MG"),
n_frames = c(23432L, 10662L, 8000L)
)
res <- pr_validate_summary(
design,
hard = list(
positive_frames = function(d) d$n_frames > 0,
mode_known = function(d) all(d$Mode %in% c("MG", "MH", "MS"))
),
label = "design"
)
res
#> # A tibble: 2 × 4
#> rule level passed message
#> <chr> <chr> <lgl> <chr>
#> 1 positive_frames hard TRUE NA
#> 2 mode_known hard TRUE NA
# A soft rule warns and is still reported. The real cohort is unbalanced
# across Mode (92 / 169 / 170 recordings), which is worth knowing but is
# not a reason to stop.
unbalanced <- rbind(design, design[2, ])
res2 <- suppressWarnings(pr_validate_summary(
unbalanced,
soft = list(balanced = function(d) {
tab <- table(d$Mode)
if (length(unique(as.integer(tab))) == 1L) {
character(0)
} else {
paste0("Mode counts differ: ",
paste(names(tab), as.integer(tab), sep = "=",
collapse = ", "))
}
}),
label = "design"
))
res2$message
#> [1] "Mode counts differ: MG=1, MH=1, MS=2"
# A hard failure aborts:
try(pr_validate_summary(
design,
hard = list(unique_id = function(d) anyDuplicated(d$ID) == 0L)
))
#> Error in pr_validate_summary(design, hard = list(unique_id = function(d) anyDuplicated(d$ID) == :
#> 1 hard validation rule failed for "data".
#> ✖ unique_id: rule returned FALSE
#> ℹ A hard rule guards a structural property; fix the data rather than the rule.