Skip to contents

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_design object, or a tbl_sample. A fully executed sample validates its stored design and compares its frame digest (when present) against frame. 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 in execute(), and every check here is one execute() runs before it samples: a frame this accepts is a frame execution accepts.

...

These dots are for future extensions and must be empty. stages and the arguments after it follow ..., so each must be named exactly: the singular stage is reported rather than prefix-matched.

stages

Which stage(s) to validate against. Defaults to all stages, and for a partial tbl_sample to every remaining stage, matching execute(). Where one frame cannot say whether it is the next stage's register or a hierarchy covering the rest, stages is required, again as in execute().

fingerprint

How to report differences between frame and what was recorded earlier: the frame fingerprint stored in a design file (designs restored with read_design() when saved with frame =) and the frame digest recorded at execution (when design is a tbl_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.

Value

Invisibly returns TRUE if validation passes. Throws an informative error if validation fails.

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

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.

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

Examples

# Create a design requiring region stratification and PPS by household count
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.

# 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)

# 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)