Skip to contents

draw() sets the sample size or sampling fraction, the selection method, and whatever that method needs: a measure of size for PPS, auxiliary variables for balanced sampling, coordinates for spatial spread. Every stage in a sampling design ends with draw(), which closes it. A second draw(), or a stratify_by() or cluster_by() after it, is refused, and sampling_design() describes the stage grammar.

Usage

draw(
  .data,
  n = NULL,
  frac = NULL,
  ...,
  min_n = NULL,
  max_n = NULL,
  method = "srswor",
  mos = NULL,
  prn = NULL,
  aux = NULL,
  spread = NULL,
  round = "up",
  control = NULL,
  certainty_size = NULL,
  certainty_prop = NULL,
  certainty_overflow = "error",
  on_empty = "error",
  on_capped = "warn"
)

Arguments

.data

A sampling_design object (piped from sampling_design(), add_stage(), stratify_by(), or cluster_by()).

n

Sample size. For random-size methods (bernoulli, pps_poisson, or a custom method registered with fixed_size = FALSE), n is the expected sample size, converted to frac = n / N, and may be fractional (n = 2.6) when no allocation method is given. At a later stage it is the expected take in each parent unit, whatever the parent's size. Fixed-size methods and allocation methods need whole units. selection_methods explains the fixed versus random distinction and the warning raised when pps_poisson falls short of n. Can be:

  • A scalar: the size taken in each stratum when stratify_by() has no alloc, or the total shared across the strata when it has one. At a later stage either reading applies within each selected parent, so a total is a total per parent, not one for the whole survey.

  • A named vector: stratum-specific sizes (for single stratification variable). A name, or a data frame row, for a stratum the stage cannot reach is refused at execution (samplyr_error_alloc_unknown_strata). A named vector or a data frame gives the sizes itself, so it is refused alongside alloc (samplyr_error_alloc_named_n_with_alloc).

  • A data frame: stratum-specific sizes, with every stratification column of stratify_by() and an n column. For a take per parent unit, stratify the stage by the parent's cluster id: add_stage() |> stratify_by(ea_id) |> draw(n = take). The table may cover every parent of the design, and a continuation on a listing of the selected parents uses only their rows. Entries for parents the sample did not select are not checked, so a mistyped identifier that matches no selected parent passes unnoticed.

  • A svyplan size or plan object, read through svyplan's documented coercions and stage-aware for cluster and stratified two-stage plans. A plan giving one size for a stage stratified without alloc is refused (samplyr_error_svyplan_total_per_stratum) rather than taken in every stratum.

  • A certainty-aware svyplan::n_alloc() plan (solved with a psu register carrying psu_id), at a clustered, stratified stage 1 and again at stage 2 for the per-PSU takes. Stage 1 takes every certainty PSU and draws exactly the plan's remainder per stratum. A plan solved with n_psu_per_zone = 2 draws two PSUs from each of its zones instead, records the zone in .zone_1, and exports one variance stratum per zone. With n_psu_per_zone = 1 it draws one PSU per zone, records the plan's variance group in .pair_1, and exports the groups as variance strata (variance_estimation). Its method must be pps_systematic, pps_brewer, pps_cps, or pps_sampford, with mos equal to the register's N. Stage 2 selects by srswor or systematic, and stratifying there requires an alloc method. Arguments the plan owns (frac, certainty_size, certainty_prop, min_n, max_n, and at stage 1 alloc) are refused alongside it. svyplan::merge_psus() merges PSUs smaller than the take.

frac

Sampling fraction: a scalar for all strata, a named vector of stratum-specific fractions, or a data frame with every stratification column and a frac column. Give n or frac, not both.

...

These dots are for future extensions and must be empty. Arguments after ... must be named in full, so a positional fourth argument is refused rather than taken for min_n.

min_n

Minimum sample size per stratum, NULL (no minimum) by default. With frac, a stratum whose rounded N_h * frac falls short is raised to min_n. With an allocation method, a stratum assigned fewer units is raised to min_n and the other strata give up the difference. For without-replacement designs a min_n above a stratum's population makes that stratum a census. min_n counts the stratum's whole take, certainty units included, so a stratum can hold fewer than min_n units drawn outside certainty, and execute() names one left with a single such unit (samplyr_message_singleton_pool). Allocations that give a nonempty stratum zero units are refused. min_n = 1 requests positive allocations explicitly. For minimums that differ by stratum, compute the sizes and pass them as a named n or a data frame.

max_n

Maximum sample size per stratum, NULL (no maximum) by default. With frac, a stratum's rounded take is capped at max_n. With an allocation method, a stratum assigned more is capped and the surplus goes to the other strata.

With frac, the bounds follow SPSS CSPLAN (RATE with MINSIZE and MAXSIZE) and SAS SURVEYSELECT (SAMPRATE= with NMIN= and NMAX=). Each stratum, or each parent unit at a later stage, is bounded on its own after round, with no fixed total. A random-size method bounds its expected size. With n and no allocation method, the bounds are ignored with a warning.

Under an allocation method the bounds narrow a range the stratum population already caps. The "Population bounds and redistribution" section of stratify_by() gives how the surplus is shared and why with-replacement methods differ. frame_summary(design, frame, detail = "pool") reports the per-stratum target each bound produces, without selecting or consuming random numbers.

method

Selection method, "srswor" by default. selection_methods lists the sixteen built-ins with what each one requires and the paper behind each.

"cube" balances the sample on aux so that Horvitz-Thompson estimates of auxiliary totals match population totals, with equal or unequal (mos) inclusion probabilities, and uses the stratified cube algorithm (Chauvet 2009) when stratified. At most two stages may use a balanced-family method. "balanced" is a compatibility alias for "cube".

A method registered with sondage::register_method() is named "pps_<name>" for type = "wor" or type = "wr", and "balanced_<name>" for type = "balanced", where mos is optional. supports_aux = TRUE permits ordinary balancing variables in aux, and supports_spread = TRUE requires coordinates in spread.

Its weights are 1 / pik, the inclusion expectation (target inclusion probabilities, or expected hits for type = "wr") samplyr hands to the method. The probabilities tier declared at registration is "exact" (the design's first-order inclusion probabilities, or expected hits, equal pik), "approximate" (to a documented approximation, as in Pareto sampling), or "unknown", the default. draw() refuses "unknown", because pik is then a selection weight only and the weights would be systematically biased. The classic trap is sample(prob = pik). With replace = TRUE its expected hits equal pik, a valid type = "wr" method, but its inclusion probabilities without replacement do not, so a type = "wor" wrapper's tier is unknown.

mos

Measure of size, a bare column name. Required for built-in PPS methods and registered pps_ methods. Optional for cube, lpm2, scps, and registered balanced_ methods, which use equal inclusion probabilities without it. A column name held in a variable is written .data[[v]], here and in prn, aux and spread. A quoted string is refused with class samplyr_error_draw_string_column.

prn

Permanent random numbers for sample coordination, a bare numeric column with values in the open interval (0, 1) and no missing values. Supported by "bernoulli", "pps_poisson", "pps_sps", "pps_pareto", and "scps". The sample is then deterministic for a given set of PRN values, which coordinates samples across survey waves. With "scps" it is also fixed by the order the pool's units are visited in, the frame's row order or the control order, so coordinated draws need the same order.

aux

Cube balancing declarations for method = "cube", or ordinary balancing variables for a registered balanced method that declares supports_aux = TRUE. Bare numeric columns, such as aux = c(income, pop_density), request approximate Horvitz-Thompson total balance. A bound() marker requests adjacent-integer count bounds for every observed category, as in aux = c(income, bound(region), bound(urban_rural)), with a separate bound() for each marginal constraint. With cluster_by(), ordinary auxiliary values are summed to cluster level, while bound variables must be constant within each cluster.

spread

Spatial coordinates for method = "lpm2" or "scps", or for a registered balanced method declaring supports_spread = TRUE, which requires them. Bare numeric columns such as spread = c(longitude, latitude), finite, with no missing values, and placed on comparable scales. With cluster_by(), coordinates must be constant within each cluster.

round

Rounding when frac is converted to the sample size of a fixed-size method. One of:

  • "up" (default): ceiling, the SAS SURVEYSELECT default.

  • "down": floor.

  • "nearest": nearest integer, halves up, as SPSS rounds a rate.

An n given directly is not rounded. A product within floating-point error of an integer counts as that integer, so frac = 0.07 of 100 units draws 7 under every rule. After rounding, every stratum or group receives at least 1 unit. A random-size method, built in or registered with fixed_size = FALSE, takes the expected size N * frac unrounded, which may be fractional or below 1.

control

<data-masking> Variables for sorting the frame before selection. Can be:

  • A single variable: control = region

  • Multiple variables: control = c(region, district)

  • With serp() for serpentine sorting: control = serp(region, district)

  • With dplyr::desc() for descending: control = c(region, desc(population))

  • Mixed: control = c(region, serp(district, commune), desc(size))

Character values sort by their bytes in UTF-8, as in the C locale, so the order and the sample do not depend on the session locale. Missing values sort last, also under desc(). Ties keep the frame's order. See the "Control sorting" section.

certainty_size

For PPS without-replacement methods, units with MOS >= this value are selected with certainty (probability 1). A scalar for all strata, or a data frame with the stratification columns and a certainty_size column. Certainty units are removed from the frame before probability sampling, which draws the remaining sample size. Mutually exclusive with certainty_prop. Equivalent to SAS SURVEYSELECT CERTSIZE=.

certainty_prop

For PPS without-replacement methods, units whose MOS share (MOS_i / sum(MOS)) >= this value are selected with certainty. A scalar strictly between 0 and 1, or a data frame with the stratification columns and a certainty_prop column. Shares are recomputed after removing certainty units, until no new unit qualifies. Mutually exclusive with certainty_size.

certainty_overflow

What to do when certainty units exceed the target sample size of a sampling pool (a stratum within its parent, when applicable). One of:

  • "error" (default): stop with an error.

  • "allow": permit a census above the target only when every unit in that pool is certain. All units in the pool get stage weight 1.

Under either setting, certainty units that exactly exhaust or exceed the target are refused if any noncertainty units remain in the pool, because those units would have zero inclusion probability.

on_empty

What to do when this stage selects nothing or has nothing to select from: a random-size method (bernoulli, pps_poisson, or a custom method registered with fixed_size = FALSE) that selects zero units in a stratum or the whole frame, or a unit selected at the previous stage with no rows in this stage's frame, such as a household with no eligible member. One of:

  • "error" (default): stop with an error. Zero selections usually signal a design problem, such as a sampling fraction or a stratum that is too small.

  • "warn": warn and keep the empty selection.

  • "silent": keep the empty selection without a message.

An empty selection is a valid realization of a random-size design. It contributes zero to Horvitz-Thompson totals, so estimates over repeated executions remain unbiased. Later stages of a multistage design then have nothing to select from, and the sample is empty. "warn" and "silent" are meant for simulation and replicated runs, so check nrow() before analyzing a single realization.

A selected unit with no rows in the next frame is refused under "error" (samplyr_error_frame_missing_parent). Under "warn" (samplyr_warning_empty_parent, once per stage) or "silent" it stays in the design with nothing selected under it and contributes zero to every total. It is recorded in the sample, counted by summary(), exempt from the incomplete-register checks of validate_frame() and execute(), and named at export by as_svydesign(). variance_estimation gives how the export treats such units, and why a primary unit left with no row is refused.

A replicated execution with empty replicates cannot feed a later execute() call (a new phase or a stage continuation). It raises samplyr_error_empty_phase_replicate rather than skip them and condition downstream results on nonempty realizations. Handle them explicitly, for example by executing each nonempty replicate separately and accounting for the empty ones in the analysis.

on_capped

What to do when a fixed take n, drawn without replacement, exceeds the units of a pool, such as a take of 12 households in an EA that lists 10. One of:

  • "warn" (default): take every unit of the pool and warn with samplyr_warning_size_capped (or samplyr_warning_census when every pool of the stage is exhausted). The inclusion probabilities describe that take-all rule, so the weights stay valid.

  • "error": refuse before anything is drawn (samplyr_error_size_capped), naming the stage and the short pools.

The check covers every pool the stage would draw from if its parent were selected, so whether a design runs does not depend on the seed. An allocation total above the population of the stage counts as capped. A stratum an allocation method fills to its population and whose surplus goes to other strata does not, because the total is delivered, and neither does a min_n above the population with frac, which the rule above makes a census. execute() checks a stage when it receives that stage's frame, so in staged execution a later stage is checked by the call that lists it. validate_frame() and exante_probabilities() refuse the same designs, while frame_summary() still reports their short pools. "error" is refused with a random-size method, a method with replacement, or frac, where no fixed take can fall short.

Value

A modified sampling_design object with selection parameters specified.

Details

Certainty selection

In PPS without-replacement sampling, very large units can have theoretical inclusion probabilities above 1. Certainty selection takes them with probability 1 before sampling the remainder, and marks them in the .certainty_k column described in sample_columns. It is available for the WOR PPS methods (pps_systematic, pps_brewer, pps_cps, pps_sampford, pps_poisson, pps_sps, pps_pareto). With-replacement (pps_multinomial) and PMR (pps_chromy) methods handle large units through their hit mechanism. A pool censused under certainty_overflow = "allow" has stage weight 1, but in a multistage design the final .weight can still exceed 1 because it compounds all stage weights.

Certainty with pps_poisson. Both n and frac give a target expected total that includes certainty units, frac as the unrounded frac * N over the original pool. After n_cert certainty units are selected, the remaining probabilities are pmin(1, (target - n_cert) * mos / sum(mos)) over the remaining units' sizes. Thus n = 5 and frac = 0.5 on ten units give the same probabilities, with or without a certainty rule. Clipping at one does not redistribute the excess to other units, so the expected size can fall below the target. frame_summary() reports both n_target and n_expected.

Control sorting

Control sorting orders the frame before selection, giving implicit stratification. It is most effective with the systematic and sequential methods (systematic, pps_systematic, pps_chromy), where the sample spreads evenly across the sorted variables.

Nested sorting (the default, control = c(var1, var2, var3)) sorts ascending by each variable in turn. Serpentine sorting (control = serp(var1, var2, var3)) alternates direction at each hierarchy level, which minimizes the jumps between adjacent units. For a geographic hierarchy, the last district of region 1 is then adjacent to the last district of region 2.

With stratify_by(), sorting is applied within each stratum, combining explicit stratification for variance control with implicit stratification for sample spread.

See also

sampling_design() for creating designs, stratify_by() for stratification, cluster_by() for clustering, execute() for running designs, serp() for serpentine sorting, variance_estimation for how each method's variance is estimated

Other design specification: add_stage(), cluster_by(), sampling_design(), selection_methods, stratify_by()

Examples

# Simple random sample of 100 EAs
sampling_design() |>
  draw(n = 100) |>
  execute(bfa_eas, seed = 1)
#> # 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 43475 Est         Gnagna   Piéla   Rural              971        112     8.95
#>  2  6592 Sud-Ouest   Noumbiel Kpuéré  Rural               88         12     7.89
#>  3 11611 Boucle du … Nayala   Yaba    Rural              111         15     8.97
#>  4 45236 Centre-Est  Boulgou  Béguédo Urban              939        201     0.32
#>  5  3549 Est         Gourma   Fada-N… Rural              263         32     3.4 
#>  6 39095 Hauts-Bass… Kenedou… Sindo   Rural               37          4     6.09
#>  7 21818 Centre-Est  Kourite… Goungu… Rural               21          4     1.07
#>  8 15528 Centre      Kadiogo  Ouagad… Urban             1207        182     0.14
#>  9 14284 Est         Gourma   Matiak… Rural              178         21     8.86
#> 10  3433 Est         Gourma   Fada-N… Rural              285         34     8.55
#> # ℹ 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>

# Systematic sample of 10%
sampling_design() |>
  draw(frac = 0.10, method = "systematic") |>
  execute(bfa_eas, seed = 123)
#> # A tbl_sample: 4457 × 18
#> # Sampling:     1 stage | 4,457/44,570 units
#> # Weights:      10 [10, 10]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 11761 Boucle du … Bale     Bagassi Rural               63          8     8.54
#>  2 11771 Boucle du … Bale     Bagassi Rural              234         28     5.91
#>  3 11781 Boucle du … Bale     Bagassi Rural               86         10     3.68
#>  4 11791 Boucle du … Bale     Bagassi Rural              970        117     1.2 
#>  5 11801 Boucle du … Bale     Bagassi Rural              102         12     9.29
#>  6 11811 Boucle du … Bale     Bagassi Rural              135         16     8.25
#>  7 11821 Boucle du … Bale     Bagassi Rural              208         25     8.13
#>  8 11831 Boucle du … Bale     Bagassi Rural               92         11     8.97
#>  9 11843 Boucle du … Bale     Bagassi Rural              132         16    10.7 
#> 10 11853 Boucle du … Bale     Bagassi Rural               63          8     6.11
#> # ℹ 4,447 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>

# PPS sample of EAs using household count
sampling_design() |>
  cluster_by(ea_id) |>
  draw(n = 50, method = "pps_brewer", mos = households) |>
  execute(bfa_eas, seed = 42)
#> # A tbl_sample: 50 × 19
#> # Sampling:     1 stage | 50 units
#> # Weights:      970.83 [117.62, 7498.52]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1  1192 Boucle du … Bale     Boromo  Rural             2570        374     2.05
#>  2 36794 Boucle du … Bale     Fara    Rural             2115        309     1.07
#>  3 41812 Boucle du … Kossi    Dokui   Rural              718         99     1.24
#>  4 11459 Boucle du … Sourou   Toéni   Rural              461         68     0.36
#>  5 27089 Cascades    Leraba   Loumana Rural              491         66     9.54
#>  6 15148 Centre      Kadiogo  Ouagad… Urban              907        137     0.16
#>  7 15241 Centre      Kadiogo  Ouagad… Urban              983        148     0.14
#>  8 16887 Centre      Kadiogo  Ouagad… Urban              914        138     0.1 
#>  9 18171 Centre      Kadiogo  Ouagad… Urban             1349        203     0.39
#> 10   831 Centre-Est  Boulgou  Bissiga Rural              368         81     3.22
#> # ℹ 40 more rows
#> # ℹ 11 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>

# Bernoulli sampling with frac (random sample size, expected ~5%)
sampling_design() |>
  draw(frac = 0.05, method = "bernoulli") |>
  execute(ken_enterprises, seed = 12345)
#> # A tbl_sample: 827 × 14
#> # Sampling:     1 stage | 827/17,004 units
#> # Weights:      20 [20, 20]
#>    enterprise_id county    region  sector  size_class employees revenue_millions
#>  * <chr>         <fct>     <fct>   <fct>   <fct>          <int>            <dbl>
#>  1 KEN_00010     Nyandarua Central Chemic… Small             10             40.5
#>  2 KEN_00020     Kiambu    Central Constr… Small              9             17.4
#>  3 KEN_00023     Kiambu    Central Constr… Small             17             17.3
#>  4 KEN_00051     Kirinyaga Central Constr… Small             10             25.5
#>  5 KEN_00063     Murang'a  Central Constr… Medium            28             81.9
#>  6 KEN_00065     Murang'a  Central Constr… Medium            31             30.5
#>  7 KEN_00077     Murang'a  Central Constr… Small             10             14.7
#>  8 KEN_00103     Nyandarua Central Constr… Small             14             18.4
#>  9 KEN_00117     Nyandarua Central Constr… Small             12             31.1
#> 10 KEN_00146     Nyeri     Central Constr… Small              9              8.3
#> # ℹ 817 more rows
#> # ℹ 7 more variables: year_established <int>, exporter <lgl>, .weight <dbl>,
#> #   .sample_id <int>, .stage <int>, .weight_1 <dbl>, .fpc_1 <int>

# Bernoulli sampling with expected n (converted to frac = 500/N)
sampling_design() |>
  draw(n = 500, method = "bernoulli") |>
  execute(bfa_eas, seed = 42)
#> # A tbl_sample: 526 × 18
#> # Sampling:     1 stage | 526/44,570 units
#> # Weights:      89.14 [89.14, 89.14]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 11781 Boucle du … Bale     Bagassi Rural               86         10     3.68
#>  2 36665 Boucle du … Bale     Fara    Rural              518         76     8.89
#>  3 36750 Boucle du … Bale     Fara    Rural              107         16     4.14
#>  4  9032 Boucle du … Bale     Poura   Urban              432         82     4.38
#>  5  9047 Boucle du … Bale     Poura   Urban              181         34     4.7 
#>  6  9053 Boucle du … Bale     Poura   Urban              436         83     3.19
#>  7 39999 Boucle du … Banwa    Balavé  Rural             1820        287     2.04
#>  8 34012 Boucle du … Banwa    Sami    Rural              135         20     5.63
#>  9  9680 Boucle du … Banwa    Sanaba  Rural               59          8     8.98
#> 10 23812 Boucle du … Banwa    Solenzo Rural              679         91     8.85
#> # ℹ 516 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 with different sizes per stratum (data frame)
region_sizes <- data.frame(
  region = levels(bfa_eas$region),
  n = c(20, 12, 25, 18, 22, 16, 14, 15, 20, 18, 12, 10, 8)
)
sampling_design() |>
  stratify_by(region) |>
  draw(n = region_sizes) |>
  execute(bfa_eas, seed = 123)
#> # A tbl_sample: 210 × 18
#> # Sampling:     1 stage | 210/44,570 units
#> # Weights:      212.24 [115.14, 414.4]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 33181 Boucle du … Kossi    Nouna   Rural              133         17     0.07
#>  2 33229 Boucle du … Kossi    Nouna   Rural               48          6     4.53
#>  3 21506 Boucle du … Kossi    Doumba… Rural              485         83     4.82
#>  4 45264 Boucle du … Bale     Pâ      Rural              825         95     0.66
#>  5 26201 Boucle du … Sourou   Di      Rural               95         17    21.1 
#>  6 26077 Boucle du … Mouhoun  Dédoug… Rural              176         25     8.96
#>  7 25510 Boucle du … Kossi    Bombor… Rural              246         36     0.44
#>  8 23774 Boucle du … Banwa    Solenzo Rural              158         21     2.9 
#>  9  8332 Boucle du … Mouhoun  Ouarko… Rural               55          8     5.68
#> 10 43713 Boucle du … Mouhoun  Safané  Rural               97         14     6.46
#> # ℹ 200 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 <dbl>

# Stratified with different rates per stratum (named vector)
sampling_design() |>
  stratify_by(size_class) |>
  draw(frac = c(Small = 0.02, Medium = 0.10, Large = 0.50)) |>
  execute(ken_enterprises, seed = 42)
#> # A tbl_sample: 1330 × 14
#> # Sampling:     1 stage | 1,330/17,004 units
#> # Weights:      12.78 [2, 50]
#>    enterprise_id county     region  sector size_class employees revenue_millions
#>  * <chr>         <fct>      <fct>   <fct>  <fct>          <int>            <dbl>
#>  1 KEN_08403     Nairobi    Nairobi Other… Medium            64             94.6
#>  2 KEN_14993     Siaya      Nyanza… Other… Medium            73            141. 
#>  3 KEN_08163     Nairobi    Nairobi Other… Medium            53             68.2
#>  4 KEN_03931     Isiolo     East a… Other… Medium            95            158. 
#>  5 KEN_04478     Nairobi    Nairobi Const… Medium            60             52.1
#>  6 KEN_02158     Tana River Coast   Other… Medium            43             33.9
#>  7 KEN_06792     Nairobi    Nairobi Hotel… Medium            41             81.7
#>  8 KEN_14492     Migori     Nyanza… Const… Medium            54             57.8
#>  9 KEN_01181     Kilifi     Coast   Const… Medium            71            213. 
#> 10 KEN_15690     Laikipia   Rift V… Hotel… Medium            20             39.8
#> # ℹ 1,320 more rows
#> # ℹ 7 more variables: year_established <int>, exporter <lgl>, .weight <dbl>,
#> #   .sample_id <int>, .stage <int>, .weight_1 <dbl>, .fpc_1 <dbl>

# Neyman allocation with minimum 2 per stratum (for variance estimation)
sampling_design() |>
  stratify_by(region, alloc = "neyman", variance = bfa_eas_variance) |>
  draw(n = 150, min_n = 2) |>
  execute(bfa_eas, seed = 2026)
#> # A tbl_sample: 150 × 18
#> # Sampling:     1 stage | 150/44,570 units
#> # Weights:      297.13 [278.67, 332.4]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 34795 Boucle du … Sourou   Tougan  Rural               45          7    14.0 
#>  2 44496 Boucle du … Mouhoun  Tchéri… Rural              180         29    16.6 
#>  3  9624 Boucle du … Banwa    Sanaba  Rural              177         23     6.56
#>  4  7012 Boucle du … Kossi    Madouba Rural              387         48     6.18
#>  5 44420 Boucle du … Mouhoun  Tchéri… Rural               63         10    22.3 
#>  6   515 Boucle du … Kossi    Barani  Rural              651         85     1.11
#>  7  1144 Boucle du … Bale     Boromo  Rural               75         11     8.92
#>  8  8378 Boucle du … Bale     Ouri    Rural              693         98     9.26
#>  9  4909 Boucle du … Sourou   Kassoum Rural              662         90     0.65
#> 10 21094 Boucle du … Kossi    Bouras… Rural               25          4     5.1 
#> # ℹ 140 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 <dbl>

# Proportional allocation with min and max bounds
sampling_design() |>
  stratify_by(region, alloc = "proportional") |>
  draw(n = 200, min_n = 10, max_n = 50) |>
  execute(bfa_eas, seed = 1)
#> # A tbl_sample: 200 × 18
#> # Sampling:     1 stage | 200/44,570 units
#> # Weights:      222.85 [161.2, 240.7]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1  9648 Boucle du … Banwa    Sanaba  Rural              279         35     8.37
#>  2 11547 Boucle du … Sourou   Toéni   Rural               49          7    21.4 
#>  3 41824 Boucle du … Kossi    Dokui   Rural               75         10    15.8 
#>  4 11012 Boucle du … Banwa    Tansila Rural              592         71     0.95
#>  5 32308 Boucle du … Sourou   Lanfiè… Rural               57          8     9.49
#>  6  7017 Boucle du … Kossi    Madouba Rural              599         74     0.74
#>  7 36700 Boucle du … Bale     Fara    Rural              402         59     6.36
#>  8 11611 Boucle du … Nayala   Yaba    Rural              111         15     8.97
#>  9  8342 Boucle du … Mouhoun  Ouarko… Rural               94         13     8.1 
#> 10 11626 Boucle du … Nayala   Yaba    Rural               56          7     8.31
#> # ℹ 190 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 <dbl>

# 0.5% of the EAs in each province, at least 3 and at most 8 (SPSS rule)
sampling_design() |>
  stratify_by(province) |>
  draw(frac = 0.005, min_n = 3, max_n = 8, round = "nearest") |>
  execute(bfa_eas, seed = 4)
#> # A tbl_sample: 211 × 18
#> # Sampling:     1 stage | 211/44,570 units
#> # Weights:      211.23 [105.67, 486]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1  8601 Boucle du … Bale     Pâ      Rural              794         92     8.93
#>  2  9046 Boucle du … Bale     Poura   Urban             4132        784     3.25
#>  3 11829 Boucle du … Bale     Bagassi Rural              133         16     8.41
#>  4 11740 Boucle du … Bale     Yaho    Rural              556         83     0.92
#>  5  9703 Boucle du … Banwa    Sanaba  Rural              272         35     7.14
#>  6 10937 Boucle du … Banwa    Tansila Rural              246         30     9.82
#>  7 24033 Boucle du … Banwa    Solenzo Rural              141         19     2.87
#>  8  9639 Boucle du … Banwa    Sanaba  Rural              300         38     6.42
#>  9 21112 Boucle du … Kossi    Bouras… Rural              106         17     8.91
#> 10 12790 Boucle du … Kossi    Djibas… Rural              914        130     2.91
#> # ℹ 201 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 <dbl>

# Control sorting with serpentine ordering (implicit stratification)
sampling_design() |>
  draw(n = 100, method = "systematic",
       control = serp(region, province)) |>
  execute(bfa_eas, seed = 2)
#> # 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 11843 Boucle du … Bale     Bagassi Rural              132         16    10.7 
#>  2  8876 Boucle du … Bale     Pompoï  Rural               55          8     8.62
#>  3 34070 Boucle du … Banwa    Sami    Rural               24          4     8.12
#>  4 24054 Boucle du … Banwa    Solenzo Rural              123         17     8.82
#>  5 21070 Boucle du … Kossi    Bouras… Rural              115         18     9.87
#>  6 37405 Boucle du … Kossi    Kombori Rural              308         39     9.76
#>  7 21009 Boucle du … Mouhoun  Bondok… Rural              556         73     9.16
#>  8  5672 Boucle du … Mouhoun  Kona    Rural              222         35     8.98
#>  9 44440 Boucle du … Mouhoun  Tchéri… Rural               55          9     5.53
#> 10 11656 Boucle du … Nayala   Yaba    Rural             1292        170     1.35
#> # ℹ 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>

# Control sorting with nested (standard) ordering
sampling_design() |>
  draw(n = 100, method = "systematic",
       control = c(region, province)) |>
  execute(bfa_eas, seed = 3)
#> # 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 11833 Boucle du … Bale     Bagassi Rural               35          4     7.15
#>  2  8620 Boucle du … Bale     Pâ      Rural               52          6     1.46
#>  3 34062 Boucle du … Banwa    Sami    Rural              286         43     7.68
#>  4 24046 Boucle du … Banwa    Solenzo Rural               24          3     6.37
#>  5 25526 Boucle du … Kossi    Bombor… Rural              152         22     0.06
#>  6 37398 Boucle du … Kossi    Kombori Rural              201         25     5.89
#>  7 21002 Boucle du … Mouhoun  Bondok… Rural              791        104     9.13
#>  8  5663 Boucle du … Mouhoun  Kona    Rural              180         28     7.94
#>  9 44432 Boucle du … Mouhoun  Tchéri… Rural              502         80    23.6 
#> 10 11649 Boucle du … Nayala   Yaba    Rural              526         69     7.66
#> # ℹ 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>

# Combined explicit stratification with control sorting within strata
sampling_design() |>
  stratify_by(urban_rural) |>
  draw(n = 50, method = "systematic",
       control = serp(region, province)) |>
  execute(bfa_eas, seed = 25)
#> # A tbl_sample: 100 × 18
#> # Sampling:     1 stage | 100/44,570 units
#> # Weights:      445.7 [137.66, 753.74]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 36744 Boucle du … Bale     Fara    Rural              565         83     9.07
#>  2  9728 Boucle du … Banwa    Sanaba  Rural               69          9     6.85
#>  3 25519 Boucle du … Kossi    Bombor… Rural              240         35     4.62
#>  4 10541 Boucle du … Kossi    Sônô    Rural              738        110     0.6 
#>  5  8318 Boucle du … Mouhoun  Ouarko… Rural              160         22     7.76
#>  6 11674 Boucle du … Nayala   Yaba    Rural             1353        178     1.3 
#>  7 34832 Boucle du … Sourou   Tougan  Rural               33          5     8.81
#>  8 20641 Cascades    Comoe    Banfora Rural              996        114     0.24
#>  9 15093 Cascades    Comoe    Niango… Rural              126         15     8.87
#> 10 10357 Cascades    Comoe    Sidéra… Rural               31          5     8.64
#> # ℹ 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>

# PPS with certainty selection (absolute threshold)
# Large EAs selected with certainty, rest sampled with PPS
sampling_design() |>
  stratify_by(region) |>
  draw(n = 100, method = "pps_brewer", mos = households,
       certainty_size = 800) |>
  execute(bfa_eas, seed = 3)
#> # A tbl_sample: 1300 × 19
#> # Sampling:     1 stage | 1,300/44,570 units
#> # Weights:      35.46 [1, 1097.49]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 39984 Boucle du … Banwa    Balavé  Rural              193         30     0.3 
#>  2 11674 Boucle du … Nayala   Yaba    Rural             1353        178     1.3 
#>  3 12747 Boucle du … Kossi    Djibas… Rural              679         97     0.37
#>  4 11053 Boucle du … Banwa    Tansila Rural              651         78     2.31
#>  5 26118 Boucle du … Mouhoun  Dédoug… Rural              683         97     0.54
#>  6 26130 Boucle du … Mouhoun  Dédoug… Rural              415         59     0.13
#>  7  8916 Boucle du … Bale     Pompoï  Rural              916        135     0.73
#>  8 23995 Boucle du … Banwa    Solenzo Rural              887        119     0.59
#>  9 26018 Boucle du … Mouhoun  Dédoug… Rural              985        140     1   
#> 10  5658 Boucle du … Mouhoun  Kona    Rural              616         97     8.53
#> # ℹ 1,290 more rows
#> # ℹ 11 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>

# PPS with certainty selection (proportional threshold)
# EAs with >= 10% of stratum total selected with certainty
sampling_design() |>
  stratify_by(region) |>
  draw(n = 100, method = "pps_systematic", mos = households,
       certainty_prop = 0.10) |>
  execute(bfa_eas, seed = 321)
#> # A tbl_sample: 1300 × 19
#> # Sampling:     1 stage | 1,300/44,570 units
#> # Weights:      33.25 [2.16, 661.2]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 11818 Boucle du … Bale     Bagassi Rural               37          4     6.38
#>  2 29503 Boucle du … Bale     Bana    Rural              530         69     0.45
#>  3  1136 Boucle du … Bale     Boromo  Rural             3613        526     4.23
#>  4  1177 Boucle du … Bale     Boromo  Rural             1383        201     2.06
#>  5  1207 Boucle du … Bale     Boromo  Rural             1465        213     1.67
#>  6 36701 Boucle du … Bale     Fara    Rural              638         93     0.81
#>  7 36734 Boucle du … Bale     Fara    Rural             1114        163     1.07
#>  8 36769 Boucle du … Bale     Fara    Rural              995        145     1.41
#>  9  8390 Boucle du … Bale     Ouri    Rural              682         97     8.14
#> 10  8435 Boucle du … Bale     Ouri    Rural              624         88     0.61
#> # ℹ 1,290 more rows
#> # ℹ 11 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>

# Stratum-specific certainty thresholds (data frame)
cert_thresholds <- data.frame(
  region = levels(bfa_eas$region),
  certainty_size = c(700, 450, 800, 850, 750, 800, 550,
                     450, 700, 950, 750, 600, 480)
)
sampling_design() |>
  stratify_by(region) |>
  draw(n = 100, method = "pps_brewer", mos = households,
       certainty_size = cert_thresholds) |>
  execute(bfa_eas, seed = 424)
#> # A tbl_sample: 1300 × 19
#> # Sampling:     1 stage | 1,300/44,570 units
#> # Weights:      35.78 [1, 847.49]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1  9046 Boucle du … Bale     Poura   Urban             4132        784     3.25
#>  2 34802 Boucle du … Sourou   Tougan  Rural               80         12     7.67
#>  3 12839 Boucle du … Kossi    Djibas… Rural             1300        185     1.74
#>  4 39958 Boucle du … Banwa    Balavé  Rural              684        108     0.88
#>  5  9914 Boucle du … Bale     Siby    Rural              166         25     3.44
#>  6 23777 Boucle du … Banwa    Solenzo Rural              485         65     8.06
#>  7 25901 Boucle du … Mouhoun  Dédoug… Rural              901        128     7.84
#>  8 34036 Boucle du … Banwa    Sami    Rural              357         54     6.67
#>  9 44351 Boucle du … Mouhoun  Tchéri… Rural              234         37     8.31
#> 10 26001 Boucle du … Mouhoun  Dédoug… Rural              183         26     0.06
#> # ℹ 1,290 more rows
#> # ℹ 11 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>