stratify_by() specifies stratification variables and an optional
allocation method for a sampling design, so that every subgroup the
variables define is represented. It applies to the current stage and comes
before that stage's draw(), which closes it.
Usage
stratify_by(
.data,
...,
alloc = NULL,
variance = NULL,
cost = NULL,
cv = NULL,
importance = NULL,
power = NULL
)Arguments
- .data
A
sampling_designobject (piped fromsampling_design(),add_stage(), orcluster_by()), before the stage'sdraw().- ...
Stratification variables as bare column names. A 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 naming the allocation method. With a method,
nindraw()is the total sample size to distribute. One of:NULL(default): No allocation.nindraw()is per stratum"equal": Splits the total as evenly as integer rounding,min_n,max_nand the stratum populations allow, so the final sizes can differ"proportional": Proportional to the number of the stage's sampling units in each stratum, or toimportancewhen it is supplied"neyman": Neyman optimal allocation (requiresvariance)"optimal": Cost-variance optimal allocation (requiresvarianceandcost)"power": Power allocation (requirescvandimportance)
Every method caps a stratum at its population and redistributes the surplus, so
"equal"on populations \((10, 490, 500)\) withn = 300gives 10/145/145 rather than 100/100/100.execute()reports this with a message of classsamplyr_message_allocation_capped(see execution_conditions).An input the method does not read is refused with class
samplyr_error_alloc_unused_auxrather than ignored:variancenext to"proportional",costnext to"neyman", or any ofvariance,cost,cv,importance,powerwith noalloc.- variance
Stratum variances for Neyman or optimal allocation. Either a data frame with every stratification column (the join keys) plus a
varcolumn, or, with a single stratification variable only, a named numeric vector whose names match the values of the stratification column (for examplec(A = 1.2, B = 0.8)).- cost
Stratum costs for optimal allocation, in the same two forms as
variancewith acostcolumn.- cv
Stratum coefficients of variation (\(C_h\)) for power allocation, in the same two forms as
variancewith acvcolumn.- importance
A positive size per stratum (\(X_h\)), in the same two forms as
variancewith animportancecolumn. It is what"proportional"allocates in proportion to, such as each stratum's household total, or the importance measure of power allocation.- power
Power exponent \(q\) for power allocation. Must satisfy \(0 \le q \le 1\). Defaults to
0.5.
Details
Proportional allocation
Each stratum receives \(n \times N_h/N\) units, where \(N_h\)
is the number of the stage's sampling units in stratum h and N their total.
At a clustered stage \(N_h\) counts PSUs, not the elements inside them,
and mos does not enter the allocation.
With importance, each stratum receives
\(n \times X_h / \sum X_h\) units instead, so a
clustered stage can allocate its PSUs in proportion to the households they
hold. This gives the same sizes as power allocation with every
\(C_h = 1\) and \(q = 1\). A two-stage plan allocated on the ultimate
units, from svyplan::n_alloc() with a cluster model, is passed to
draw() directly.
Neyman allocation
Minimizes variance for a fixed sample size. Each stratum receives \(n \times (N_h \times S_h) / \sum(N_h \times S_h)\) units, where S_h is the stratum standard deviation. At a clustered stage the units allocated are PSUs, so S_h is the standard deviation of PSU totals.
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})\) units, where C_h is the per-unit cost in stratum h.
Power allocation
A compromise allocation (Bankier, 1988) with \(n_h \propto C_h \times X_h^q\), where \(C_h\) is the stratum CV, \(X_h\) a stratum importance measure, and \(q \in [0, 1]\).
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 cap is always present, and min_n and max_n add user
bounds on top of it.
Saturated strata are fixed at their bound and removed, and the remaining total is reallocated over the other strata with the method's own factors (\(N_h\) for proportional, \(N_h \sqrt{S_h^2}\) for Neyman, and so on), which keeps the stated criterion after saturation. When the free strata all carry zero factors, the remainder is split equally in stratum order.
Real-valued targets become integers by largest remainder, which keeps the total. Among strata with equal remainders the larger target is served first, and strata still tied are served in a fixed order of their labels, the "stratum order" above. It depends on neither the order of the frame's rows nor the session locale, so a permuted frame gets the same allocation.
Requesting more than the frame holds allocates every unit rather than
failing, and execute() warns with class samplyr_warning_census, or
samplyr_warning_nominal_cap for a random-size method (see
execution_conditions). Bounds that make the request impossible are
errors of class samplyr_error_alloc_min_infeasible or
samplyr_error_alloc_max_infeasible.
With-replacement (srswr, pps_multinomial) and minimum-replacement
(pps_chromy) methods can select a unit more than once, so the number of
distinct units does not bound the draws, and 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 never redistributed. Selection caps an impossible one
and reports it.
Custom allocation
For sizes or rates chosen per stratum, pass draw() a data frame as n
or frac, with every stratification column plus an n or frac column.
References
Bankier, M.D. (1988). Power allocations: determining sample sizes for subnational areas. The American Statistician, 42(3), 174-177.
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>
# With an allocation method the same n is a total: 20 EAs in all,
# shared as evenly as possible across the 13 regions
shared <- sampling_design() |>
stratify_by(region, alloc = "equal") |>
draw(n = 20) |>
execute(bfa_eas, seed = 1234)
#> Stage 1: 6 strata take a single unit outside certainty.
#> ℹ Strata with one unit: "Est", "Hauts-Bassins", "Nord", "Plateau-Central",
#> "Sahel", and 1 more.
#> ℹ These sizes come from `alloc = "equal"`, which shares `n` across the strata.
#> Without `alloc`, `n` is taken in each stratum.
#> ℹ A stratum needs two selections for its variance to be estimated. survey stops
#> on a stratum with one unless its strata are collapsed, and a replicate method
#> has nothing to resample in it.
#> ℹ To estimate within each stratum, allocate at least two units outside
#> certainty to it. `min_n` bounds a stratum's whole take, certainty units
#> included.
table(shared$region)
#>
#> Boucle du Mouhoun Cascades Centre Centre-Est
#> 2 2 2 2
#> Centre-Nord Centre-Ouest Centre-Sud Est
#> 2 2 2 1
#> Hauts-Bassins Nord Plateau-Central Sahel
#> 1 1 1 1
#> Sud-Ouest
#> 1
# 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>
# EAs allocated in proportion to each region's households rather than to
# its number of EAs
households <- stats::aggregate(households ~ region, bfa_eas, sum)
names(households)[2] <- "importance"
sampling_design() |>
stratify_by(region, alloc = "proportional", importance = households) |>
cluster_by(ea_id) |>
draw(n = 100) |>
execute(bfa_eas, seed = 7)
#> # A tbl_sample: 100 × 18
#> # Sampling: 1 stage | 100 units
#> # Weights: 445.7 [243, 836]
#> ea_id region province commune urban_rural population households area_km2
#> * <int> <fct> <fct> <fct> <fct> <int> <int> <dbl>
#> 1 9030 Boucle du … Bale Poura Urban 562 107 8.94
#> 2 10970 Boucle du … Banwa Tansila Rural 488 59 10.7
#> 3 10975 Boucle du … Banwa Tansila Rural 141 17 16.0
#> 4 20930 Boucle du … Mouhoun Bondok… Rural 792 104 9.76
#> 5 21006 Boucle du … Mouhoun Bondok… Rural 102 13 3.08
#> 6 26158 Boucle du … Mouhoun Dédoug… Rural 214 30 5.38
#> 7 44747 Boucle du … Nayala Toma Rural 73 12 10.2
#> 8 4080 Boucle du … Sourou Gomboro Rural 163 23 0.2
#> 9 32313 Boucle du … Sourou Lanfiè… Rural 45 6 8.78
#> 10 20713 Cascades Comoe Banfora Rural 935 107 0.52
#> # ℹ 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 <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, min_n = 2) |>
execute(bfa_eas, seed = 2025)
#> # A tbl_sample: 300 × 18
#> # Sampling: 1 stage | 300/44,570 units
#> # Weights: 148.57 [14.5, 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>