Skip to contents

stratify_by() specifies stratification variables and optional allocation methods for a sampling design. Stratification ensures representation from all subgroups defined by the stratification variables.

Usage

stratify_by(
  .data,
  ...,
  alloc = NULL,
  variance = NULL,
  cost = NULL,
  cv = NULL,
  importance = NULL,
  power = NULL
)

Arguments

.data

A sampling_design object (piped from sampling_design() or add_stage()).

...

Stratification variables specified as bare column names. Any name given here is a label and does not rename the variable. A label resembling one of the arguments below (allocc, varianc) is refused, because those arguments follow ... and are matched exactly.

alloc

Character string specifying the allocation method. One of:

  • NULL (default): No allocation. n in draw() is per stratum

  • "equal": Equal allocation across strata

  • "proportional": Proportional to stratum size

  • "neyman": Neyman optimal allocation (requires variance)

  • "optimal": Cost-variance optimal allocation (requires variance and cost)

  • "power": Power allocation (requires cv and importance)

Every method is capped at the stratum population: a stratum is never allocated more units than it holds, and the surplus is redistributed across the strata with room left, in proportion to the same factors the method uses. A named rule can therefore be departed from when a stratum is too small to absorb its share, so "equal" on populations \((10, 490, 500)\) with n = 300 gives 10/145/145 rather than 100/100/100. execute() reports this once per stage with a message of class samplyr_message_allocation_capped, however many parent pools or replicates the stage runs over.

variance

Stratum variances for Neyman or optimal allocation. Either a data frame with columns for all stratification variables plus a var column, or a named numeric vector (when using a single stratification variable) where names correspond to stratum levels. For named vectors, names must match the values in the stratification column (for example c(A = 1.2, B = 0.8)).

cost

Stratum costs for optimal allocation. Either a data frame with columns for all stratification variables plus a cost column, or a named numeric vector (when using a single stratification variable) where names correspond to stratum levels. For named vectors, names must match the values in the stratification column.

cv

Stratum coefficients of variation (\(C_h\)) for power allocation. Either a data frame with stratification columns plus a cv column, or a named numeric vector for a single stratification variable (names are stratum levels).

importance

Stratum importance measure (\(X_h\)) for power allocation. Either a data frame with stratification columns plus an importance column, or a named numeric vector for a single stratification variable (names are stratum levels).

power

Power exponent \(q\) for power allocation. Must satisfy \(0 \le q \le 1\). Defaults to 0.5.

Value

A modified sampling_design object with stratification specified.

Details

Allocation methods

When no alloc is specified, the n parameter in draw() is interpreted as the sample size per stratum. When an alloc method is specified, n becomes the total sample size to be distributed according to the allocation method.

Population bounds and redistribution

For an allocation method the realized sizes satisfy \(\sum_h n_h = n\) and \(0 \le n_h \le N_h\) whenever the request is feasible. The population size is an upper bound in its own right, so min_n and max_n add user bounds on top of a cap that is always present rather than introducing one.

Saturated strata are fixed at their bound and removed from the problem, then the remaining total is reallocated over the remaining strata using the method's own factors (\(N_h\) for proportional, \(N_h \sqrt{S_h^2}\) for Neyman, and so on). This keeps the stated criterion intact after saturation. When the free strata all carry zero factors the criterion is indifferent between them, and the remainder is split equally in stratum order.

Requesting more than the frame holds allocates every unit rather than failing. execute() reports it through the same per-stage diagnostic as every other population cap, so a stage that ends up taking everything it could reach warns with class samplyr_warning_census, and a random-size method in the same position warns with samplyr_warning_nominal_cap. Bounds that make the request impossible are errors rather than silent adjustments, with classes samplyr_error_alloc_min_infeasible and samplyr_error_alloc_max_infeasible.

With-replacement and Poisson-multinomial designs draw units repeatedly, so the number of distinct units is not an upper bound on the number of draws. For those methods only min_n and max_n bind.

Per-stratum sizes given directly, through a scalar or named n, a frac, or a data frame, are instructions rather than a total to distribute. They are never redistributed. Selection caps an impossible one and reports it.

Equal allocation

Each stratum receives n/H units, where H is the number of strata.

Proportional allocation

Each stratum receives \(n \times N_h/N\) units, where \(N_h\) is the stratum population size and N is the total population size.

Neyman allocation

Minimizes variance for fixed sample size. Each stratum receives: \(n \times (N_h \times S_h) / \sum(N_h \times S_h)\) where S_h is the stratum standard deviation.

Optimal allocation

Minimizes variance for fixed cost (or cost for fixed variance). Each stratum receives: \(n \times (N_h \times S_h / \sqrt{C_h}) / \sum(N_h \times S_h / \sqrt{C_h})\) where C_h is the per-unit cost in stratum h.

Power allocation

Power allocation (Bankier, 1988) is a compromise allocation: \(n_h \propto C_h \times X_h^q\), where \(C_h\) is stratum CV, \(X_h\) is a stratum importance measure, and \(q \in [0, 1]\).

Custom allocation

For custom stratum-specific sample sizes or rates, pass a data frame directly to the n or frac argument in draw(). The data frame must contain columns for all stratification variables plus an n or frac column.

Auxiliary input formats (variance, cost, cv, importance)

  • With one stratification variable, you may use a named vector (e.g., variance = c(A = 1.2, B = 0.8)).

  • With multiple stratification variables, you must use a data frame containing all stratification columns plus the value column.

Data frame requirements

Auxiliary data frames (variance, cost) must contain:

  • All stratification variable columns (used as join keys)

  • The appropriate value column (var or cost)

See also

sampling_design() for creating designs, draw() for specifying sample sizes, cluster_by() for cluster sampling

Other design specification: add_stage(), cluster_by(), draw(), sampling_design(), selection-methods

Examples

# Simple stratification: 20 EAs per region
sampling_design() |>
  stratify_by(region) |>
  draw(n = 20) |>
  execute(bfa_eas, seed = 1234)
#> # A tbl_sample: 260 × 18
#> # Sampling:     1 stage | 260/44,570 units
#> # Weights:      171.42 [80.6, 275.25]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1  9635 Boucle du … Banwa    Sanaba  Rural              357         45     0.77
#>  2  9908 Boucle du … Bale     Siby    Rural              113         17     8.92
#>  3 20945 Boucle du … Mouhoun  Bondok… Rural              395         52     7.58
#>  4 34029 Boucle du … Banwa    Sami    Rural              363         55     8.85
#>  5 31936 Boucle du … Sourou   Kiemba… Rural              683         89     0.6 
#>  6 26038 Boucle du … Mouhoun  Dédoug… Rural              351         50     8.67
#>  7 41793 Boucle du … Kossi    Dokui   Rural              184         25     8.27
#>  8  5643 Boucle du … Mouhoun  Kona    Rural              101         16     8.38
#>  9 21026 Boucle du … Mouhoun  Bondok… Rural               64          8     7.92
#> 10 33091 Boucle du … Kossi    Nouna   Rural               28          4     8.55
#> # ℹ 250 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 across regions
sampling_design() |>
  stratify_by(region, alloc = "proportional") |>
  draw(n = 200) |>
  execute(bfa_eas, seed = 123)
#> # A tbl_sample: 200 × 18
#> # Sampling:     1 stage | 200/44,570 units
#> # Weights:      222.85 [217.78, 237.43]
#>    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
#> # ℹ 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>

# Neyman allocation using pre-computed variances
sampling_design() |>
  stratify_by(region, alloc = "neyman", variance = bfa_eas_variance) |>
  draw(n = 200) |>
  execute(bfa_eas, seed = 12)
#> # A tbl_sample: 200 × 18
#> # Sampling:     1 stage | 200/44,570 units
#> # Weights:      222.85 [209, 243]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 31987 Boucle du … Sourou   Kiemba… Rural               24          3     7.24
#>  2  4968 Boucle du … Sourou   Kassoum Rural              303         41     4.13
#>  3  4958 Boucle du … Sourou   Kassoum Rural              142         19     8.24
#>  4 23903 Boucle du … Banwa    Solenzo Rural               94         13    13.5 
#>  5 25996 Boucle du … Mouhoun  Dédoug… Rural              538         76     8.63
#>  6 21501 Boucle du … Kossi    Doumba… Rural              247         42     7.93
#>  7 43794 Boucle du … Mouhoun  Safané  Rural               87         12     8.96
#>  8  9724 Boucle du … Banwa    Sanaba  Rural               51          6     8.02
#>  9 10979 Boucle du … Banwa    Tansila Rural               71          9     7.72
#> 10  8893 Boucle du … Bale     Pompoï  Rural              522         77     0.48
#> # ℹ 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>

# Optimal allocation considering both variance and cost
sampling_design() |>
  stratify_by(region, alloc = "optimal",
              variance = bfa_eas_variance,
              cost = bfa_eas_cost) |>
  draw(n = 200) |>
  execute(bfa_eas, seed = 1)
#> # A tbl_sample: 200 × 18
#> # Sampling:     1 stage | 200/44,570 units
#> # Weights:      222.85 [194.4, 243.76]
#>    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>

# Power allocation (Bankier, 1988)
sampling_design() |>
  stratify_by(
    region,
    alloc = "power",
    cv = data.frame(
      region = levels(bfa_eas$region),
      cv = c(0.40, 0.35, 0.12, 0.20, 0.30, 0.18,
             0.15, 0.38, 0.22, 0.32, 0.17, 0.45, 0.25)
    ),
    importance = data.frame(
      region = levels(bfa_eas$region),
      importance = c(60, 40, 120, 70, 80, 65,
                     50, 55, 90, 75, 45, 35, 30)
    ),
    power = 0.5
  ) |>
  draw(n = 200) |>
  execute(bfa_eas, seed = 7)
#> # A tbl_sample: 200 × 18
#> # Sampling:     1 stage | 200/44,570 units
#> # Weights:      222.85 [139.52, 388.8]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 10970 Boucle du … Banwa    Tansila Rural              488         59    10.7 
#>  2 32313 Boucle du … Sourou   Lanfiè… Rural               45          6     8.78
#>  3 21006 Boucle du … Mouhoun  Bondok… Rural              102         13     3.08
#>  4 20930 Boucle du … Mouhoun  Bondok… Rural              792        104     9.76
#>  5 26158 Boucle du … Mouhoun  Dédoug… Rural              214         30     5.38
#>  6 10975 Boucle du … Banwa    Tansila Rural              141         17    16.0 
#>  7 44747 Boucle du … Nayala   Toma    Rural               73         12    10.2 
#>  8  9030 Boucle du … Bale     Poura   Urban              562        107     8.94
#>  9  4080 Boucle du … Sourou   Gomboro Rural              163         23     0.2 
#> 10 34042 Boucle du … Banwa    Sami    Rural              100         15     7.46
#> # ℹ 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>

# Custom sample sizes per stratum using a data frame
custom_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 = custom_sizes) |>
  execute(bfa_eas, seed = 2026)
#> # 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 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 
#> # ℹ 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>

# Multiple stratification variables
sampling_design() |>
  stratify_by(region, urban_rural, alloc = "proportional") |>
  draw(n = 300) |>
  execute(bfa_eas, seed = 2025)
#> # A tbl_sample: 300 × 18
#> # Sampling:     1 stage | 300/44,570 units
#> # Weights:      148.35 [82, 176]
#>    ea_id region      province commune urban_rural population households area_km2
#>  * <int> <fct>       <fct>    <fct>   <fct>            <int>      <int>    <dbl>
#>  1 34056 Boucle du … Banwa    Sami    Rural              136         20     7.25
#>  2 12794 Boucle du … Kossi    Djibas… Rural               61          9     8.98
#>  3 36709 Boucle du … Bale     Fara    Rural              341         50     6.47
#>  4 11042 Boucle du … Banwa    Tansila Rural              118         14     8.43
#>  5  1167 Boucle du … Bale     Boromo  Rural              120         17   181.  
#>  6 23952 Boucle du … Banwa    Solenzo Rural              106         14     7.32
#>  7 32327 Boucle du … Sourou   Lanfiè… Rural              108         15     8.21
#>  8 44753 Boucle du … Nayala   Toma    Rural               72         12     7.27
#>  9 12732 Boucle du … Kossi    Djibas… Rural              772        110     8.62
#> 10 11071 Boucle du … Banwa    Tansila Rural               26          3     7.28
#> # ℹ 290 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>