Skip to contents

This vignette documents what each samplyr operation assumes, what it computes, and what is preserved or lost at survey export.

Use this as a topic reference while working through the tutorials. The main reading path is vignette("introduction"), then vignette("survey-analysis").

Components, scope and transitions

A design is a sequence of stages. Each stage declares optional strata and allocation, an optional sampling-unit key, and one selection specification. draw() completes the stage, which then takes no further strata, clusters or selection, so a design is extended with add_stage(). execute() interprets it against frames. Without allocation, scalar n applies per stratum. With allocation it is a total over those strata. At later stages, both meanings apply within each selected parent pool. For random-size methods, the target is an expectation.

Operation Input Result and statistical meaning
sampling_design() and specification verbs Column names and selection rules An inspectable specification, with no sample drawn
frame_summary(design, frame) Specification and registers Resolved pool targets and chances, without random draws
execute(design, frame) Specification and population register(s) A realized sample with selection metadata and weights
execute(partial_sample, listing) An unfinished design’s sample and its next register Continuation with completed-stage probabilities retained
execute(new_design, previous_sample) A new design and an earlier realized sample New phase with conditional selection factors compounded with prior weights
execute(master, wave = t) A master sample with a panel schedule Activation by conditional selection under the frozen panel quotas
as_svydesign(sample) or as_svrepdesign(sample) A supported sample and collected observations An analysis representation, subject to the inference restrictions

The composition laws are that previews agree with execution’s resolved chances, continuation under the same frame order and uninterrupted RNG stream agrees with one-call execution, and serialization preserves the declared specification. Weight interpretation must survive every transformation, and an export must preserve the required stage information or refuse the design. Agreement of numerical targets does not turn approximate probabilities into exact ones. Phase-wise conditional factors also need not be unconditional marginal inclusion probabilities.

Assumptions at a glance

For a compact selection table, see ?selection_methods, and for how each method’s variance is estimated, ?variance_estimation. A complete three-stage fieldwork example is in vignette("introduction").

The following table summarizes what samplyr assumes and what the user must verify. Each row is expanded in the sections below.

Assumption Applies to samplyr behavior User must verify
Strata are exhaustive and disjoint stratify_by() Partitions the frame by unique combinations of strata variables Strata variables define a valid partition
Independent sampling across strata stratify_by() |> draw() Each stratum sampled with a separate call to sondage, except stratified cube Correct by construction when strata are disjoint. Stratified cube balances across strata in one call
Cluster variables constant within clusters cluster_by() Validated at execution: MOS and strata variables may not vary within a cluster Cluster ID, read within its strata and earlier-stage units, identifies the group
Conditional selection laws add_stage() Stage \(k\) sampling depends only on the units selected at stage \(k - 1\) and its declared inputs Later frame construction and selection depend on prior randomization only through selected ancestors and recorded design variables
FPC records method-specific stage information execute() Stores a population or cluster count for WOR and Inf for WR or PMR. PPS WOR is converted to \(\pi_i\) at survey export Frame size at execution is treated as the population, one of several definitions when a population changes between waves
Weights are inverse selection chances, unadjusted All methods Uses inclusion probabilities for WOR and expected hit counts for WR or PMR Downstream adjustment is the analyst’s responsibility
PRN values are iid \(\text{U}(0, 1)\) draw(prn = ...) Validated: strictly in \((0, 1)\), no NAs, numeric Independence and uniformity of the PRN column

Notation

Throughout this vignette:

  • \(U = \{1, \ldots, N\}\): finite population (the frame).
  • \(S \subseteq U\): selected population units for a WOR design.
  • \(K_i\): number of realized hits for unit \(i\) in a WR or PMR design.
  • \(q_i\): stage chance, equal to \(\pi_i = \Pr(i \in S)\) for WOR and \(E(K_i)\) for WR or PMR.
  • \(\pi_{kl} = \Pr(k \in S \text{ and } l \in S)\): joint (second-order) inclusion probability for WOR.
  • \(E(K_k K_l)\): joint expected hits for WR or PMR.
  • \(w_i = 1 / q_i\): design weight per selected unit or realized occurrence.
  • \(N_h\): number of the stage’s sampling units in stratum \(h\) (\(h = 1, \ldots, H\)), so PSUs at a clustered stage.
  • \(n_h\): sample size drawn from stratum \(h\).
  • \(K\): number of sampling stages.
  • \(\pi_i^{(k \mid S^{(k-1)})}\): conditional inclusion probability of unit \(i\) at stage \(k\), given the set \(S^{(k-1)}\) selected at stage \(k - 1\).

execute() produces design weights only. Calibration, trimming, and nonresponse adjustment are out of scope and belong in the estimation layer (survey, srvyr, or similar).

One declared transformation departs from that, and says so in the object it returns: share_weights() produces estimation weights for a population other than the one that was sampled. Its semantics, and the contract that keeps the two kinds of weight apart, are the subject of a section below.

What each verb means

sampling_design(): frame-independent plan

A sampling_design object is a specification, not a computation. Column names referenced in stratify_by(), cluster_by(), and draw() are stored as strings (deferred resolution). No data is touched until execute() binds the design to a frame. This separation means the same design can be applied to different frames or re-executed with different seeds.

library(samplyr)
library(dplyr)

design <- sampling_design(title = "SRS WOR") |>
  stratify_by(region) |>
  draw(n = 50, method = "srswor")

# No data has been sampled yet
design
#> ── Sampling Design: SRS WOR ────────────────────────────────────────────────────
#> 
#> ℹ 1 stage
#> 
#> ── Stage 1 ─────────────────────────────────────────────────────────────────────
#> • Strata: region
#> • Draw: n = 50 (per stratum), method = srswor

stratify_by(): partition into independent sub-populations

stratify_by() partitions the frame into \(H\) strata defined by the Cartesian product of its variables:

\[ U = U_1 \cup U_2 \cup \cdots \cup U_H, \qquad U_h \cap U_{h'} = \emptyset \; \text{for } h \neq h'. \]

Sampling within each stratum is independent. This holds by construction because samplyr invokes sondage algorithms separately for each stratum. Stratified cube is the exception: its global flight phase balances across strata, so its strata are not selected independently.

When an allocation method is specified, the total sample size \(n\) is distributed across strata:

Allocation Formula Requires
"equal" \(n_h = n / H\)
"proportional" \(n_h \propto N_h\), or \(X_h\) if given importance, optional
"neyman" \(n_h \propto N_h \, S_h\) variance
"optimal" \(n_h \propto N_h \, S_h / \sqrt{C_h}\) variance, cost
"power" \(n_h \propto \text{CV}_h \cdot X_h^q\) cv, importance

Each formula gives a factor \(w_h\). The allocation is the solution of

\[ n_h = \operatorname{clamp}(\lambda w_h, \; \ell_h, \; u_h), \qquad \ell_h = \min(\texttt{min\_n}, N_h), \qquad u_h = \min(\texttt{max\_n}, N_h) \]

with \(\lambda\) chosen so that \(\sum_h n_h = n\). The population size \(N_h\) is an upper bound whether or not max_n was supplied, so a fixed-size without-replacement design satisfies \(0 \le n_h \le N_h\) and \(\sum_h n_h = n\) for every feasible request.

Saturated strata are fixed at their bound and removed from the problem, then \(\lambda\) is recomputed over the strata still free. Redistribution therefore follows the same factors \(w_h\) the method is defined by, which is what keeps a Neyman allocation Neyman after a stratum saturates: redistributing in proportion to unused capacity instead would silently change the criterion. When every free stratum has \(w_h = 0\) the criterion cannot distinguish them and the remainder is split equally in stratum order.

One consequence is worth stating plainly: a named rule can be departed from when a stratum is too small to hold its share. With populations \((10, 490, 500)\) and \(n = 300\), "equal" yields 10/145/145, not 100/100/100. execute() reports this once per run.

The constrained real-valued targets are then rounded with largest-remainder (Hare-Niemeyer) rounding that preserves \(\sum n_h = n\) and stays inside every bound.

With-replacement (srswr, pps_multinomial) and minimum-replacement (pps_chromy) designs can select a unit more than once, so \(N_h\) is not a bound on the number of draws and only min_n and max_n apply. Per-stratum sizes supplied directly, rather than through alloc, are instructions and are never redistributed.

cluster_by(): define the sampling unit

cluster_by() declares the variable(s) that identify the primary sampling unit (PSU) at the current stage. Operationally, samplyr:

  1. Groups the frame by the cluster variable(s).
  2. Draws a sample of clusters (not individual units).
  3. Returns all population units within selected clusters.

Any variable used in PPS size measures (mos) or in stratification must be constant within each cluster. samplyr validates this at execution time and raises an error if cluster-level variables vary within groups.

cluster_by() is purely structural. It does not affect inclusion probabilities directly. They are determined by draw() applied to the cluster-level aggregates.

Cluster ids may be numbered within the level above them. A unit is identified by its id together with the units earlier stages selected and the strata of its own stage, so town 1 of county A and town 1 of county B are two towns. cluster_by(nest = FALSE) instead declares the ids unique across strata and turns that into a check, which refuses a cluster coded into two strata instead of splitting it. ?cluster_by gives the rules, including what a separate register for a later stage must carry.

draw(): specify the selection mechanism

draw() attaches a selection method and sample size (or fraction) to the current stage. The inclusion probabilities and weights depend on the method class:

Equal-probability without replacement (srswor, systematic, bernoulli):

\[ \pi_i = \frac{n}{N} \quad (\text{or } \pi_i = f \text{ for bernoulli}), \qquad w_i = \frac{1}{\pi_i}. \]

Fixed-size for srswor and systematic and random-size for bernoulli. Systematic sampling induces implicit stratification by row order. Use control to specify the sort variable.

PPS methods (pps_brewer, pps_systematic, pps_cps, pps_sampford, pps_poisson, pps_sps, pps_pareto):

\[ \pi_i = n \, \frac{\text{mos}_i}{\sum_j \text{mos}_j} \]

computed by sondage::inclusion_prob(), which iteratively caps units with \(\pi_i \geq 1\) and redistributes. These “certainty” units (\(\pi_i = 1\)) receive stage weight 1 and contribute zero first-stage variance. In multi-stage designs, final .weight still compounds all stages, so certainty at one stage does not imply overall weight 1. For Poisson sampling, \(\pi_i = f \cdot N \cdot \text{mos}_i / \sum \text{mos}\), capped at 1 (random-size design).

Two of the seven attain that \(\pi_i\) only approximately. pps_sps and pps_pareto are order sampling designs, which fix the sample size at the cost of the marginals: the quantity above is the target inclusion probability handed to the algorithm, and the realized inclusion probability is close to it but not equal, approaching it as \(n\) and \(N\) grow (Tillé 2020, sec. 5.11; Rosén 1997). Both sit in the "approximate" probabilities tier. Everywhere below that reads \(\pi_i\) off .weight inherits that distinction. The other five methods satisfy it exactly.

With-replacement and PMR (srswr, pps_multinomial, pps_chromy):

The quantity of interest is the expected number of hits \(E(n_i) = n p_i = n \, \text{mos}_i / \sum \text{mos}\). If unit \(i\) is selected \(k\) times, samplyr creates \(k\) rows with a .draw column indexing each occurrence. The weight on every output row is \(w_i = 1 / E(n_i) = 1 / (n p_i)\). For WR the draws are independent and this is the per-hit Hansen-Hurwitz estimator. No finite population correction applies (FPC = \(\infty\)).

Chromy’s sequential PPS (pps_chromy) is classified as probability minimum replacement (PMR): each unit receives \(\lfloor E(n_i) \rfloor\) or \(\lceil E(n_i) \rceil\) hits. Its occurrences are not independent WR draws. At survey export, samplyr uses the Hansen-Hurwitz approximation documented below. When all \(E(n_i) < 1\), the selection itself reduces to WOR.

Balanced and spatially balanced sampling (cube, lpm2, scps):

The cube method (Deville and Tillé 2004) selects a fixed-size sample that satisfies (or nearly satisfies) the balancing equations \(\sum_{i \in S} a_{ij} / \pi_i = \sum_{i \in U} a_{ij}\) for auxiliary variables \(a_j\) specified via aux. Without mos, \(\pi_i = n/N\) and probabilities are equal. With mos, \(\pi_i\) is computed by sondage::inclusion_prob(). A bound() marker adds hard adjacent-integer category-count constraints.

When stratified, the stratified cube algorithm (Chauvet 2009) runs flight phases per stratum, then a global flight phase, then per-stratum landing phases.

LPM2 and SCPS instead use the coordinates supplied through spread. They do not accept cube auxiliaries or count bounds. At most 2 stages may use a balanced-family method. Certainty selection is not supported, and of the two only SCPS accepts a PRN.

PRN-compatible methods (bernoulli, pps_poisson, pps_sps, pps_pareto, scps):

When prn is specified, the permanent random numbers are passed through to sondage. Selection becomes deterministic given the PRN values, so the seed argument to execute() has no effect on the selection outcome. For scps it is also fixed by the order in which the pool’s units are visited.

add_stage(): delimit stages

add_stage() is syntactic: it closes the current stage and opens a new one. It carries no statistical content of its own. Stages are numbered \(1, 2, \ldots, K\) in definition order.

execute(): materialize the sample

execute() binds the design to one or more frames and runs the selection:

  1. For each stage \(k\), the frame is restricted to units belonging to clusters selected at stage \(k - 1\) (or the full frame for \(k = 1\)).
  2. Strata and cluster groupings are resolved against the (subsetted) frame.
  3. The selection algorithm from draw() is invoked via sondage.
  4. The .weight, .fpc, .draw, and .certainty columns are computed where they apply and suffixed with the stage number (?sample_columns).

One frame or one per stage. The number of frames schedules the stages. execute(design, hierarchy) runs every stage against one table, and execute(design, schools, classes, students) gives each stage its own register, mapped by position. Any other count is refused. The frames may equally be held as a value with execute(design, list(schools, classes, students)). A name on a list element is a diagnostic label for that frame.

A register is supplied whole. samplyr restricts it to the units its parent stage selected and carries onto it the variables earlier stages introduced, so a class register needs neither pre-filtering to the sampled schools nor a copy of the school-level stratification column. A register that omits such a carried variable is fine. One whose own copy disagrees with the parent is an error.

Partial execution. execute(design, psu_frame, stages = 1) runs only the first stage and returns a tbl_sample. That object stores both the unchanged design and the realized PSU selection. Use it as the first argument of the later call and supply the new listing as the frame: execute(psu_sample, listing_frame). Execution resumes at the next unexecuted stage, carries forward the PSU weights and FPC, and remains one multi-stage design.

The stages argument may select any initial contiguous batch beginning at stage 1 and on a partial tbl_sample, it may select the next contiguous batch. Omitting it executes all remaining stages, with one exception. If more than one stage remains and a single frame is supplied, that frame could be the next stage’s register or a hierarchy covering the rest. The two draw different samples, so execute() asks for stages rather than guessing.

The three spellings are one implementation. A three-stage design executed against one hierarchy, against three registers in a single call, or stage by stage as a continuation runs the same stage transition each time. All three draw the same sample, provided the continuation runs under a single RNG stream and names stages on every intermediate call:

one_call     <- execute(design, schools, classes, students, seed = 7)
hierarchical <- execute(design, hierarchy, seed = 7)

continued <- withr::with_seed(7, {
  s1 <- execute(design, schools, stages = 1)
  s2 <- execute(s1, classes, stages = 2)
  execute(s2, students, stages = 3)
})

Note where the seed goes. One seed wraps the whole chain, and the individual calls take none. Seeding each call separately is a different and equally valid RNG boundary, but it is not this equivalence and will select different units. The equivalence is a tested invariant rather than an incidental property. The continued sample is therefore the one the single call would have drawn.

Two-phase sampling. A new phase starts with a new design: execute(design2, phase1_sample). Here the phase-1 tbl_sample is the frame argument, not the execution object. samplyr records a previous-phase link and compounds the phase-1 and phase-2 weights multiplicatively. The conditional phase-2 weight is computed at survey export time. Thus execute(psu_sample, listing_frame) means “continue this design’s stages,” whereas execute(new_design, prior_sample) means “sample a new phase.”

Weight compounding across stages

Single-stage. Let \(q_i\) be the stage selection quantity. For WOR, \(q_i = \pi_i\). For WR or PMR, \(q_i = E(K_i)\), the expected hit count. The design weight is:

\[ w_i = \frac{1}{q_i}. \]

Multi-stage. Let \(q_i^{(k)}\) denote the quantity resolved within the clusters selected at prior stages. The compound weight is:

\[ w_i = \prod_{k=1}^{K} \frac{1}{q_i^{(k)}} = \prod_{k=1}^{K} w_i^{(k)}. \]

For an all-WOR exact design this is the inverse product of conditional inclusion probabilities. With a WR or PMR stage, each output row is a realized occurrence and that stage contributes an inverse expected-hit factor. samplyr joins each stage’s weight to its parent by the shared cluster variables and multiplies. The per-stage weights remain in .weight_1, .weight_2, and so on. The .weight column always equals their product.

two_stage <- sampling_design() |>
  add_stage("Districts") |>
    stratify_by(province) |>
    cluster_by(district) |>
    draw(n = 2, method = "srswor") |>
  add_stage("EAs") |>
    draw(n = 5, method = "srswor") |>
  execute(zwe_eas, seed = 123456)
#> Warning: Stage 1: sample size exceeded the pool population in 1 of 10 pools.
#> ✖ Requested 20 units, selected 19.
#> ℹ Capped pools: "Bulawayo".
#> ℹ Inspect with `frame_summary(sample, detail = "pool")` and the capped column.

# Compound weight equals the product of per-stage weights
all.equal(two_stage$.weight,
          two_stage$.weight_1 * two_stage$.weight_2)
#> [1] TRUE

The warning is the design meeting the frame. Bulawayo is a single-district province, so a take of two districts selects the one that exists. execute() reports every pool it could not fill, once per stage, because a capped pool makes the design non-self-weighting. The weights above still compound exactly and Bulawayo’s first-stage weight is simply 1.

Two-phase. When a new phase-2 design is executed with the phase-1 tbl_sample as its frame, the phase-1 weight is stored internally and multiplied into the final .weight. The conditional phase-2 weight \(w_{\text{cond}} = w_{\text{overall}} / w_{\text{phase 1}}\) is computed during survey export for use with survey::twophase().

Finite population corrections

samplyr stores the FPC as a population count in .fpc_k. The interpretation depends on the method, and the conversion for survey export happens automatically in as_svydesign():

Method class .fpc_k stores Passed to survey as survey interpretation
EP-WOR (srswor, systematic) \(N_h\) (stage units) \(N_h\) directly Sampling fraction \(f_h = n_h / N_h\)
PPS-WOR (pps_brewer, …) \(N_h\) (stage units) \(1 / w_k = \pi_i\) Inclusion probability
WR / PMR (srswr, pps_chromy, …) \(\infty\) (synthetic) \(\infty\) No FPC (Hansen-Hurwitz variance)

The PPS conversion merits explanation. survey::svydesign() interprets FPC values less than 1 as inclusion probabilities and values \(\geq 1\) as population sizes. samplyr stores \(N_h\) for every without-replacement method, then at export time, as_svydesign() creates a per-row column .fpc_pi_k = 1 / .weight_k = \pi_i for PPS-WOR stages. The transformation is lossless: the per-stage weight already encodes \(\pi_i\), so nothing is approximated in converting it. What is approximate for pps_sps and pps_pareto is the quantity being transformed, since their \(\pi_i\) is a target rather than the realized inclusion probability.

For WR and PMR methods, a synthetic column filled with Inf is created. The survey package interprets this as “no finite population correction,” which gives the Hansen-Hurwitz variance estimator.

The table describes one represented stage, or several without a PPS-WOR stage. survey::svydesign() needs every FPC term on the same scale, so a multi-stage design with a PPS-WOR stage passes fractions instead: \(1 / w_k\) for every without-replacement stage and 0 for WR and PMR stages. ?as_svydesign gives both encodings.

The population the correction refers to. samplyr treats the frame as executed as the population, so \(N_h\) is counted from the frame the stage drew from. That is one convention among several. Where the sampling fraction is not negligible and the population itself changes between occasions, the finite population correction matters more and admits several competing definitions, particularly for measures of change across coordinated or overlapping samples (Tam 1984; Laniel 1988; Nordberg 2000; Berger 2004). samplyr does not choose among them for you: it records the frame size it saw, and a different convention has to be imposed downstream.

Independence across stages

samplyr assumes that the stage-\(k\) selection mechanism depends on stage \(k - 1\) only through the set of selected clusters, not through the specific randomization that produced it. Formally:

\[ \Pr(S^{(k)} \mid S^{(k-1)}) \text{ depends on } S^{(k-1)} \text{ as a set.} \]

This is the standard assumption in multi-stage sampling theory (Cochran 1977, ch. 11; Särndal, Swensson, and Wretman 1992, ch. 4.3).

Consequence for variance estimation. Under this assumption, the recursive multi-stage variance formula applies (Särndal, Swensson, and Wretman 1992, ch. 4.3). For methods with a supported linearization estimator, as_svydesign() exports every executed stage to survey::svydesign(): one ids term, one fpc term, and (when the stage is stratified) one strata term per stage. A first-stage census, for example, correctly attributes all variance to the later stages.

A stage that samples with replacement is handled differently: later stages nested in it contribute nothing beyond the between-draw variability, which the Hansen-Hurwitz estimator already captures.

Bounded cube, LPM2, and SCPS do not currently have supported linearization estimators. Their variance needs as_svrepdesign(type = "subbootstrap") or "mrbbootstrap", which ?variance_estimation describes.

Weights for a population other than the one sampled

Two transformations of an executed sample produce a weight for units that were never in the frame. Neither changes the draw.

share_weights() is the generalized weight share method (Lavallée 2007). A sample is selected from population \(U^A\), and estimates are wanted for a second population \(U^B\) linked to it: children through their parents, establishments through their enterprises, persons through their dwellings. Every target unit in a reached cluster \(i\) receives

\[ w_i = \sum_j \frac{I(j \in S^A)}{\pi_j} \frac{L_{ji}}{L_i}, \]

the source weights carried across the links and divided by the population number of links \(L_i\) to that cluster. \(\pi_j\) is needed only for selected source units, which .weight already holds. \(L_i\) sums over the whole of \(U^A\), so counting a supplied link table asserts that the table is a complete population register. That assertion has no default: either name a column holding the population multiplicity, or state it with complete_links(). ?share_weights gives the target-cluster choices and weighted links.

stack_frames() collects samples selected independently from two or more registers of the same population, each incomplete. The multiplicity estimator for overlapping frames is the weight share method with the standardized link matrix, since the number of links to a unit is the number of frames containing it (Lohr 2021, sec. 3.2; Mecatti 2007). A stack records which frames each sampled unit belongs to and leaves the compositing factor to the export, because the appropriate factor depends on the estimand. A stack does not assert that the frames together cover the target population.

The weight contract

A shared weight is an estimation weight and is not generally the reciprocal of a target unit’s inclusion probability. The design the object carries describes selection of the source population. The result records which kind of weight it holds, and every statistical consumer states what it does about that record rather than treating the sample as ordinary.

Consumer On a shared weight
design_effect(), effective_n() Supported. They read .weight and nothing else
as_svrepdesign() Supported. It replicates the source design and shares inside every replicate
The data-manipulation methods Preserve the record
as_svydesign() Supported. It exports one row per source-target contribution
joint_expectation(), varcomp() Refuse
A further execute(), and the panel verbs Refuse

Each refusal has its own condition class and all of them are also catchable as samplyr_error_weight_contract. Neither as_tbl_sample() nor an ordinary dplyr reconstruction can launder an estimation weight back into a design-weight sample.

A target cluster with no link to the source population can never be surveyed, and the estimator understates totals by exactly its share. share_weights() records that finding without warning, and the warning fires where the estimate is formed. For a stack_frames() collection it is evaluated over the union of the frames and names only the clusters no frame reaches.

What is lost at survey export

as_svydesign() translates a tbl_sample into a survey::svydesign() object. The translation is faithful for standard variance estimators, but some samplyr metadata is not representable in the survey framework.

1. Element sampling before later stages

An unclustered element-sampling stage followed by further stages is not nested cluster sampling: the later selections operate on the realized element sample, not within larger units, so there is no hierarchy for the multi-stage variance recursion (or for replicate-weight resampling) to use. This is phase sampling. execute() refuses this shape (samplyr_error_stage_parent_id), and the supported path is a two-phase sample: execute the element stage under its first-phase design, execute a new second-phase design with that sample as its frame, and export with as_svydesign(), which uses survey::twophase(). A final unclustered stage is fully supported: it is exported with a synthesized row-identity ids term and its own FPC.

2. Variance by selection method

How each selection method is linearized, what a joint matrix from joint_expectation() changes, how with-replacement and Chromy stages are represented, and how certainty units form a take-all stratum are in ?variance_estimation, with the measured error of the approximations that were measured.

3. Panel assignments

The .panel column is not passed to survey. Panels are randomized fixed-quota partitions for rotation or workload distribution. The full-sample weights in .weight are correct for the combined sample. Panel assignment is not an additional probability-sampling phase, so a single panel does not automatically have inclusion probability \(\pi_i / k\). Conditional on the frozen quotas, its probability is the block’s realized quota over the block size. Multiplying one panel’s weights by \(k\) is therefore not generally valid for population inference.

4. Design metadata

samplyr stores the full sampling_design object, stage labels, seed, and execution history as attributes of the tbl_sample. These are accessible via get_design() and get_stages_executed() but are not round-tripped through the survey export.

Panel partitioning semantics

There are two distinct operations here and they have different statistical status. Partitioning (panels = k with no schedule) splits one execution into \(k\) rotation groups by randomized fixed quota inside ordered blocks. It changes no inclusion probability and no .weight, so the combined sample is analyzed with the stored weights. Wave activation (a scheduled panels plus execute(wave = t)) selects a subset of those groups, and that is a second sampling phase. Its activation probability is the block’s frozen quota for the active panels over the block size, and the weight is multiplied by the inverse: the block size over that quota. For a block of eight units with four activated, the factor is 2. It is exact and generally not the nominal \(k / r\) for \(r\) active panels of \(k\), and it is why the block sizes and quotas are recorded with the sample.

Neither operation replenishes the sample. A program that draws refreshment cohorts against later frame vintages is a registry of separate executions, not a partition of one. See rotation_program(). ?panel_assignment gives the assignment rules, the choice of assignment stage and what it costs, certainty units, schedules and small pools.

PRN and sample coordination semantics

samplyr passes permanent random numbers through to sondage without transformation. PRN-compatible methods (bernoulli, pps_poisson, pps_sps, pps_pareto, scps) produce a deterministic selection given the PRN values, and for scps the visiting order. When PRN is supplied, the seed argument to execute() does not affect selection.

Positive and negative coordination across survey waves is a user-level workflow, not a package feature. samplyr passes PRNs to the selection method, the user manages their persistence and update. The negative-coordination update is \(u_{\text{new}} = (u - \pi) \bmod 1\) (Ohlsson 1995).

References

Berger, Yves G. 2004. “Variance Estimation for Measures of Change in Probability Sampling.” Canadian Journal of Statistics 32: 451–67.
Chauvet, Guillaume. 2009. “Stratified Balanced Sampling.” Survey Methodology 35 (1): 115–19.
Cochran, William G. 1977. Sampling Techniques. 3rd ed. Wiley.
Deville, Jean-Claude, and Yves Tillé. 2004. “Efficient Balanced Sampling: The Cube Method.” Biometrika 91 (4): 893–912.
Laniel, Normand. 1988. “Variances for a Rotating Sample from a Changing Population.” In Proceedings of the Business and Economic Statistics Section, 246–50. American Statistical Association.
Lavallée, Pierre. 2007. Indirect Sampling. Springer Series in Statistics. New York: Springer.
Lohr, Sharon L. 2021. “Multiple-Frame Surveys for a Multiple-Data-Source World.” Survey Methodology 47 (2): 229–63.
Mecatti, Fulvia. 2007. “A Single Frame Multiplicity Estimator for Multiple Frame Surveys.” Survey Methodology 33 (2): 151–57.
Nordberg, Lennart. 2000. “On Variance Estimation for Measures of Change When Samples Are Coordinated by the Use of Permanent Random Numbers.” Journal of Official Statistics 16: 363–78.
Ohlsson, Esbjörn. 1995. “Coordination of Samples Using Permanent Random Numbers.” In Business Survey Methods, edited by Brenda G. Cox, David A. Binder, B. Nanjamma Chinnappa, Anders Christianson, Michael J. Colledge, and Phillip S. Kott, 153–69. New York: John Wiley & Sons. https://doi.org/10.1002/9781118150504.ch9.
Rosén, Bengt. 1997. “Asymptotic Theory for Order Sampling.” Journal of Statistical Planning and Inference 62 (2): 135–58.
Särndal, Carl-Erik, Bengt Swensson, and Jan Wretman. 1992. Model Assisted Survey Sampling. Springer.
Tam, S. M. 1984. “On Covariances from Overlapping Samples.” The American Statistician 38: 288–89.
Tillé, Yves. 2020. Sampling and Estimation from Finite Populations. Wiley.