Skip to contents

write_design() saves a sampling design (or the design carried by an executed sample) to a human-readable, samplyr-native JSON file. read_design() reads it back into a sampling_design that executes identically to the original.

The file is versioned JSON, diffable in version control. It stores the complete design specification (stages, stratification, clustering, draw settings, including per-stratum vectors and data frames), never the frame data itself. A certainty plan from svyplan is the exception: its PSU register (identifiers, strata, sizes and certainty flags) is part of the design and is written with it.

A file written from an executed sample can also contain its frame digest. That digest retains selected-unit identifiers and may contain per-unit chance metadata. Treat an execution receipt as potentially confidential even though it does not contain the ordinary frame columns.

Usage

write_design(x, path, frame = NULL, ..., pretty = TRUE)

read_design(file)

Arguments

x

A sampling_design, a tbl_sample (the stored design is saved along with an execution receipt), a frame_stack, or a sample carrying shared weights.

path

File path to write to. Conventionally with a .json extension.

frame

Optional sampling frame, fingerprinted (name, dimensions, column types, content hash) so it can be verified later. A data frame is the one frame the design was built against. An ordered list of data frames is the stage registers, in the order execute() received them, each fingerprinted separately. A one-element list is one frame (see frame_input_grammar). The number of frames must be one the design could be executed with, and for an executed sample the number its receipt records. For a sample built by several calls, one element per call that read frames (the first call and each continuation), in call order: that call's frame, or the list of its registers. One data frame stands for all of them when each read that same frame. For a frame_stack, a list named by component, holding what each component was drawn from, matched by name rather than position. For a shared-weight sample, the register the source selection was drawn from. vignette("serialization") states what the hash covers. The ordinary frame columns are never written, but an executed sample's receipt can still contain selected-unit identifiers in its frame digest.

...

These dots are for future extensions and must be empty. pretty follows ..., so it is matched exactly and must be named.

pretty

Whether to pretty-print the JSON. Defaults to TRUE for files and FALSE for design_json().

file

A path to a local file, or a JSON string produced by design_json(). URLs are refused: read_design() never fetches remote files. Download the file first and read the local copy.

Value

write_design() returns x invisibly. read_design() returns a sampling_design, a frame_stack_design for a frame collection file, or a shared_sample_design for a shared-weight sample file. Any frame information and execution receipt in the file are attached as the "frame_info" and "execution" attributes.

Details

Lifecycle

The serialization interface and its samplyr-native file format are experimental. They are not a finalized cross-tool interchange standard, and the structure may change. vignette("serialization") walks through the file contents and the replay workflow.

Frame information

Design files are frame-independent. Every file records the columns each stage needs (stratification, clustering, mos, prn, aux, and control variables), so a candidate frame can be checked with validate_frame() before execution. When frame is supplied, the file also records a fingerprint: portable dimensions and column types in frame, plus the R source label, native classes, and content hash in tools.samplyr.

Execution receipts

When x is a tbl_sample, the file also records an execution receipt: every execute() argument that affects the result (seed, executed stages, panels, reps, and the per-replicate seeds), the RNG configuration, package versions, the number of selected units, the execution timestamp, and how each call mapped its frames to stages. A receipt without that mapping is read as a one-frame call. With the same frame, compatible package implementations, and any recorded custom methods, replay_design() reproduces the sample. The sampled rows themselves are not stored. Use a data format (CSV, parquet) for those.

A sample built by several execute() calls (a stage continuation, a new phase drawn from an earlier sample, or a materialized panel wave) is flagged as chained. Its receipt records every earlier call in earlier_calls: seed, stages, frame mapping, panel and replicate arguments, RNG kind, how it used the call before it, and, for a call followed by a new phase, its own design.

A sample whose rows or design columns were modified after execution is flagged modified, and its receipt describes the original execution. A new phase drawn from a modified sample is recorded from that phase on, with the modified sample as its frame. Two chains cannot be recorded: a continuation of a modified sample, since replaying the earlier call rebuilds the sample as it was before the change, and a continuation made by a samplyr version that did not keep its parent's seed. For these write_design() warns with class samplyr_warning_receipt_chained and gives the recipe of replay_design().

Control expressions

draw(control = ...) expressions are stored as declarative JSON terms ("ascending", "descending", or "serpentine", with their variables), not R code. Only bare column names, dplyr::desc(), and serp() can be represented, and write_design() errors on anything else.

Collections and shared weights

A frame_stack from stack_frames() is written as samplyr/frame-stack. Each component entry is a complete samplyr/design document plus its name and membership column, and the collection's key and any overlaps declared with declared_overlaps() are recorded alongside. read_design() returns the components' designs and receipts rather than the collection, which needs the registers.

A sample carrying shared weights from share_weights() is written as samplyr/shared-sample. It records the source selection, the transformation's arguments, and two integrity records, and nothing else. The links and the target register are supplied again to replay_design(), the way a frame is.

Frame data, target data and link tables are never written. What would need them is refused rather than written in part: a collection whose overlaps come from exante_overlaps() (one chance per selected unit, which is unit-level data), and a shared-weight sample used as a component of a collection (no place for a link table). Serialize the sample on its own, or the collection without them, and rebuild afterwards. saveRDS() preserves any of these objects whole.

Document validation

Reads and writes check the document before reconstruction or file creation, with native R checks generated from the same source as the bundled JSON Schemas. Unknown executable fields, duplicate JSON keys, malformed structures and contradictory method descriptors are refused, and so is a nonempty required_extensions array at any level, since no executable extensions are currently supported. The reader accepts design format version 3 and version 1 of the frame-stack and shared-sample formats. An optional execution.frame_digest is exempt and keeps its own version check and warn-and-drop policy. Frame-dependent checks run in validate_frame() and execute(). Validation errors name read_design(), also when it is called inside replay_design(read_design(path), frame).

The design, frame, and execution blocks are declarative JSON, with method vocabulary version 2. Its descriptors are not a finalized external method vocabulary. The tools.samplyr block holds what rebuilding and replaying the native object needs (exact method names, R classes, the R-derived frame hash, the execution environment). Unknown namespaces in the top-level annotations and tools objects are preserved when a file is written again, but cannot change selection. system.file("schema", "README.md", package = "samplyr") locates the schema and compatibility notes.

See also

replay_design() for reproducing a sample from its receipt, design_json() for in-memory JSON, validate_frame() for checking a frame against a design, get_design() for extracting the design from a sample.

Other serialization: as.list.sampling_design(), design_json(), replay_design()

Examples

design <- sampling_design(title = "Household Survey") |>
  stratify_by(region, alloc = "proportional") |>
  draw(n = 200, method = "systematic", control = c(province, ea_id))

path <- tempfile(fileext = ".json")
write_design(design, path, frame = bfa_eas)

restored <- read_design(path)
restored
#> ── Sampling Design: Household Survey ───────────────────────────────────────────
#> 
#> ℹ 1 stage
#> 
#> ── Stage 1 ─────────────────────────────────────────────────────────────────────
#> • Strata: region (proportional)
#> • Draw: n = 200 (total), method = systematic, control = c(province, ea_id)
#> 

# The restored design executes identically
s1 <- execute(design, bfa_eas, seed = 42)
s2 <- execute(restored, bfa_eas, seed = 42)
identical(s1$ea_id, s2$ea_id)
#> [1] TRUE

# Saving an executed sample records a reproducibility receipt
write_design(s1, path, frame = bfa_eas)
attr(read_design(path), "execution")$seed
#> [1] 42

# Replay the receipt to reproduce the sample exactly
s3 <- replay_design(read_design(path), bfa_eas)
identical(s3$ea_id, s1$ea_id)
#> [1] TRUE

unlink(path)