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.
Arguments
- x
A
sampling_design, atbl_sample(the stored design is saved along with an execution receipt), aframe_stack, or a sample carrying shared weights.- path
File path to write to. Conventionally with a
.jsonextension.- 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 aframe_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.
prettyfollows..., so it is matched exactly and must be named.- pretty
Whether to pretty-print the JSON. Defaults to
TRUEfor files andFALSEfordesign_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)