Skip to contents

Stratification divides the population before sampling. An allocation then decides how many units to select from each stratum. A domain is different. It is a population for which an estimate is required, and it can contain one or more strata.

This article covers one-indicator allocation. See Multiple indicators and domains for requirements that combine into one total, and Joint allocation when one allocation must satisfy several estimands and domains simultaneously.

Allocate a fixed sample

The frame has one row per stratum. N is the population size, sd is the anticipated within-stratum standard deviation, and mean supplies the scale for a CV calculation:

frame <- data.frame(
  stratum = c("Urban", "Peri-urban", "Rural"),
  N = c(4000, 3000, 3000),
  sd = c(10, 15, 8),
  mean = c(50, 60, 55)
)

allocation <- n_alloc(frame, n = 600, alloc = "neyman")
allocation
#> Stratum allocation (neyman, 3 strata)
#> field design: n = 600, cv = 0.0079, cost = 600
#> continuous optimum: n = 600, cv = 0.0079, se = 0.4305
#> (deff = 1)
#> design df = 597

Neyman allocation places more sample where N_hS_h is larger. The result keeps a continuous optimum in $detail$n and a whole-unit fieldwork proposal in $detail$n_int:

allocation$detail[, c("stratum", "N", "n", "n_int", "weight")]
#>      stratum    N        n n_int   weight
#> 1      Urban 4000 220.1835   220 18.16667
#> 2 Peri-urban 3000 247.7064   248 12.11111
#> 3      Rural 3000 132.1101   132 22.70833

Use the operational column when issuing a sample. Its planned weight is the population-to-sample ratio for the allocation, not a final analysis weight adjusted for realized selection and response.

Check an operational allocation

prec_alloc() evaluates any proposed set of stratum sample sizes. Passing the planning result evaluates its continuous design. Supply the integer column to check the whole-unit fieldwork design:

prec_alloc(allocation)
#> Sampling precision for alloc
#> n = 600 (3 strata)
#> se = 0.4305, moe = 0.8438, cv = 0.0079, rmoe = 0.0155
prec_alloc(allocation, n = allocation$detail$n_int)
#> Sampling precision for alloc
#> n = 600 (3 strata)
#> se = 0.4305, moe = 0.8438, cv = 0.0079, rmoe = 0.0155

The detail table shows how much each stratum contributes to overall variance. .share sums to 1:

assessment <- prec_alloc(allocation, n = allocation$detail$n_int)
assessment$detail[, c("stratum", "n", ".se", ".cv", ".share")]
#>      stratum   n       .se        .cv    .share
#> 1      Urban 220 0.6553972 0.01310794 0.3708035
#> 2 Peri-urban 248 0.9122818 0.01520470 0.4041246
#> 3      Rural 132 0.6808195 0.01237854 0.2250719

Solve for precision or budget

Instead of fixing total n, ask for a target CV:

n_alloc(frame, cv = 0.04, alloc = "neyman")
#> Stratum allocation (neyman, 3 strata)
#> field design: n = 27, cv = 0.0384, cost = 27
#> continuous optimum: n = 24.93353, cv = 0.0400, se = 2.1800
#> (deff = 1)
#> design df = 24

For budget allocation, add a cost per sampled unit and use the optimal cost-adjusted rule:

cost_frame <- transform(frame, unit_cost = c(1, 1.5, 2.5))
n_alloc(cost_frame, budget = 900, alloc = "optimal")
#> Stratum allocation (optimal, 3 strata)
#> field design: n = 621, cv = 0.0079, cost = 900
#> continuous optimum: n = 621.7392, cv = 0.0079, se = 0.4290
#> (deff = 1)
#> design df = 618

The three common rules answer different questions:

Rule Relative allocation Use
"proportional" N_h Equal sampling fractions.
"neyman" N_hS_h Minimize variance with equal unit costs.
"optimal" N_hS_h/\sqrt{c_h} Minimize variance with unequal unit costs.

alloc = "power" implements Bankier allocation (Bankier 1988), with weights proportional to the population CV sd / abs(mean) times alloc_measure^alloc_q. The allocation measure defaults to N * abs(mean), and with that default alloc_q = 1 gives Neyman allocation. At alloc_q = 0 the weights are the population CVs whatever the measure. Means must be nonzero. The population CV describes variation among units and differs from the cv precision target, which describes the relative standard error of an estimate.

Supply a positive alloc_measure column to use another stratum measure:

bankier_frame <- transform(frame, alloc_measure = N)
n_alloc(bankier_frame, n = 600, alloc = "power", alloc_q = 0.5)
#> Stratum allocation (power, 3 strata)
#> field design: n = 600, cv = 0.0079, cost = 600
#> continuous optimum: n = 600, cv = 0.0079, se = 0.4308
#> (deff = 1)
#> design df = 597

With a custom measure, alloc_q = 1 need not give Neyman allocation. Stratum-specific response rates and design effects multiply the weights by sqrt(deff / resp_rate). Bankier allocation does not generally equalize domain CVs. Use explicit domain targets when comparable precision is required.

Add operational constraints

Minimum stratum sizes, maximum planned weights, and census strata can be declared with min_n_stratum, max_weight, and take_all:

constrained_frame <- transform(
  frame,
  unit_cost = c(1, 1.5, 1),
  max_weight = c(25, 20, NA),
  take_all = c(FALSE, FALSE, TRUE)
)

constrained <- n_alloc(
  constrained_frame,
  budget = 3500,
  alloc = "optimal",
  min_n_stratum = 40
)
constrained
#> Stratum allocation (optimal, 3 strata)
#> field design: n = 3403, cv = 0.0076, cost = 3500
#> continuous optimum: n = 3403.425, cv = 0.0076, se = 0.4125
#> (min_n_stratum = 40, deff = 1)
#> design df = 401

The constraints change the optimum, so assess the returned operational design as a unit. Independently rounding continuous stratum values can violate a budget or precision requirement.

Fieldwork assumptions by stratum

deff and resp_rate can be scalar values or frame columns. Stratum-specific response rates change both the number issued and the best allocation:

response_frame <- transform(
  frame,
  resp_rate = c(0.92, 0.80, 0.65)
)

response_plan <- n_alloc(response_frame, n = 600, alloc = "neyman")
response_plan$detail[, c("stratum", "n", "n_int")]
#>      stratum        n n_int
#> 1      Urban 205.4620   205
#> 2 Peri-urban 247.8752   248
#> 3      Rural 146.6628   147

The response adjustment sets an expected responding count. It does not remove nonresponse bias. Use two-phase planning when a subsample of nonrespondents will receive additional follow-up.

Require precision for domains

When each domain is a union of complete strata, domains requests a target for every domain:

domain_frame <- data.frame(
  province = c("North", "North", "South", "South"),
  stratum = c("Urban", "Rural", "Urban", "Rural"),
  N = c(2000, 3000, 1800, 3200),
  sd = c(12, 18, 10, 16),
  mean = c(55, 48, 58, 50)
)

domain_plan <- n_alloc(
  domain_frame,
  domains = "province",
  cv = 0.04,
  alloc = "power",
  alloc_q = 0.3
)
domain_plan
#> Stratum allocation (power, 4 strata)
#> field design: n = 110, cv = 0.0271, cost = 110
#> continuous optimum: n = 108.2371, cv = 0.0273, se = 1.4160
#> (deff = 1)
#> design df = 106
#> Domains: 2
#> 
#>  province .domain .n       .se     .moe     .rmoe      .cv    .cost
#>  North    5_North 58.67031 2.03200 3.982647 0.07839856 0.0400 59   
#>  South    5_South 49.56679 1.97265 3.866324 0.07311505 0.0373 50
domain_plan$domains
#>   province .domain       .n     .se     .moe      .rmoe        .cv    .cost
#> 1    North 5_North 58.67031 2.03200 3.982647 0.07839856 0.04000000 58.67031
#> 2    South 5_South 49.56679 1.97265 3.866324 0.07311505 0.03730428 49.56679

These domains are known on the frame. For a domain that appears naturally within one sample and is not identified on the frame, size the sample from the domain’s expected share, as described in Multiple indicators and domains. That calculation applies to single-stage indicator tables only, because a population share alone does not describe how domain members spread across clusters.

Construct candidate strata

When a continuous auxiliary variable is available on the frame, strata_bound() constructs candidate boundaries. The objective is the precision of the auxiliary variable itself, so its usefulness for a study variable depends on how closely the two are related.

set.seed(12345)
x <- rlnorm(5000, meanlog = 6, sdlog = 1.2)

boundaries <- strata_bound(
  x,
  n_strata = 4,
  cv = 0.05,
  method = "cumrootf"
)
boundaries
#> Strata boundaries (Dalenius-Hodges, 4 strata)
#> n = 63, cv = 0.0491, allocation: neyman
#> 
#>  stratum  lower upper    N share     sd   mean  n
#>        1 3.8267   400 2473 0.495  105.3  191.4 10
#>        2    400  1200 1647 0.329  222.7  700.6 14
#>        3   1200  3100  672 0.134  519.5 1871.7 14
#>        4   3100 21958  208 0.042 3094.3 5543.6 25

Four methods are available:

Method Character
"cumrootf" Fast Dalenius–Hodges cumulative root frequency rule.
"geo" Fast geometric progression, useful for skewed values.
"lh" LH-inspired coordinate search.
"kozak" Kozak random search, exact by enumeration on small problems.

The last two are heuristics. Their results are candidates rather than certified global optima.

The Kozak search starts from more points than the LH search, so it costs more run time, but more starting points do not guarantee a better partition. At a fixed sample size, compare the CV each method reaches on the frame at hand:

set.seed(2024)
kozak_boundaries <- strata_bound(x, n_strata = 4, n = 300, method = "kozak")
kozak_boundaries
#> Strata boundaries (Kozak random search, 4 strata, converged)
#> n = 300, cv = 0.0195, allocation: neyman
#> 
#>  stratum  lower  upper    N share     sd   mean   n
#>        1 3.8267 490.12 2835 0.567  129.7  223.6  70
#>        2 490.12 1356.2 1406 0.281  236.4  816.2  63
#>        3 1356.2 3571.6  596 0.119  579.7 2101.8  66
#>        4 3571.6  21958  163 0.033 3234.5 6160.1 101

method_cv <- c(
  sapply(c("cumrootf", "geo", "lh"), function(method) {
    strata_bound(x, n_strata = 4, n = 300, method = method)$cv
  }),
  kozak = kozak_boundaries$cv
)
round(method_cv, 4)
#> cumrootf      geo       lh    kozak 
#>   0.0199   0.0320   0.0195   0.0195

On this variable the LH and Kozak searches reach the same smallest CV, the cumulative root frequency rule comes close, and the geometric rule falls well behind.

The search allocates each candidate partition by Neyman allocation unless alloc says otherwise. With alloc = "power", it uses the Bankier rule (Bankier 1988)

n_h \propto \frac{S_h}{|\bar{X}_h|}\left(N_h|\bar{X}_h|\right)^q,

where S_h and \bar{X}_h are the standard deviation and mean of x in stratum h, N_h is the stratum population, and q is alloc_q. The population CV S_h/|\bar{X}_h| and the measure N_h|\bar{X}_h| are recomputed for every candidate partition, so the allocation rule shapes the cut points:

strata_bound(x, n_strata = 4, n = 300, alloc = "power", alloc_q = 0.5)
#> Strata boundaries (LH-inspired coordinate search, 4 strata, converged)
#> n = 300, cv = 0.0197, allocation: power (alloc_q = 0.50)
#> 
#>  stratum  lower  upper    N share     sd   mean  n
#>        1 3.8267 510.33 2893 0.579  134.2  229.1 89
#>        2 510.33 1378.7 1361 0.272  236.8  834.9 57
#>        3 1378.7 3571.6  583 0.117  575.6 2118.1 56
#>        4 3571.6  21958  163 0.033 3234.5 6160.1 98

At alloc_q = 1 the rule is Neyman allocation. At alloc_q = 0 the sample is proportional to the stratum population CVs. The rule does not generally equalize stratum CVs. Because it divides by each stratum mean, x should be a positive size measure. To use an allocation measure other than N_h|\bar{X}_h|, construct the strata first and then add an alloc_measure column to the frame passed to n_alloc().

predict() applies the learned boundaries to new observations:

predict(
  boundaries,
  newdata = c(100, 500, 1500, 5000),
  labels = paste0("S", 1:4)
)
#> [1] S1 S2 S3 S4
#> Levels: S1 S2 S3 S4

The strata table connects directly to n_alloc():

allocated_boundaries <- n_alloc(boundaries$strata, cv = 0.05)
allocated_boundaries
#> Stratum allocation (neyman, 4 strata)
#> field design: n = 63, cv = 0.0491, cost = 63
#> continuous optimum: n = 60.95014, cv = 0.0500, se = 40.3814
#> (deff = 1)
#> design df = 59

Both functions use the same variance calculation. Design assumptions carry through both, passed directly or through a svyplan() profile:

plan <- svyplan(deff = 1.8, resp_rate = 0.85)
planned_boundaries <- strata_bound(x, n_strata = 4, cv = 0.05, plan = plan)
c(
  strata_bound = planned_boundaries$n,
  n_alloc = n_alloc(planned_boundaries$strata, cv = 0.05, plan = plan)$n
)
#> strata_bound      n_alloc 
#>     118.0000     116.4124

The difference between the two totals is whole-unit rounding, not a change of model. strata_bound() reports the whole-unit allocation in $strata$n, whereas n_alloc() reports the continuous optimum.

A scalar deff does not move the cut points. At a fixed sample size it multiplies the variance of every candidate partition by the same factor, so the same partition remains best and only the reported CV changes:

rbind(
  deff_1.0 = strata_bound(x, n_strata = 4, n = 300)$boundaries,
  deff_1.8 = strata_bound(x, n_strata = 4, n = 300, deff = 1.8)$boundaries
)
#>              [,1]     [,2]    [,3]
#> deff_1.0 490.1171 1356.216 3571.62
#> deff_1.8 490.1171 1356.216 3571.62

Under a cv target, what deff changes is the n the target requires.

Compare the stratum sampling fractions before deciding whether the proposed boundaries create workable fieldwork loads:

plot(boundaries)
Bar chart of the sampling fraction in each of four constructed strata. A dashed horizontal line marks the overall sampling fraction.

Sampling fractions under the candidate four-stratum design. Compare each stratum with the dashed overall fraction to identify where sampling is most intensive.

More design workflows

Multi-indicator surveys

Multiple indicators and domains explains how indicator requirements combine, including separate quotas and naturally occurring domains.

Joint indicator and domain targets

Joint allocation shows how to satisfy several indicator and overlapping domain requirements with one allocation.

Stratified two-stage designs

The stratified-cluster workflow estimates components within strata, constructs the n_alloc() frame, and checks the whole-unit design and PSU availability bounds.

Two-phase designs

Two-phase planning covers screening, phase-specific response, operational precision, assurance, and nonresponse follow-up.

The allocation formulas and distinctions used here follow standard survey-sampling treatments (Cochran 1977; Valliant, Dever, and Kreuter 2018).

References

Bankier, M. D. 1988. “Power Allocations: Determining Sample Sizes for Subnational Areas.” The American Statistician 42 (3): 174–77. https://doi.org/10.1080/00031305.1988.10475559.
Cochran, William G. 1977. Sampling Techniques. 3rd ed. New York: Wiley.
Valliant, Richard, Jill A. Dever, and Frauke Kreuter. 2018. Practical Tools for Designing and Weighting Survey Samples. 2nd ed. Cham: Springer. https://doi.org/10.1007/978-3-319-93632-1.