Skip to contents

Creates a survey::svydesign() object from a tbl_sample, using the sampling design metadata (strata, clusters, weights, and finite population corrections) captured during execute().

Usage

as_svydesign(x, ...)

# S3 method for class 'tbl_sample'
as_svydesign(
  x,
  ...,
  nest = TRUE,
  method = NULL,
  systematic_variance = c("warn", "approximate", "error")
)

Arguments

x

A tbl_sample object produced by execute().

...

Additional arguments passed to survey::svydesign(), or to survey::twophase() for a two-phase sample. In particular, you can pass pps = survey::ppsmat(joint_matrix) to supply exact joint inclusion probabilities instead of the default Brewer approximation (see Details). Every argument must be named, and its name must be one the receiving function accepts: nest and method follow the ... and so are matched exactly, and a near miss such as nes is reported rather than forwarded. The design arguments themselves (ids, strata, weights, probs, fpc, data, and subset for a two-phase sample) are derived from the sample and cannot be given here.

nest

If TRUE (the default), relabel cluster ids to enforce nesting within strata, which suits most complex survey designs. Passed to survey::svydesign(). It has no effect on a two-phase sample, which is exported with survey::twophase(), and giving it there raises a warning.

method

For two-phase samples, the variance method passed to survey::twophase(). One of "full", "approx", or "simple". This argument is only accepted for two-phase samples.

systematic_variance

What to do about the variance approximation used for systematic stages: simple random sampling for systematic, Brewer's for pps_systematic. "warn" (default) applies it and warns once per call, naming every affected stage and its approximation. "approximate" applies it silently, for a caller who has acknowledged it, and "error" refuses. Census stages are exempt, since a stage that took everything within reach contributes no variance, and so is a pps_systematic first stage exported with a caller-supplied pps object. The choice, the affected stages and each stage's approximation are recorded in the "samplyr_systematic_variance" attribute of the result. as_svrepdesign() takes the same argument for its own approximation of these stages.

Value

A survey.design2 object for single-phase and multi-stage samples, or a twophase/twophase2 object for two-phase samples.

Details

Every executed stage contributes one ids term, one fpc term and, when stratified, one strata term, so survey::svydesign() represents the multi-stage structure in its linearization (Sarndal et al. 1992, ch. 4.3):

  • Cluster ids (ids): the cluster_by() variable of a clustered stage. A stage clustered by several variables, which execution treats as one cluster id, gets one synthesized interaction column, because survey::svydesign() reads each formula term as a separate stage. A final unclustered stage gets a synthesized row-identity column so that its sampling variance is represented. A WR or PMR stage uses its .draw_k column, and survey treats each occurrence as independent for Hansen-Hurwitz variance estimation. This is exact for WR and the documented approximation for PMR.

  • Strata (strata): aligned with ids. A stage stratified by several variables exports their cross-classification as one synthesized column, since survey silently ignores extra variables within a stage's term. Trailing unstratified stages are omitted, and unstratified stages before a stratified stage get a constant placeholder column.

  • Weights (weights): the .weight column, the product of the per-stage weights described in sample_columns.

  • FPC (fpc): aligned with ids. survey::svydesign() requires every FPC term on the same scale, so one of two encodings is used:

    • Count scale (designs without unequal-probability WOR stages): .fpc_k, the stratum population count \(N_h\), for equal-probability WOR stages, and a synthetic Inf column (no correction, Hansen-Hurwitz variance) for WR/PMR stages.

    • Fraction scale (multi-stage designs with a PPS WOR, balanced, or custom WOR stage): every WOR stage passes its per-unit stage sampling fraction \(1 / w_k = \pi_k\), and WR/PMR stages pass 0 (no correction). A multi-stage design with a random-size Poisson stage is refused by linearization export.

    A single-stage PPS WOR design passes \(\pi_i\) directly, which survey interprets as inclusion probabilities.

For a two-stage stratified-cluster design with a final element stage, the exported call is equivalent to:

survey::svydesign(
  ids     = ~ ea_id + .id_2,       # stage-1 clusters, stage-2 elements
  strata  = ~ region,              # stage-1 strata
  weights = ~ .weight,             # product of per-stage weights
  fpc     = ~ .fpc_pi_1 + .fpc_f_2,  # per-stage sampling fractions
  data    = sample,
  nest    = TRUE
)

Multi-stage designs

Exactness depends on the variance treatment available for each method (variance_estimation). A design whose first stage is a census of PSUs correctly attributes all variance to the later stages. Executing stages separately, as stage1 <- execute(design, psu_frame, stages = 1) then sample <- execute(stage1, listing_frame), still gives one multi-stage design, exported with survey::svydesign().

An unclustered element-sampling stage followed by further stages is not nested cluster sampling, because the later selections are conditional on the realized element sample, which is phase sampling. execute() refuses it (samplyr_error_stage_parent_id). Express it as a two-phase sample instead: execute the element stage under its first-phase design, then a new second-phase design with that sample as its frame.

Two-phase samples and waves

A new phase-2 sampling_design executed with the phase-1 tbl_sample as its frame, as in phase2 <- execute(design2, phase1), records a previous-phase link, and as_svydesign() calls survey::twophase().

A materialized wave, execute(master, wave = t), is the other two-phase case. Its second phase is the panel activation: within a frozen block the master took a simple random sample without replacement of the realized quota, so the blocks are the phase-2 strata and their sizes the phase-2 population counts. The master is retained as the first phase and supplies the rows the wave did not keep. Columns added to the wave for analysis are carried into the exported design. Wave or phase-2 analysis columns replace same-named first-phase columns and are missing on unsampled rows. Columns absent from the wave remain available from the master, so dropping a wave column does not erase the master's measurements. Design identifiers, strata and internal sampling columns keep their recorded meanings. Keep earlier measurements under separate names if both are needed.

On a stratified master whose stratum means differ strongly relative to the variation within them, the two-phase variance of a total can be negative, and survey::svytotal() returns NaN with base R's sqrt(v): NaNs produced. That is survey::twophase()'s exact estimator, computed only when an estimator is called, so samplyr cannot intercept it. method = "approx" gives a finite standard error. A mean is unaffected.

A clustered first phase needs its second phase drawn inside each of its units, with stratify_by(<unit>), or by taking whole phase-1 units before sampling inside them. In a simulation both gave 0.93 to 1.03 of the true variance and never a negative one. When phase 2 draws smaller units across the phase-1 clusters, the estimator is right over repeated samples but not in one (variance_estimation has the measured error), and the export warns (samplyr_warning_twophase_across_units). The weights are exact either way.

survey::twophase() takes no pps for the first phase, so a two-phase sample or wave whose phase 1 was drawn with unequal probabilities without replacement is refused (samplyr_error_twophase_phase1_pps, samplyr_error_wave_phase1_pps). So are cube, first-stage pps_poisson and the spatial methods, which are unsupported at either phase. Equal-probability first phases export, stratified, clustered, multistage and with-replacement alike. No two-phase sample, a wave included, has a replicate export. The weights of a refused sample are exact, so totals and means are right. For a variance, an ultimate-cluster approximation treats phase 1's units as drawn with replacement and carries everything below in them. It is conservative, by the margins variance_estimation reports, and much more so when phase 2 was drawn across the phase-1 units:

survey::svydesign(ids = ~ea_id, strata = ~region, weights = ~.weight,
                  data = as.data.frame(phase2))

Modified samples and domain analysis

as_svydesign() raises an error on a tbl_sample whose row set changed after execute() (rows removed by dplyr::filter() or [, added, or duplicated by a join) or whose internal design columns (.weight, .weight_k, .fpc_k, ...) were overwritten, dropped, or renamed. The sample is verified against an integrity record stored at execution (row count and a hash of the weights, design metadata, and strata and cluster columns), so changes the dplyr hooks cannot see (base assignment, rbind(), vctrs operations, third-party verbs) are also caught, and an overwrite that left every value identical passes. Row reordering, one-to-one joins, and adding ordinary data columns do not mark the sample. One complete replicate extracted from a replicated execution (filter(.replicate == r)) is verified against the execution metadata and remains supported.

Dropping out-of-domain rows before conversion is not domain estimation. The point estimate can agree, but its variance estimate is generally wrong, often too small, because the domain sample size is random under the design. Convert the full sample first and then subset the design:

svy <- as_svydesign(sample)
survey::svymean(~y, subset(svy, domain))
# or with srvyr:
as_survey_design(sample) |> filter(domain) |> summarise(...)

Variance by selection method

Which estimator each selection method reaches, how exact it is, and which designs are refused are in variance_estimation.

pps is read as follows. "brewer" is the default treatment and keeps every stage. FALSE states that no stage is PPS, so it is refused on a design with one. "overton", survey::HR() and the matrix objects (survey::ppsmat(), survey::poisson_sampling()) are single-stage in survey, so a multi-stage sample is exported at stage 1 with a warning. A matrix object is indexed by row, so it also needs one row per stage-1 unit, and a sample with several is refused (samplyr_error_pps_rows_per_psu). Any other value is refused (samplyr_error_pps_argument). A two-phase or wave export takes no pps, because survey::twophase() does not apply it to the phase-2 variance (samplyr_error_twophase_phase2_pps). A phase 2 of one stage drawn with pps_sampford, pps_cps, pps_brewer, pps_sps or pps_pareto is exported with its joint inclusion probabilities, computed on the phase-1 sample, and needs method = "full". Any other unequal-probability, balanced or spatial phase 2 is refused with the same class.

A shared estimation weight

A sample from share_weights() carries weights for a population other than the one that was selected, so it is exported as its source-target contributions: one row per link, weighted by the recorded coefficient times the source unit's design weight. This gives the generalized weight share total exactly for any link structure, provided every selected source unit has at least one contribution. A selected source unit with no link must still count in the variance, which contribution rows cannot do without inventing a target row, so this route refuses it and names as_svrepdesign() instead. The result has more rows than the transformation returned, but no estimate changes: a total sums the same terms, and a mean's denominator is the estimated target population size either way.

An unequal-probability or random-size source design is also refused here, because its variance comes from a structure indexed by the source sample's rows, which the contribution rows no longer are. Use as_svrepdesign(), which replicates the source design and applies the sharing inside every replicate.

The survey package is required but not imported. It must be installed to use this function.

References

Sarndal, C.-E., Swensson, B. and Wretman, J. (1992). Model Assisted Survey Sampling. Springer.

See also

execute() for producing tbl_sample objects, survey::svydesign() for the underlying function, as_survey_design.tbl_sample for converting directly to a srvyr tbl_svy, as_svrepdesign() for replicate-weight export, as_svydesign.frame_stack() for overlapping frames

Other survey export: as_survey_design.tbl_sample(), as_survey_rep.tbl_sample(), as_svrepdesign(), stack_waves(), variance_estimation, variance_estimators()

Examples

# Stratified sample -> survey design
sample <- sampling_design() |>
  stratify_by(region, alloc = "proportional") |>
  draw(n = 300) |>
  execute(bfa_eas, seed = 42)

svy <- as_svydesign(sample)
survey::svymean(~households, svy)
#>              mean     SE
#> households 71.668 3.6922

# Two-stage sample: PPS selection of EAs, then households from a listing
selected <- sampling_design() |>
  add_stage() |>
    stratify_by(region) |>
    cluster_by(ea_id) |>
    draw(n = 5, method = "pps_brewer", mos = households) |>
  add_stage() |>
    draw(n = 8) |>
  execute(bfa_eas, stages = 1, seed = 2025)
listing <- selected |>
  as.data.frame() |>
  dplyr::reframe(hh_id = seq_len(households), .by = ea_id)
sample <- execute(selected, listing, seed = 2026)

# Brewer's variance approximation at the PPS stage
svy <- as_svydesign(sample)

# A joint-probability matrix instead, for a single-stage Sampford sample,
# whose matrix is exact. A matrix needs one row per stage-1 unit.
sampford <- sampling_design() |>
  stratify_by(region) |>
  draw(n = 5, method = "pps_sampford", mos = households) |>
  execute(bfa_eas, seed = 2025)
jip <- joint_expectation(sampford, bfa_eas)
svy_joint <- as_svydesign(sampford, pps = survey::ppsmat(jip[[1]]))
survey::svytotal(~population, svy_joint)
#>               total     SE
#> population 20854071 297547