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_sampleobject produced byexecute().- ...
Additional arguments passed to
survey::svydesign(), or tosurvey::twophase()for a two-phase sample. In particular, you can passpps = 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:nestandmethodfollow the...and so are matched exactly, and a near miss such asnesis reported rather than forwarded. The design arguments themselves (ids,strata,weights,probs,fpc,data, andsubsetfor a two-phase sample) are derived from the sample and cannot be given here.- nest
If
TRUE, relabel cluster ids to enforce nesting within strata. Passed tosurvey::svydesign(). Default isTRUE, which is appropriate for most complex survey designs. It has no effect on a two-phase sample, which is exported withsurvey::twophase(). 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 simple random sampling variance approximation used for equal-probability
systematicstages."warn"(default) applies it and warns once per call, naming every affected stage."approximate"applies it silently for a caller who has acknowledged it, while"error"refuses. Census stages are exempt, since a stage that took everything within reach contributes no variance, andpps_systematicis unaffected, having its own treatment. The choice and the affected stages are recorded on the returned object in the"samplyr_systematic_variance"attribute.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
The conversion maps samplyr's design specification to the arguments
expected by survey::svydesign():
Cluster ids (
ids): one formula term per executed stage. Clustered stages use thecluster_by()variable and when a stage clusters by several variables, their combination (which execution treats as a single cluster id) is collapsed into one synthesized interaction column, becausesurvey::svydesign()reads each formula term as a separate sampling stage. A final unclustered stage (elements sampled within the previous stage's clusters) gets a synthesized row-identity column so that its sampling variance is represented. For WR/PMR stages, the.draw_kcolumn is used as the sampling unit identifier instead (each draw is treated as an independent unit for Hansen-Hurwitz variance estimation).Strata (
strata): one term per stage, aligned withids. A stage stratified by several variables exports their cross-classification as a single synthesized interaction column (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.weightcolumn i.e. the compound weight across all stages (i.e., the product of per-stage weights \(w = \prod w_k = \prod 1/\pi_k\)). This is the inverse of the overall inclusion probability and is the correct weight for design-based point estimation (\(\hat{Y} = \sum w_i y_i\)).FPC (
fpc): one term per stage, aligned withids. Becausesurvey::svydesign()requires every FPC term on the same scale, two encodings are used:Count scale (designs without unequal-probability WOR stages):
.fpc_k(the stratum population count \(N_h\)) is passed for equal-probability WOR stages. A syntheticInfcolumn (no correction, Hansen-Hurwitz variance) is used for WR/PMR stages and for random-size Poisson stages after the first.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\). WR/PMR and later Poisson stages pass 0 (no correction).
A single-stage PPS WOR design passes \(\pi_i\) directly, which survey interprets as inclusion probabilities.
Multi-stage designs
Every executed sampling stage is represented in the exported design:
one ids term, one fpc term, and (when stratified) one strata
term per stage, so survey::svydesign() represents the multi-stage
structure in its linearization (Sarndal et al. 1992, ch. 4.3). Exactness
still depends on the variance treatment available for each method. A
design whose first stage is a census of PSUs correctly attributes
all variance to the later stages.
Operational execution does not change this classification. For example,
stage1 <- execute(design, psu_frame, stages = 1) followed by
sample <- execute(stage1, listing_frame) remains one multi-stage design.
The partial tbl_sample stores the same design plus the realized PSU
selection and the final sample records all executed stages and
as_svydesign() calls survey::svydesign(), not survey::twophase().
A two-phase sample has a different provenance. A new phase-2
sampling_design is executed with the phase-1 tbl_sample as its frame,
for example phase2 <- execute(design2, phase1). That execution records a
previous-phase link, and as_svydesign() calls survey::twophase().
A materialized wave, execute(master, wave = t), is the other two-phase
provenance. Its second phase is the panel activation rather than an
executed design: 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, which survey::twophase() needs to build that phase. Columns added
to the wave for analysis are carried into the exported design. Columns the
master already has keep the master's values.
The activation is exact, but the first phase is only as expressible as
survey::twophase() allows, which takes no pps specification there. A
wave of a master with unequal inclusion probabilities is therefore refused
rather than exported with a with-replacement approximation: pps_*
methods, cube, first-stage pps_poisson, and the spatial methods, whose
variance family is unsupported at either phase. Waves of equal-probability
masters export, stratified, clustered, multistage and with-replacement
alike. as_svrepdesign() does not build replicate weights for any
two-phase sample, a wave included.
An unclustered element-sampling stage followed by further stages is not
nested cluster sampling (the later selections are conditional on the realized
element sample, i.e. phase sampling). It can't be represented currently and
as_svydesign() raises an error. Express such designs as two-phase samples instead.
Execute the element stage under its first-phase design, then execute a new
second-phase design with that sample as its frame. This exports via survey::twophase().
Concretely, 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
)Modified samples and domain analysis
The conversion requires a sample whose rows still match the executed
design. A tbl_sample whose row set was 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 is marked as
modified, and as_svydesign() raises an error. The check is
authoritative, not just mark-based: the sample is verified against
an integrity record (row count and a hash of the weights, design
metadata, and strata/cluster columns) stored at execution, so
modifications through routes 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. Physically dropping out-of-domain
rows before conversion is not equivalent to domain estimation, in that
situation the point estimate can agree, but its variance estimate is
generally wrong and is often too small because the domain sample size is
random under the design.
For subpopulation estimates, convert the full sample first and then subset the design, which applies the proper domain estimator:
svy <- as_svydesign(sample)
survey::svymean(~y, subset(svy, domain))
# or with srvyr:
as_survey_design(sample) |> filter(domain) |> summarise(...)Row reordering, one-to-one joins, and adding ordinary data columns
do not mark the sample. Extracting one complete replicate from a
replicated execution (filter(.replicate == r)) is verified against
the execution metadata and remains supported.
Equal-probability systematic sampling
systematic stages are exported with the SRSWOR variance estimator,
the standard approximation for systematic sampling. Depending on the
frame ordering (see the control argument of draw()), the true
variance can be smaller (favorable ordering) or larger (periodic
ordering) than this estimate.
The size of that gap is worth stating, because it is not always small. A systematic design with interval \(k\) has only \(k\) distinct samples per stratum, so its variance for one particular frame is a fixed quantity that need not sit near the SRSWOR value. Measured over repeated draws, 90 per stratum from 1800 with an interval of 20:
| frame order | reported / true variance | coverage of a 95% interval |
| random | 0.73 | 90.2% |
| ordered by a trend | 1.76 | 100.0% |
| period equal to the interval | 0.0006 | 9.8% |
A favourable ordering is therefore conservative, which is the usual reason for choosing one, and a frame whose structure resonates with the sampling interval is not merely imprecise but reports intervals that almost never cover. Even an unstructured ordering is not guaranteed close. Where the frame may carry periodicity, prefer a randomized order or a design whose variance is estimable, and treat the exported standard error for a systematic stage as an approximation whose direction depends on the frame.
Because a returned survey.design carries no sign that its variance model
is approximate, the export says so once: see systematic_variance. The
approximation is still supplied, since one systematic sample generally does
not identify its own design variance, and refusing would leave the caller
with nothing.
Replicate weights are no way around this. as_svrepdesign() takes the same
systematic_variance argument, because no replicate type on offer
reconstructs a systematic sample's random start or its dependence on frame
order. Each resamples the realized sample as though its units had been
drawn independently within strata. Requesting a particular type is
therefore not an acknowledgement, and does not silence the condition. The
figures in the table above were measured for the linearization export and
are not a measurement of the replicate one.
Variance estimation for PPS designs
For fixed-size PPS without-replacement stages (pps_brewer,
pps_systematic, pps_cps, pps_sampford, pps_sps, pps_pareto),
variance is estimated by default using Brewer's approximation (pps = "brewer" in survey's terminology), which approximates the joint inclusion
probabilities from the marginal inclusion probabilities. Here Brewer names
the variance estimator, not the selection algorithm e.g Sampford selection
receives this default treatment. This is the approximation
described by Berger (2004). Its accuracy depends on the sampling design and
population. It is not an exact substitute for joint inclusion probabilities.
For supported methods, you can instead compute joint inclusion
probabilities using joint_expectation() and pass them via pps = survey::ppsmat(joint_matrix). The matrix is exact for CPS, Sampford,
systematic PPS, and Poisson selection. Generalized Brewer, SPS, Pareto, and
unconstrained cube use the documented high-entropy approximation.
Spatial and constrained balanced methods
Bounded cube, LPM2, and SCPS alter pairwise selection behavior beyond the
available linearization approximation. as_svydesign() therefore refuses
these designs, and joint_expectation() does not provide a matrix for them.
Use as_svrepdesign(type = "subbootstrap") or "mrbbootstrap" for a
generic PPS bootstrap approximation. These replicates do not recreate the
count constraints or spatial algorithm and are not an exact,
design-specific variance estimator.
Random-size Poisson methods
Methods bernoulli and pps_poisson select units independently
with known marginal inclusion probabilities, so the realized
sample size is random. The standard SRSWOR variance estimator
is not appropriate, and Brewer's approximation (designed for
fixed-size PPS) understates the variance. Instead, these
methods are exported with pps = survey::poisson_sampling(pi),
which produces the Horvitz-Thompson Poisson variance estimator
\(\hat V = \sum_{i \in S} (1 - \pi_i) / \pi_i^2 \cdot y_i^2\)
described in Sarndal, Swensson and Wretman (1992), section 2.8.
This applies under the following conditions.
Single-stage designs (no
cluster_by(), orcluster_by()with one row per sampled cluster) are exported withpoisson_sampling()and produce the exact Horvitz-Thompson Poisson variance.Multi-stage designs with a random-size Poisson method at stage k > 1 omit the finite-population correction at the Poisson stage (the same handling used for with-replacement methods). The Poisson stage is treated as sampled with replacement, which is mildly conservative.
Multi-stage designs with a random-size Poisson method at stage 1 are not supported by
survey::svydesign(), which rejects multi-stage designs when theppsargument is set. Such designs raise an error suggestingas_svrepdesign(type = "subbootstrap").Single-stage designs that use
cluster_by()with multiple rows per sampled cluster (for example a household listing within sampled EAs) raise an error.survey::poisson_sampling()treats rows as independent and does not honor within-cluster correlation. Useas_svrepdesign(type = "subbootstrap")for these designs.Custom methods registered with
fixed_size = FALSE(sondage::register_method()) are also random-size, but samplyr cannot verify that their selections are independent across units, which the Poisson estimator requires. The method author can settle this at registration: a method registered withvariance_family = "poisson"asserts independent selections and is exported throughpoisson_sampling()exactly like the built-ins above. Undeclared methods raise an error. If you know the method is Poisson-type, pass the probabilities explicitly:as_svydesign(x, pps = survey::poisson_sampling(1 / x$.weight)), or useas_svrepdesign(type = "subbootstrap").
Declared variance families for custom methods
sondage::register_method() accepts a variance_family declaration
("srs", "pps_brewer", "poisson", "wr", "unsupported").
When present it overrides the classification samplyr would otherwise
infer from the method's type and fixed_size: "srs" receives the
equal-probability treatment (count-scale FPC), "pps_brewer" the
fixed-size PPS treatment (Brewer approximation), "poisson" exact
Poisson linearization, and "wr" the with-replacement treatment.
A method declared "unsupported" cannot be linearized at all:
as_svydesign() refuses with an error and
as_svrepdesign(type = "subbootstrap") remains the escape hatch.
Chromy's sequential PPS method (PMR)
pps_chromy is classified as a Probability Minimum Replacement
(PMR) method which is neither with-replacement nor without-replacement.
Each unit receives exactly \(\lfloor E(n_i) \rfloor\) or
\(\lfloor E(n_i) \rfloor + 1\) hits, where
\(E(n_i) = n \cdot \textrm{mos}_i / \sum \textrm{mos}\).
When all expected hit counts are below 1, this reduces to WOR,
otherwise large units receive multiple hits.
For variance estimation, Chromy (2009) recommends the
Hansen-Hurwitz (with-replacement) approximation rather than
exact pairwise expectations, which he found "quite variable."
Chauvet (2019) confirmed this in simulation. Accordingly,
as_svydesign() treats pps_chromy stages like
with-replacement stages (no FPC, no pps argument).
Note that survey::ppsmat() is not valid for the general
PMR case. The survey package reads \(\pi_i\) from the diagonal
of the joint matrix, but for PMR the diagonal contains
\(E(n_i^2)\), which differs from \(E(n_i)\) when units
receive multiple hits. The generalized Sen-Yates-Grundy variance
requires \(E(n_i) E(n_j) - E(n_i n_j)\) as the pairwise
weight (Chromy 2009, eq. 5), not \(E(n_i^2) E(n_j^2) - E(n_i n_j)\).
Certainty stratum (take-all units)
For stages exported under the PPS without-replacement (Brewer)
treatment, units with inclusion probability \(\pi_i = 1\)
are placed in a separate take-all stratum. This covers every route to
probability one, whether a certainty_size or certainty_prop rule
named the unit or the probability calculation capped it, and it covers
balanced (cube) designs alongside the PPS methods. Random-size designs
(bernoulli, pps_poisson) keep the Poisson treatment and form no
take-all stratum, even when some probabilities equal one. This follows
the standard practice from Cochran (1977, ch. 11) and Sarndal et al.
(1992, ch. 3.5): the take-all stratum contributes zero variance (it is
a census) and does not inflate the degrees of freedom for the
probability stratum.
Splitting certainty units out of a user stratum can leave a single
probability unit behind. Such a stratum has no estimable
within-stratum variance, and survey signals a lonely PSU rather than
returning a number. This reflects the design: set
options(survey.lonely.psu = "adjust") for the conservative
population-mean centering, or collapse the affected strata before
export.
For stages using with-replacement methods (srswr,
pps_multinomial), the finite population correction is omitted
and the .draw_k column (sequential draw index) is used as the
sampling unit identifier for Hansen-Hurwitz variance estimation.
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. The generalized weight share total
is the Horvitz-Thompson total of a variable derived on the source units,
and expanding the contributions is what lets survey form that variable
inside each sampling unit for whatever is being analyzed. It is exact for
any link structure, with no condition on how many source units reach a
target.
The rows of the result are contributions rather than target units, so there are more of them than the transformation returned. No estimate is affected: a total sums the same terms, and a mean's denominator is the estimated size of the target population either way.
An unequal-probability or random-size source design is refused on this
route. Both take their variance from a structure indexed by the rows of
the source sample, and those rows are no longer the sampled units once
each appears per contribution. Use as_svrepdesign() there, 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
Berger, Y.G. (2004). A Simple Variance Estimator for Unequal Probability Sampling Without Replacement. Journal of Applied Statistics, 31, 305-315.
Brewer, K.R.W. (2002). Combined Survey Sampling Inference (Weighing Basu's Elephants). Chapter 9.
Chauvet, G. (2019). Properties of Chromy's sampling procedure. arXiv:1912.10896.
Chromy, J.R. (2009). Some Generalizations of the Horvitz-Thompson Estimator. JSM Proceedings, Survey Research Methods Section.
Cochran, W.G. (1977). Sampling Techniques. 3rd edition. Wiley.
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()
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 cluster sample with PPS first stage
sample <- sampling_design() |>
add_stage() |>
stratify_by(region) |>
cluster_by(ea_id) |>
draw(n = 5, method = "pps_brewer", mos = households) |>
add_stage() |>
draw(n = 12) |>
execute(bfa_eas, seed = 2025)
#> Warning: Stage 2: selected every unit available in the pools it executed.
#> ✖ Requested 780 units, selected all 65 available.
#> ℹ Exhausted pools: "33131", "33250", "10550", "36826", "44761", and 60 more.
#> ℹ This stage contributes no sampling variance. Earlier stages are unaffected:
#> the design as a whole is a census only if every stage is.
# Default: Brewer variance approximation
svy <- as_svydesign(sample)
# Exact: compute joint probabilities from frame
jip <- joint_expectation(sample, bfa_eas, stages = 1)
svy_exact <- as_svydesign(sample, pps = survey::ppsmat(jip[[1]]))
#> Warning: Exact PPS variance (`pps`) is single-stage in survey.
#> ℹ Exporting the stage-1 design only; later-stage sampling variance is not
#> represented.
#> ℹ Omit `pps` for multi-stage linearization with Brewer's approximation at the
#> PPS stage.