Skip to contents

The basic workflow

samplyr separates a sampling design from the frame on which it runs. You describe the design first, validate or inspect it, and then execute it against one or more frames.

The smallest complete example is a simple random sample without replacement.

library(samplyr)
library(dplyr)

data(bfa_eas)

design <- sampling_design(title = "Burkina Faso EA sample") |>
  draw(n = 100)

sample <- execute(design, bfa_eas, seed = 24)
sample |>
  as.data.frame() |>
  dplyr::select(ea_id, region, households, .weight) |>
  head(4)
#>   ea_id      region households .weight
#> 1  5866 Centre-Nord        146   445.7
#> 2 24905        Nord         32   445.7
#> 3  5145 Centre-Nord        156   445.7
#> 4 32861         Est         22   445.7

The frame has one row per enumeration area. The result keeps its frame columns and adds the design information needed for analysis.

.weight is the overall design weight. In a one-stage sample it equals .weight_1. A multistage sample also carries one weight column per stage and multiplies them into .weight.

The design grammar

Six functions cover the ordinary workflow.

Function Role
sampling_design() Start a reusable design
stratify_by() Divide a stage into sampling strata
cluster_by() Name the units selected as clusters
draw() Choose the size and selection method
add_stage() Start another sampling stage
execute() Run the design against its frame or frames

Column names are stored in the design and resolved when a frame is supplied. This lets the same design be validated and reused against compatible frame vintages.

stratified_design <- sampling_design(title = "Stratified EA sample") |>
  stratify_by(region, alloc = "proportional") |>
  draw(n = 260)

validate_frame(stratified_design, bfa_eas)

validate_frame() returns invisibly when the required columns and values are usable. It reports problems before selection begins.

Stratified sampling

Stratification samples independently within groups. Here n = 260 is a total allocated across regions in proportion to their frame sizes.

stratified_sample <- execute(stratified_design, bfa_eas, seed = 25)

stratified_sample |>
  count(region, name = "n_sampled")
#> # A tibble: 13 × 2
#>    region            n_sampled
#>    <fct>                 <int>
#>  1 Boucle du Mouhoun        29
#>  2 Cascades                 15
#>  3 Centre                   23
#>  4 Centre-Est               17
#>  5 Centre-Nord              20
#>  6 Centre-Ouest             22
#>  7 Centre-Sud                9
#>  8 Est                      32
#>  9 Hauts-Bassins            28
#> 10 Nord                     17
#> 11 Plateau-Central          10
#> 12 Sahel                    24
#> 13 Sud-Ouest                14

If stratify_by() has no allocation rule, a scalar n is interpreted per stratum. You can also pass a named vector or a svyplan::n_alloc() result to draw() when each stratum needs its own take.

Use equal allocation when comparable precision within each stratum is the goal. Use proportional allocation for a self-weighting element design when the other design features permit it. Neyman and cost-weighted allocations need prior variability or cost information and optimize the criterion supplied for that planning variable. They are not universal optima for every outcome and domain.

Sample-size bounds can protect small reporting domains.

bounded_design <- sampling_design() |>
  stratify_by(region, alloc = "proportional") |>
  draw(n = 260, min_n = 12, max_n = 30)

bounded_sample <- execute(bounded_design, bfa_eas, seed = 26)
bounded_sample |>
  count(region, name = "n_sampled")
#> # A tibble: 13 × 2
#>    region            n_sampled
#>    <fct>                 <int>
#>  1 Boucle du Mouhoun        29
#>  2 Cascades                 14
#>  3 Centre                   22
#>  4 Centre-Est               17
#>  5 Centre-Nord              20
#>  6 Centre-Ouest             21
#>  7 Centre-Sud               12
#>  8 Est                      30
#>  9 Hauts-Bassins            28
#> 10 Nord                     17
#> 11 Plateau-Central          12
#> 12 Sahel                    24
#> 13 Sud-Ouest                14

For without-replacement sampling, the population of a stratum remains a hard upper bound. If a requested allocation cannot be reached, execute() reports the affected pools and uses the attainable take.

Cluster and PPS sampling

cluster_by() changes the sampling unit. The next example selects enumeration areas with probability related to their household counts.

pps_design <- sampling_design(title = "PPS EA sample") |>
  cluster_by(ea_id) |>
  draw(n = 80, method = "pps_brewer", mos = households)

pps_sample <- execute(pps_design, bfa_eas, seed = 27)
nrow(pps_sample)
#> [1] 80

mos is the measure of size. A large measure gives a unit a larger selection probability. If a resolved probability reaches one, that unit is selected with certainty and contributes no variance at that stage. Certainty at one stage does not make the final multistage weight equal to one.

pps_brewer is one fixed-size PPS method. Method choice also depends on the second-order information required for variance estimation, whether a random sample size is acceptable, and whether permanent random numbers are needed. The comparison is in ?selection-methods.

Continue inside selected units

add_stage() starts another selection within each selected parent. For example, adding add_stage() |> draw(n = 12) after the EA selection declares a household draw within each selected EA. Execution needs household rows, with EA identifiers linking them to their parents.

The next tutorial, vignette("three-stage-sampling"), works through this process in full: commune and EA registers, household listing, continuation, and survey export. It also explains the scope of n at every stage.

Fractions, replacement, and ordering

draw() also accepts frac. Fixed-size methods convert the fraction to an integer take, while Bernoulli sampling keeps a random realized size.

fraction_sample <- sampling_design() |>
  draw(frac = 0.01) |>
  execute(bfa_eas, seed = 29)

nrow(fraction_sample)
#> [1] 446

With-replacement methods may select the same population unit several times. Their samples carry .draw_1, .draw_2, and later-stage equivalents so every draw occurrence remains distinct.

Systematic and sequential methods depend on frame order. Use control when an explicit ordering is part of the design. Their variance properties depend on that ordering, so an SRS variance approximation for systematic selection need not be conservative.

Inspecting the result

A tbl_sample is a tibble with a design and an execution receipt attached. Ordinary one-to-one joins and new analysis columns preserve it. Removing or duplicating rows changes the realized sample and prevents survey export.

summary(stratified_sample)
#> ── Sample Summary: Stratified EA sample ────────────────────────────────────────
#> 
#> ℹ n = 260 of 44,570 | stages = 1/1 | seed = 25
#> 
#> ── Stage 1 ─────────────────────────────────────────────────────────────────────
#> • srswor, by region (proportional)
#> • 13 strata: N_h 1,612-5,505, n_h 9-32, f_h 0.0056-0.0060
#> 
#> ── Weights ─────────────────────────────────────────────────────────────────────
#> • Mean 171.42 [166.2, 179.11] | CV 0.01 | Kish DEFF 1 | n_eff 260

frame_summary() reports what the execution resolved in each selection pool.

frame_summary(stratified_sample, detail = "pool") |>
  select(stage, region, N, n_target, n_realized) |>
  slice_head(n = 6)
#> # A tibble: 6 × 5
#>   stage region                N n_target n_realized
#>   <int> <fct>             <dbl>    <dbl>      <dbl>
#> 1     1 Boucle du Mouhoun  5009       29         29
#> 2     1 Cascades           2508       15         15
#> 3     1 Centre             3888       23         23
#> 4     1 Centre-Est         2941       17         17
#> 5     1 Centre-Nord        3402       20         20
#> 6     1 Centre-Ouest       3723       22         22

For domain estimates, convert the complete sample first and subset the survey design afterwards. See vignette("survey-analysis") for that workflow.

Choose your next step

The main learning path is:

  1. Get started: the design grammar and a first sample, covered here.
  2. Select a three-stage sample: vignette("three-stage-sampling") follows fieldwork from area registers to households and observations.
  3. Analyze the sample: vignette("survey-analysis") covers outcome joins, domain estimates and variance choices, with optional two-phase and overlapping-frame examples.

Use the other articles when their task arises:

Task Article
Choose sizes and allocations before selection vignette("survey-planning")
Coordinate new draws with permanent random numbers vignette("sampling-coordination")
Schedule panels and refreshment cohorts vignette("rotating-panels")
Save or replay a design vignette("serialization")

For a method or inference lookup, use ?selection-methods. Consult vignette("design-semantics") for the underlying contracts and vignette("validation") for simulation diagnostics.

A compact checklist

  1. Make sure one row represents the unit selected at the current stage.
  2. Declare strata, clusters, and selection methods in their sampling order.
  3. Validate the design against the intended frame vintage.
  4. Use a recorded seed when the selection must be reproducible.
  5. Keep the returned design columns unchanged.
  6. Archive the executed sample and its frame provenance.

Continue with vignette("three-stage-sampling"), or go directly to vignette("survey-analysis") if you already have a selected sample.

References