Skip to contents

execute() runs a sampling design against one or more data frames, producing a sampled dataset with appropriate weights and metadata.

Usage

execute(
  .data,
  ...,
  stages = NULL,
  seed = NULL,
  panels = NULL,
  panel_stage = NULL,
  small_pool = NULL,
  reps = NULL,
  wave = NULL,
  response_status = NULL,
  frame_digest = c("summary", "full", "none")
)

Arguments

.data

A sampling_design object to start a new execution, or a partially executed tbl_sample to continue the remaining stages of its stored design.

...

Data frame(s) to sample from: one frame for a single-stage design or a shared hierarchy, or one frame per stage in stage order. A tbl_sample passed here while .data is a new sampling_design starts a new sampling phase rather than continuing that sample's stages. Ordinary input frames must have unique column names and must not use columns reserved for execution output, such as .weight, .sample_id, .stage, .weight_k, or .fpc_k for stage k. Frames are matched by position, so a name given here is a label. A label resembling an argument below (seedd, or the singular stage, rep, panel) is refused, because those arguments follow ... and are matched exactly.

A single unnamed list of data frames is the same call, for code that already holds the frames, such as a loop over registers or replay_design(). Mixing it with separate frames is refused. Names inside the list are frame labels, so a misspelled argument must be written outside it to be reported as one. See frame_input_grammar.

stages

Integer vector of the stages to execute, or NULL (default) for all remaining stages. From a sampling_design it must start at stage 1, so an operational workflow can stop after its first contiguous batch of stages. From a partial tbl_sample it must start at the next unexecuted stage.

seed

Integer random seed, between -.Machine$integer.max and .Machine$integer.max. A given seed leaves the session's random number stream untouched. With seed = NULL, selection draws from the session stream and advances it, and replay_design() refuses the resulting sample because its receipt cannot reproduce it.

panels

Rotation groups (panels) to partition the sample into, as an integer count, a rotation schedule (a data frame with integer panel and wave columns, an optional logical active column, an optional integer visit column that opens tenures and an optional integer period column that names each wave's reporting period), or an svyplan_schedule from svyplan::design_schedule(). periods() partitions it into fieldwork periods instead, each unit visited once. The output gains a .panel column, and only a sample drawn with a schedule or periods can be materialized by wave. Default NULL means no panels. Cannot be combined with reps, or redeclared on a sample that already carries an assignment. panel_assignment describes the assignment and the schedules.

panel_stage

Stage whose selected units are assigned to panels, as a single stage number, or NULL (the default) for the first executed stage. Accepted only alongside panels. The stage must be one the execution completes, and not the stage that sets reserve in draw() (samplyr_error_panel_stage_reserve). Later stages inherit their ancestor's panel, so assigning below stage 1 rotates units inside parents that stay in the survey. panel_assignment explains what that changes, including how it can bias estimates over time.

small_pool

What to do when a rotation schedule would leave a pool with no unit to activate at some wave. "error" (the default, reached by NULL) refuses the assignment, and "permanent" activates those units at every wave and warns with samplyr_warning_panel_small_pool. Meaningful only with a schedule. See panel_assignment.

reps

Integer number of independent replicate samples to draw (>= 2), or NULL (default) for a single sample. The replicates are stacked in one tbl_sample with a .replicate column (integer 1 through reps). Their seeds are drawn from seed, since consecutive seeds do not give independent draws in R, and recorded in the replicate_seeds element of the sample's metadata, so replicate r alone is execute(design, frame, seed = replicate_seeds[r]). Cannot be combined with panels or with stages that use permanent random numbers. A replicated sample continued, or used as a phase-2 frame, carries its replicates into the stages run now, and these cannot use permanent random numbers either. as_svrepdesign(type = "random_groups") estimates variance from the spread of the replicates, for any selection method.

wave

Integer wave of a master that carries a panel assignment, or NULL (default). It has two uses, told apart by whether a frame is given. Without a frame, execute(master, wave = t) materializes wave t. It activates the panels the stored schedule declares active at t and compounds the exact activation factor into .weight. A wave selects units the master already assigned, under the policy the master froze, so it takes none of seed, stages, panels, panel_stage, small_pool, reps or frame_digest. Each is refused by name rather than ignored. Several distinct waves, as wave = 1:3, pool those waves into one sample with a .wave column (panel_assignment gives the weights). With a frame, execute(master, listing, wave = t) draws the master's remaining stages under the units whose tenure opens at t, the units of period t for periods(), and returns the master. It accepts seed and stages, refuses the other arguments, and takes a master with a rotation schedule or periods. See "Drawing the later stages wave by wave" below.

response_status

A column of the sample that holds the final disposition of each listed unit, in the terms of the AAPOR Standard Definitions: whether the unit was eligible and whether it responded. It settles the stage that sets reserve in draw(). execute(sample, response_status = col) takes no other argument. The column holds AAPOR final disposition codes, read by their leading digit (1 or 1.1 an interview, 2 or 2.24 an eligible non-interview, 3 an unknown eligibility, 4 or "4.60" not eligible), or the words "interview", "noninterview", "unknown" and "ineligible". NA marks a reserve unit the field did not release. Every unit of the take needs a code. See "Settling a reserve" below, and reserve_lists for the protocol and the codes.

frame_digest

Controls the frame digest, a compact execution manifest recorded with the sample and read by frame_summary(). The digest never affects selection, weights, or estimation. "summary" (default) records selection pools, resolved chances (exact for cluster stages, constant or quantile-compressed for element stages), and the selected-unit trace. "full" keeps exact per-unit chances for element stages too. "none" records no digest and skips trace construction for minimum execution overhead. "summary" saves storage, not time. Compressing the chances can take longer than keeping them all, so "none" is the mode for repeated executions such as a simulation loop.

When one universe frame feeds every stage, later stages also record the pools their realization never reached, with chances resolved from the design (chance_status = "design_resolved"), which gives complete universe denominators without the frame. A stage continuation extends the digest of its input sample, and an input without a valid digest yields none. Replicates share one population structure with their own traces. A replicated multi-stage execution keeps only the stage prefix shared by all replicates, with status "partial", as frame_summary() describes. Replicated multi-phase and replicated-continuation executions do not record a digest yet.

A digest is not anonymized. Its selection trace keeps the identifiers of realized units (frame-derived cluster keys or row indices), even under "summary", so treat the sample and any serialized execution receipt as potentially confidential.

Value

A tbl_sample: a data frame subclass carrying the selected rows, the design that produced them, and generated columns recording the selection. Those are .sample_id, .weight, the per-stage .weight_k, .fpc_k, .draw_k and .certainty_k, .reserve_k at a stage that sets reserve in draw(), .zone_1 and .pair_1 for a zoned certainty plan, .replicate or .panel when reps or panels is used, and .wave for a window of several waves. sample_columns documents what each one holds.

Details

Multi-stage with a single frame

For hierarchical data where all stages are in one frame, pass that one frame. It must contain all clustering variables and represent the stage hierarchy correctly. Lower-stage IDs may repeat across different parents, because samplyr resolves them using the full ancestry from earlier stages.

Multi-stage with one frame per stage

When each stage has its own register, pass one frame per stage. The number of frames schedules the stages: one frame is a shared hierarchy covering all of them, one frame per stage gives each its own, and any other count is samplyr_error_frame_count.

Registers are supplied whole. Each is restricted to the units its parent stage selected, and the variables earlier stages introduced are carried onto it, so a lower register need not be pre-filtered or duplicate the upper stages' stratification columns. A register may legitimately omit a carried stratum, but one whose own copy of it disagrees is an error. Each earlier stage's measure of size (mos) is carried too, so the sample keeps what every stage's inclusion probabilities were computed from. A register holding a column of the same name keeps its own, and a stage that selects on that name must find it in its own register.

Before any stage draws, every later stage is checked on all the units it could reach, as validate_frame() checks them, so a defect in a unit the seed happens to skip is still refused.

This form, the single-hierarchy form, and the stage continuation below run the same stage transition. Under one shared RNG stream, and with stages given on every intermediate call, all three draw the same sample.

Partial execution (operational sampling)

stages executes only the stages named, returning a partial tbl_sample. Fieldwork then produces the next stage's frame, and passing the partial sample back as .data continues the same design. frame_input_grammar states how a continuation reads its frame, when it asks for stages, and how it differs from starting a new phase on a sample.

Settling a reserve

A stage that sets reserve in draw() lists its take and its reserve, with .reserve_k giving the release order. The field works the take, the office releases one reserve unit, in .reserve_k order, for each unit that fails, and work in a pool stops at its target number of respondents or when the list runs out. An earlier stop must not depend on how the pool was going, such as giving up after a run of refusals.

execute(sample, response_status = col) reads the codes, walks each pool in release order and returns the respondents. Their weight at the stage estimates the pool's eligible units from the units worked, leaving out the unit that stopped the list, and divides it among the respondents. Units released after the stop are dropped, so a pool worked past its target keeps the weight of the stop rule. A pool stopped early is settled with the units it worked. How unknowns are coded is the choice of their eligibility: 3 estimates it from the resolved units of the pool, 2 counts them as eligible and 4 as ineligible.

A pool whose eligible units include no respondent returns no rows, but its eligible units are still estimated from the units worked. When every unit worked is of unknown eligibility, the estimate takes the resolved rate of the pools that share its strata, or counts every unit as eligible when none of them resolved one. Both are assumptions about the pool, and summary() counts the pools that rest on them. The export keeps that weight in a placeholder row for a nonresponse adjustment (see the "A settled reserve" section of as_svydesign()).

A sample is settled once. A stage below the reserve is drawn from the settled sample, execute(settled, listing), and a call that would draw it from the list is refused (samplyr_error_reserve_unsettled). So is a new phase drawn from the list. summary() shows the settlement per pool, and write_design() records it for replay_design().

Drawing the later stages wave by wave

A master split into periods() at its first stage can have its later stages drawn period by period, as listing proceeds in the field. execute(master, listing, wave = t) draws every remaining stage under the units of period t and returns the master, so the calls are chained, as in master <- execute(master, listing_3, wave = 3, seed = 103).

  • The listing needs rows for every unit of period t. Rows under units of other periods are ignored, so one growing listing file can be passed every time. A unit of period t with no row is refused (samplyr_error_frame_missing_parent) unless its stage sets on_empty.

  • The listing keys its rows to the master's units by their cluster ids, such as ea_id, and carries what the later stages select on. Strata and weights come from the master.

  • Each period is drawn once (samplyr_error_wave_already_drawn), in any order, with its own seed. stages may be given, and must name every remaining stage.

  • Until every period is drawn, the master keeps its first-stage rows and every other verb reads it as the first-stage sample. print() and summary() say which periods are drawn, and an ordinary continuation is refused (samplyr_error_wave_draws_in_progress). execute(master, wave = t) gives a drawn period's sample with exact weights. That sample is provisional. as_svydesign() and as_svrepdesign() export it by ultimate clusters from its own rows, and the two-phase export, which reads the whole master, stack_waves() and write_design() refuse it (samplyr_error_wave_provisional, samplyr_error_serialize_wave_provisional).

  • The call that draws the last period returns the completed sample, as one continuation would, with every stage executed, rows in the master's order of their units and .sample_id numbered once. It is the design of execute(master, listing), drawn with other random numbers.

  • The completed sample's frame digest holds every period's pools and selections, so frame_summary() reports each stage as for one call. A listing passed for every period keeps its frame record. Separate listings share one record of the rows the periods drew from, with no fingerprint, since no single frame holds them.

  • write_design() records the master's call and each period's call in the order they were made, and replay_design() replays them with one frame per call, the master's frame and then each period's listing. A master with some periods drawn is written and replayed the same way.

  • A master with a rotation schedule is drawn the same way, tenure by tenure. The call at wave t draws the units of the panels whose tenure opens there, where the schedule's visit is 1, and the first call also draws the permanent units, which keep one tenure throughout. A wave that opens no tenure is refused (samplyr_error_wave_already_drawn) with the waves that drew its units. When a panel has several tenures, the master never closes: its draws accumulate, and execute(master, wave = t) reads each panel's current tenure. A new tenure takes the full listing: samplyr leaves out the units earlier tenures drew and weights the rest to the whole listing. Such a master takes no reserve at any stage (samplyr_error_tenure_stages), since it is never complete and a wave exports no reserve. panel_assignment describes tenures.

Multi-phase sampling

execute(new_design, previous_sample) starts a new phase, with the earlier sample as the frame of a new design rather than .data carrying unexecuted stages. Weights compound across phases, and as_svydesign() exports this path through survey::twophase().

Weights

.weight is the product of the per-stage weights and of the phase weights across phases. sample_columns gives the formulas.

Large frames

A partial execution on one frame keeps every row under the units it selected, because the continuation draws the next stage from those rows. On a frame of persons, the stage-1 sample of a two-stage design holds every person of the selected EAs. Drawing stage 1 from a register of the EAs, one row per EA, keeps the partial sample to one row per selected EA, and the person frame is passed when the design continues, as in execute(partial, persons). It is the same design. frame_input_grammar states how a continuation reads its frame.

A factor column keeps all of its levels in the sample, as R keeps them in any subset of a factor. A household id stored as a factor with a million levels therefore weighs about as much in a sample of a few thousand rows as in the frame. samplyr leaves the levels as they are, since they are part of the data. droplevels() on the sample removes them, and ids stored as character in the frame avoid them.

A materialized wave keeps its master as its first phase, which its survey export reads. saveRDS() of a wave therefore writes the master too, again in every file and for every wave saved. Saving the master alone is enough, since execute(master, wave = t) gives back the same wave and draws no random numbers.

When a design cannot be realized as written

A pool holding fewer units than the stage asks for is selected whole, which makes the design non-self-weighting. execute() reports this and four related outcomes as classed conditions carrying a payload. With the default digest, the capped column of frame_summary(sample, detail = "pool") shows capping after the fact. execution_conditions gives the five classes, their payload fields, and how to capture one when no digest is kept.

See also

sampling_design() for creating designs, frame_input_grammar for the frame forms every verb accepts, sample_columns for what the generated columns hold, execution_conditions for what execute() signals when a design cannot be realized as written, get_design() for extracting metadata

Other execution: execution_conditions, rotation_program(), validate_frame()

Examples

# Basic SRS execution
sample <- sampling_design() |>
  draw(n = 100) |>
  execute(bfa_eas, seed = 1234)
sample
#> # A tbl_sample: 100 × 18
#> # Sampling:     1 stage | 100/44,570 units
#> # Weights:      445.7 [445.7, 445.7]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1  6473 Sahel       Soum     Koutou… Rural              133         23    19.4 
#>  2  6543 Sahel       Soum     Koutou… Rural              183         32     6.15
#>  3 10735 Sahel       Yagha    Tankou… Rural               36          5    16.7 
#>  4 36151 Centre-Nord Namente… Boulsa  Rural              526         75     3.75
#>  5 10510 Nord        Loroum   Sollé   Rural              239         28     5.45
#>  6 23624 Nord        Yatenga  Senguè… Rural              555         59     8.84
#>  7 33774 Centre-Nord Sanmate… Pissila Rural              857        110     4.94
#>  8  1118 Centre-Nord Namente… Boala   Rural              830        137    10.2 
#>  9 29691 Centre-Oue… Sissili  Bieha   Rural              190         28     8.83
#> 10 10559 Boucle du … Kossi    Sônô    Rural               69         10     8.89
#> # ℹ 90 more rows
#> # ℹ 10 more variables: pop_density <dbl>, longitude <dbl>, latitude <dbl>,
#> #   remoteness <fct>, fieldwork_cost <int>, .weight <dbl>, .sample_id <int>,
#> #   .stage <int>, .weight_1 <dbl>, .fpc_1 <int>

# Stratified execution with proportional allocation
sample <- sampling_design() |>
  stratify_by(region, alloc = "proportional") |>
  draw(n = 300) |>
  execute(bfa_eas, seed = 5789)
table(sample$region)
#> 
#> Boucle du Mouhoun          Cascades            Centre        Centre-Est 
#>                34                17                26                20 
#>       Centre-Nord      Centre-Ouest        Centre-Sud               Est 
#>                23                25                11                37 
#>     Hauts-Bassins              Nord   Plateau-Central             Sahel 
#>                32                20                11                28 
#>         Sud-Ouest 
#>                16 

# Two-stage cluster sample execution
zwe_frame <- zwe_eas |>
  dplyr::mutate(district_hh = sum(households), .by = district)

sample <- sampling_design() |>
  add_stage(label = "Districts") |>
    cluster_by(district) |>
    draw(n = 20, method = "pps_brewer", mos = district_hh) |>
  add_stage(label = "EAs") |>
    draw(n = 10) |>
  execute(zwe_frame, seed = 3)
#> Stage 1: 1 unit is selected with certainty.
#> ℹ A unit whose share of its pool's size is above 1 / n has chance 1, and the
#>   rest of the take is spread over the other units.
#> ℹ Set `certainty_size` or `certainty_prop` in `draw()` to state the rule in the
#>   design, or check before drawing with `frame_summary()`.
length(unique(sample$district))  # 20 districts selected
#> [1] 20

# Partial execution: stage 1 only
design <- sampling_design() |>
  add_stage(label = "EAs") |>
    stratify_by(region) |>
    cluster_by(ea_id) |>
    draw(n = 5, method = "pps_brewer", mos = households) |>
  add_stage(label = "Households") |>
    draw(n = 12)

# Execute only stage 1 to get selected EAs
selected_eas <- execute(design, bfa_eas, stages = 1, seed = 2)
nrow(selected_eas)  # Number of selected EAs
#> [1] 65

# Continuation: the listing produced by fieldwork becomes the next frame,
# and the partial sample is passed back as `.data`
listing <- selected_eas |>
  as.data.frame() |>
  dplyr::reframe(hh_id = seq_len(20), .by = ea_id)
sample <- selected_eas |> execute(listing, seed = 43)
nrow(sample)  # 12 households in each selected EA
#> [1] 780

## Later stages drawn one period at a time
monthly <- sampling_design() |>
  add_stage() |> stratify_by(region) |> cluster_by(ea_id) |>
    draw(n = 12, method = "pps_systematic", mos = households) |>
  add_stage() |> draw(n = 5) |>
  execute(bfa_eas, stages = 1, seed = 1, panels = periods(12))
# The EAs of month 3 go to the listing team
month_3 <- dplyr::filter(as.data.frame(monthly), .panel == 3)
listing_3 <- dplyr::reframe(month_3, hh_id = seq_len(20), .by = ea_id)
monthly <- execute(monthly, listing_3, wave = 3, seed = 103)
monthly  # the master, with month 3's households drawn
#> # A tbl_sample: 156 × 20
#> # Stages:       1/2
#> # Sampling:     1 stage | 156 units
#> # Periods:      12 (in .panel)
#> # Drawn:        stage 2 in 1 of 12 periods (3)
#> # Weights:      296 [27.15, 2530.67]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 29516 Boucle du … Bale     Bana    Rural              585         76     1.17
#>  2  8572 Boucle du … Bale     Pâ      Rural             1740        201     1.78
#>  3  6314 Boucle du … Banwa    Kouka   Rural             3778        435     3.12
#>  4 23910 Boucle du … Banwa    Solenzo Rural              346         46     5.91
#>  5   657 Boucle du … Kossi    Barani  Rural               80         10     6.22
#>  6 21509 Boucle du … Kossi    Doumba… Rural              974        167     1.25
#>  7 20925 Boucle du … Mouhoun  Bondok… Rural              723         95     7.13
#>  8 26134 Boucle du … Mouhoun  Dédoug… Rural              210         30     0.13
#>  9 43791 Boucle du … Mouhoun  Safané  Rural               60          9     8.92
#> 10 44729 Boucle du … Nayala   Toma    Rural              204         33     8.85
#> # ℹ 146 more rows
#> # ℹ 12 more variables: pop_density <dbl>, longitude <dbl>, latitude <dbl>,
#> #   remoteness <fct>, fieldwork_cost <int>, .weight <dbl>, .sample_id <int>,
#> #   .stage <int>, .weight_1 <dbl>, .fpc_1 <int>, .certainty_1 <lgl>,
#> #   .panel <int>
nrow(execute(monthly, wave = 3))  # month 3's sample
#> [1] 65

## One frame per stage
# Frames map to stages by position: districts first, then EAs
districts <- dplyr::distinct(zwe_eas, province, district)
two_stage <- sampling_design() |>
  add_stage(label = "Districts") |>
    cluster_by(district) |>
    draw(n = 8) |>
  add_stage(label = "EAs") |>
    draw(n = 3)
sample <- two_stage |> execute(districts, zwe_eas, seed = 424)
length(unique(sample$district))  # 8 districts, 3 EAs each
#> [1] 8

# The same call with the frames held as a list
registers <- list(districts, zwe_eas)
same <- two_stage |> execute(registers, seed = 424)
identical(sample$.sample_id, same$.sample_id)
#> [1] TRUE

## Multi-phase
# The previous phase's sample is the new phase's frame
phase1 <- sampling_design() |>
  draw(n = 200) |>
  execute(bfa_eas, seed = 42)
phase2 <- sampling_design() |>
  draw(n = 50) |>
  execute(phase1, seed = 123)
nrow(phase2)  # weights compound across both phases
#> [1] 50

# Replicated sampling: 5 independent draws
sample <- sampling_design() |>
  draw(n = 100) |>
  execute(bfa_eas, seed = 42, reps = 5)
table(sample$.replicate)  # 100 per replicate
#> 
#>   1   2   3   4   5 
#> 100 100 100 100 100 

# Rotating panel: 4 rotation groups
sample <- sampling_design() |>
  draw(n = 200) |>
  execute(bfa_eas, seed = 1, panels = 4)
table(sample$.panel)  # 50 per panel
#> 
#>  1  2  3  4 
#> 50 50 50 50