Checks if a data frame contains all required variables for a sampling design and reports any issues.
Usage
validate_frame(
design,
frame,
...,
stages = NULL,
fingerprint = c("inform", "warn", "ignore")
)Arguments
- design
A
sampling_designobject, or atbl_sample. A fully executed sample validates its stored design and compares its frame digest (when present) againstframe. A partial sample validates the frame that would continue it, against the units it selected.- frame
A data frame to validate, or an ordered list of stage frames: one per stage, in the order
execute()would receive them. Each entry may itself be hierarchical. A data frame and a one-element list are the same input, as they are inexecute(), and every check here is oneexecute()runs before it samples: a frame this accepts is a frame execution accepts.- ...
These dots are for future extensions and must be empty.
stagesand the arguments after it follow..., so each must be named exactly: the singularstageis reported rather than prefix-matched.- stages
Which stage(s) to validate against. Defaults to all stages, and for a partial
tbl_sampleto every remaining stage, matchingexecute(). Where one frame cannot say whether it is the next stage's register or a hierarchy covering the rest,stagesis required, again as inexecute().- fingerprint
How to report differences between
frameand what was recorded earlier: the frame fingerprint stored in a design file (designs restored withread_design()when saved withframe =) and the frame digest recorded at execution (whendesignis atbl_sample, or a restored design whose receipt carries one). One of"inform"(default, emits a message),"warn", or"ignore". Both comparisons are informational and never fail validation, because a design remains executable on any frame that passes the variable checks.
Details
Validation checks include:
Presence of required stratification variables
Presence of required clustering variables
Presence of measure of size (MOS) variables for PPS sampling
Non-empty frame
Positive values for MOS variables
A fixed-size stage drawn without replacement whose take exceeds the
population of a candidate pool is not an error by default, because
execute() takes every unit of that pool and warns. A stage that sets
on_capped = "error" in draw() is refused here with
samplyr_error_size_capped, as execute() refuses it. Otherwise
validate_frame() names such pools in
a message of class samplyr_message_capped_pools, with the stage and a
payload holding pool_keys, n_capped and n_pools. A candidate pool
is one the stage would draw from if its parent were selected, so the
message does not depend on the seed. The labels are the pool_key
column of frame_summary(design, frame, detail = "pool").
In the same way, a candidate parent that lacks a stratum an explicit n
names is reported with samplyr_message_absent_strata, the message
execute() raises for a selected parent (see execution_conditions).
For designs restored with read_design(), if the design file carries a
frame fingerprint, validate_frame() also compares it against frame
and reports what changed (rows, columns, column types, or content).
When a frame digest is available (an executed tbl_sample, or a
design file written from one), the structural comparison goes
further: the role-scoped fingerprint (analysis columns added later
do not trigger it), the frame size, and per-pool population sizes
recomputed from frame at every stage the digest can anchor
(stage 1 over the universe and later stages under the recorded
parents). The report says where the frame drifted, not merely that
it did.
Ordered stage frames
A data frame means one hierarchy. A list means separately supplied
stage frames, and validate_frame() then checks everything
execute() checks before it draws: the frame count, the columns
each stage selects on, cluster-level variables that must be constant
within a unit, ancestry types that must be joinable, and ancestry
values that name no parent.
Values are judged on every unit a stage could reach, not on the parents a
draw happens to select: missing, NA or mistyped strata, clusters,
measures of size and the other columns a stage reads, reported with class
samplyr_error_frame_invalid. execute() runs the same check on every
later stage before it draws, so whether a frame is accepted never depends
on the seed. Both also refuse a size in n or frac for a stratum no
reachable unit belongs to (samplyr_error_alloc_unknown_strata), which
would otherwise be dropped without a word. Tables that only describe
strata, such as variance, may hold extra rows, and so may a take table
keyed by a parent's identifier, for the parents a continuation did not
select.
It also checks candidate coverage, and is stricter there than
execution: a unit reachable at one stage with no rows in the
register the next stage samples from is
samplyr_error_frame_incomplete_register. execute() only warns,
because the sample it happens to draw may never need that unit. The
two agree on realized parents, which fail in both.
Continuing a partial sample
When design is a tbl_sample with stages left to run,
validate_frame() checks the supplied frames against the units that
sample actually selected, through the same transition the
continuation would use: complete ancestry, joinable key types, no
missing parent identifiers, and a row for every selected parent. It
accepts the same shapes execute() does, so one register per
remaining stage works here as it does there. The recorded fingerprint
and frame digest are not compared, because they describe the frame
the executed stages drew from rather than the register the next stage
needs.
Preparing a second phase
When frame is itself a tbl_sample (that is, the design is being
prepared as phase 2 of a two-phase sample), validate_frame() also
pre-flights the two-phase export requirements. The phases declare
their sampling units independently, so the link is the compound of
every identifier either phase declares with cluster_by() that both
samples carry, which is the bridge as_svydesign() builds. It warns
when neither phase declares an identifier the phase-1 sample carries,
and when the identifiers together do not uniquely identify phase-1
rows. Problems are reported as warnings rather than errors, because
selection and weighting work without linkage. Only as_svydesign()
needs it.
A previous-phase sample also carries identifiers that later stages
must keep. One that varies inside a sampling unit cannot be carried,
and is refused here for the same reason execute() refuses it: a
clustered stage keeps one representative row, so by the time anything
downstream looks, the disagreement is gone.
See also
Other execution:
execute(),
execution_conditions,
rotation_program()
Examples
# A design stratified by region, with PPS on household counts
design <- sampling_design() |>
stratify_by(region) |>
cluster_by(ea_id) |>
draw(n = 10, method = "pps_brewer", mos = households)
# Validate against bfa_eas (should pass)
validate_frame(design, bfa_eas)
# Create a frame missing required variables (will fail)
bad_frame <- data.frame(id = 1:100, value = rnorm(100))
try(validate_frame(design, bad_frame))
#> Error in validate_frame(design, bad_frame) :
#> frame 1, used for stage 1, is missing region, ea_id, and households.
#> ✖ region is the stratification variable.
#> ✖ ea_id is the cluster variable.
#> ✖ households is the MOS variable.
#> ℹ stage 1 selects on region, ea_id, and households.
#> ℹ A column name held in a variable is written `.data[[v]]`, as in `mos =
#> .data[[v]]`.
# Validate only specific stages of a multi-stage design
zwe_frame <- zwe_eas |>
dplyr::mutate(district_hh = sum(households), .by = district)
multi_design <- sampling_design() |>
add_stage(label = "Districts") |>
cluster_by(district) |>
draw(n = 20, method = "pps_brewer", mos = district_hh) |>
add_stage(label = "EAs") |>
draw(n = 10)
# Validate stage 1 only
validate_frame(multi_design, zwe_frame, stages = 1)
# One register per stage: a list, in the order execute() receives them
districts <- zwe_frame |>
dplyr::distinct(district, district_hh)
validate_frame(multi_design, list(districts, zwe_eas))
# A partial sample validates the register that would continue it
stage1 <- execute(multi_design, districts, stages = 1, seed = 5)
validate_frame(stage1, zwe_eas)
#> ℹ 73778 of 107250 rows of the register for stage 2 "EAs" belong to units the
#> sample did not select.
#> `execute()` sets them aside and samples from the other 33472.
# Designs restored from a file also check the stored frame fingerprint
path <- tempfile(fileext = ".json")
write_design(design, path, frame = bfa_eas)
restored <- read_design(path)
# Same frame, no message
validate_frame(restored, bfa_eas)
# A modified frame passes validation with an informational message
validate_frame(restored, bfa_eas[-1, ])
#> Frame differs from what was recorded when the design was saved:
#> • 44569 rows instead of the 44570 recorded
#> ℹ This is informational. The design remains executable on any frame that passes
#> the variable checks.
unlink(path)