Skip to contents

Distribute a total sample size across strata defined by a single stratification variable, under a fixed total \(n\), target CV, or budget. When the design uses multiple stratification variables (e.g. region and urbanicity), cross them into a single variable beforehand so that each row of frame represents one unique stratum.

Usage

n_alloc(frame, ...)

# Default S3 method
n_alloc(
  frame,
  ...,
  domains = NULL,
  n = NULL,
  cv = NULL,
  budget = NULL,
  measures = NULL,
  targets = NULL,
  objective = NULL,
  alloc = c("neyman", "optimal", "proportional", "power"),
  unit_cost = NULL,
  alpha = 0.05,
  deff = 1,
  resp_rate = 1,
  df = NULL,
  min_n_stratum = NULL,
  alloc_q = 0.5,
  plan = NULL
)

# S3 method for class 'svyplan_prec'
n_alloc(frame, ..., n = NULL, cv = NULL, budget = NULL)

Arguments

frame

For the default method: a stratum-level data frame describing the population you want to sample. Each row represents one stratum, a subgroup of the population defined by a stratification variable such as region, age group, or urbanicity. The values in this frame typically come from a census, a population register, or a previous survey. Any stratum table with the columns below works, for example the pool summary of an executed samplyr sample (samplyr::frame_summary()), once the measure columns are added.

When a design stratifies by several variables at once (e.g. region \(\times\) urbanicity), cross them into a single variable before calling n_alloc (e.g. with interaction()) so that each row maps to exactly one population cell.

When measures and targets are supplied, frame instead describes atomic allocation strata for joint constrained allocation. It requires unique, non-missing stratum and positive finite N columns. It may contain unit_cost, max_weight, take_all, and complete domain classification columns named by targets$domain. In that mode sd, var, mean and p belong in measures instead. Each row must be homogeneous for every domain classification used by a target. For a fixed-take two-stage design, also supply whole N_psu and n_per_psu, plus positive cost_psu and cost_ssu. A three-stage design additionally requires whole N_ssu and n_per_ssu, and positive cost_tsu. N_psu is the number of PSUs in the stratum. In three-stage mode, N_ssu is the aggregate number of SSUs across all PSUs in the stratum (not the number per PSU). Stage costs replace unit_cost.

Required columns:

N

Number of units (e.g. households, individuals) in each stratum. These are population counts, not sample sizes. Must be positive and finite.

sd or var (legacy mode)

A measure of how spread out the variable of interest is within each stratum. Provide exactly one:

  • sd: the stratum standard deviation (\(\sqrt{\text{variance}}\)), or

  • var: the stratum variance.

Both must be non-negative and finite. When all strata have equal variability (or variability is unknown), a constant column (e.g. sd = 1) yields proportional-to-size allocation.

Optional columns:

stratum

A label identifying each stratum (e.g. "Urban", "Rural"). If omitted, row numbers are used. Must be unique, or unique within each domain when domains is set.

mean or p

The stratum population mean or proportion of the variable of interest. Required when solving for cv, because the coefficient of variation is defined relative to the mean. Use mean for continuous variables and p (in \([0, 1]\)) for binary (yes/no) variables.

unit_cost

Per-unit interviewing cost in each stratum (positive, finite). Set higher values for strata that are more expensive to reach. Defaults to 1 everywhere (equal cost).

max_weight

Maximum allowed sampling weight \(N_h / n_h\). Caps how under-represented a stratum can be. Use NA for strata without a cap. It bounds the gross weight, the units drawn, not the response-adjusted \(N_h/(n_hr_h)\), which differs once resp_rate varies by stratum.

take_all

Logical (or 0/1). If TRUE, every unit in the stratum is included, a census stratum. Useful for small strata whose total population is tiny enough to enumerate.

deff, resp_rate

Per-stratum design effect and expected response rate, for designs whose fieldwork differs across strata. NA in either column falls back to the scalar argument. The deff and resp_rate arguments override these columns when given as vectors.

For svyplan_prec objects: a precision result from prec_alloc().

...

Additional arguments passed to methods. Unused arguments are rejected.

domains

Character vector of column names in frame to treat as domain identifiers, or NULL (default) for no domains. All names must exist in frame. Domains define sub-populations that each contain one or more strata. When cv is the target, precision is enforced within every domain (see Details).

n

Total sample size. Specify exactly one of n, cv, or budget.

cv

Target coefficient of variation (relative standard error). For example, cv = 0.05 means the standard error of the estimated population mean or total should be at most 5 percent of the estimate. Requires mean or p in frame. When domain columns are present, this target is enforced in each domain. Specify exactly one of n, cv, or budget.

budget

Total field budget. In the classic modes, specify exactly one of n, cv, or budget, and budget caps the affordable allocation. In joint constrained allocation it must be accompanied by objective: the allocation minimizes the priority-weighted relative variance of the objective components subject to the budget, every hard target in targets, and the stratum bounds. Supplying budget without objective in joint mode is an error, because the two readings are incompatible.

measures

Optional long data frame for joint constrained allocation, with one row per required stratum and indicator name. Each row must contain either p in [0, 1], or mean and exactly one of non-negative sd or var. Optional row-specific deff and resp_rate values override the scalar arguments, and NA uses the scalar default. In fixed-take multistage mode, icc_psu is required and var_ratio_psu defaults to 1. Three-stage mode also requires icc_ssu and derives var_ratio_ssu as var_ratio_psu * (1 - icc_psu) when it is absent, which is the value the variance decomposition implies. Stage parameters may instead be supplied as stratum defaults in frame, in which case non-missing measure-row values take precedence. Only rows selected by a target and its domain are value-validated. Unused rows are ignored after their non-empty, unique keys and frame-stratum membership are checked. Must be supplied with targets.

targets

Optional long data frame with one precision requirement per row. It requires indicator name and exactly one positive cv, moe, or rmoe. Optional domain and level select a frame domain (.overall and level = NA denote the whole population), alpha overrides the scalar value for MOE, and constraint supplies a stable unique identifier. Must be supplied with measures. Optional when objective and budget are given, which requests the best design a budget can buy with no hard precision requirement.

objective

Optional estimates whose priority-weighted relative variance is minimized among the allocations that meet targets and the budget. Either a character vector of indicator names (overall domain, equal priority) or a data frame with name plus optional domain, level, non-negative priority, and component columns, using the same indicator-domain identifiers as targets. At least one priority must be positive. Priorities need not be normalized. Requires budget and must be supplied with measures.

alloc

Allocation rule: "neyman" (default), "optimal", "proportional", or "power".

unit_cost

Optional scalar or length-nrow(frame) vector of per-stratum unit costs, overriding frame$unit_cost. Not used with fixed-take multistage joint allocation, which requires stage costs.

alpha

Significance level, default 0.05.

deff

Design effect multiplier (> 0). A scalar applies to every stratum. A length-nrow(frame) vector gives one per stratum and overrides a deff column in frame.

resp_rate

Expected response rate, in (0, 1]. Default 1. A scalar applies to every stratum. A length-nrow(frame) vector gives one per stratum and overrides a resp_rate column in frame.

df

Degrees of freedom of the variance estimator the allocation will have, typically sampled PSUs minus strata, and available from design_df(). It switches the quantile used to translate a moe target in and to report moe out; a cv target carries no quantile and is unaffected. NULL (default) applies no adjustment.

min_n_stratum

Optional minimum sample size per stratum.

alloc_q

Bankier power parameter, used only when alloc = "power". Numeric scalar in \([0, 1]\). At alloc_q = 1 the allocation equals Neyman. At alloc_q = 0 it yields near-equal subnational CVs. Default 0.5.

plan

Optional svyplan() object providing design defaults.

Value

A svyplan_n object with type = "alloc" and a stratum-level allocation table in $detail (also available via as.data.frame()), with columns:

stratum, N, sd, unit_cost

Stratum identifiers and inputs carried over from the frame. In cluster mode unit_cost is the derived effective per-element cost cost_psu / n_per_psu + cost_ssu (1 when no stage costs were given), while sd stays the input stratum SD.

n

Allocated sample size (continuous).

n_int

Integer allocation. In n mode the requested total is preserved (bounded largest-remainder rounding), except under a fixed cluster take, where the take is held and the total moves to the nearest whole multiple of it instead. In cv mode each stratum is rounded up so the integer design meets the target. In budget mode units are added by variance reduction per unit cost so the integer design stays within budget. Always inside the integerized bounds (ceiling(.lower), floor(.upper)). An error is raised when no integer allocation can satisfy them.

weight

Design weight N / n.

n_eff

Effective sample size n * resp_rate / deff.

.lower, .upper

Bounds applied to the stratum (from min_n_stratum, max_weight, take_all, N_psu, or N).

.binding

Whether the allocation sits on one of its bounds.

.bound_source

Which constraint produced the bound the allocation sits on, and NA when it sits on none.

.psu_frac

Cluster mode with N_psu only: the share of the stratum's PSU universe the design selects, n_psu / N_psu.

.se, .moe, .rmoe, .cv

Precision of the stratum's own mean under the allocation, on the same scale as mean (or p). Multiply .se by N for the stratum total. .rmoe is .moe as a fraction of the stratum mean. .cv and .rmoe are NA unless the frame supplies mean or p.

.share

The stratum's share of the design variance of the overall mean. Sums to 1 and identifies where precision is actually being bought.

take_all, mean

Present when take-all strata or stratum means were supplied.

n_per_psu, n_psu

Cluster mode only: the continuous per-stratum take and implied number of PSUs (n / n_per_psu).

n_psu_int, n_per_psu_int

Cluster mode only: the whole-unit field design (whole PSUs and whole takes), chosen so that budget designs stay within budget and cv designs meet the target. Here n_int = n_psu_int * n_per_psu_int.

n_psu, n_psu_int, n_per_psu, n_per_ssu

Joint fixed-take multistage mode: continuous and whole first-stage decisions and the fixed later-stage takes. n_per_ssu is present only for three stages. Public n remains in ultimate-unit units, so n = n_psu * n_per_psu for two stages and n = n_psu * n_per_psu * n_per_ssu for three stages. The same exact identities hold for n_int and n_psu_int.

The result also carries an $operational list describing the integer field design. In joint mode it contains total n, variable field cost, the full target table in constraints, all_pass, and the number of integer-repair additions. In legacy modes it also reports the applicable aggregate se, moe, and cv. Top-level quantities describe the continuous design, while as.integer() returns the operational total.

In fixed-budget objective mode the result adds $objective, one row per component with its priority, relative variance .relvar, equivalent .cv, weighted .contribution, and .share of the total, plus the scalar $objective_value. $operational carries the same two fields for the integer design together with budget_residual. $params$mode is "budget_objective" rather than "targets", and $optimization adds the budget, whether it binds, its residual, the local budget_sensitivity (change in objective value per extra unit of budget), and the epsilon-constraint objective_bound and its multiplier.

Details

Joint constrained allocation

Supplying both measures and targets requests a minimum-cost allocation that meets all indicator-domain precision requirements simultaneously. This mode is mutually exclusive with scalar n, cv, budget, domains, and an explicit alloc rule: the target table defines the constraints and the allocation is solved jointly.

CV targets apply to domain means or totals (the CV is the same on either scale). MOE targets are absolute margins of error for domain means. An rmoe target is a margin of error for the domain mean stated as a fraction of it, which is qnorm(1 - alpha / 2) times the CV of the same estimate; the solver takes it through the CV branch and reports .achieved and .sensitivity back in rmoe units. The variance model uses Wald/linearized variances, the package's gross-sample response-rate convention, design effects, and finite population correction. Domain classifications may overlap. For example, separate region and residence targets can use the same atomic region x residence frame rows.

Fixed-take two- and three-stage joint designs keep only the first-stage PSU count free. Later-stage takes must be positive whole numbers. The canonical optimizer works in PSU units, while public n, min_n_stratum, max_weight, and precision assessments remain in ultimate-unit units. PSU upper bounds use min(N_psu, N / n_per_psu) for two stages and min(N_psu, N_ssu / n_per_psu, N / (n_per_psu * n_per_ssu)) for three stages. Because sampling every PSU does not establish an ultimate-unit census, take_all is not supported in this mode. The response rate applies once to the gross ultimate-unit take. Jointly optimized later-stage takes are not supported.

What the optimum then means is worth stating plainly. The convex problem is solved to optimality for the planning coefficients supplied, and the feasibility and KKT diagnostics report on that problem. It is a planning model rather than an exact design-based variance for every multistage design: the later-stage takes are fixed inputs rather than decision variables, and the stage variances enter through the design-effect and FPC adapter described above. A design that is optimal here is optimal within that family, not across all multistage designs.

The three population columns are all stratum totals, not per-parent counts: N is the number of ultimate units in the stratum, N_psu the number of PSUs in it, and N_ssu the number of SSUs across all of its PSUs. The take columns are the mirror image, counting per parent unit: n_per_psu units (or SSUs) inside one selected PSU and n_per_ssu units inside one selected SSU. The N_ssu / n_per_psu bound above reads correctly only under this convention, since it divides a stratum total by a per-PSU take to bound the number of PSUs. A frame that stores SSUs per PSU instead must be converted first, typically N_ssu = N_psu * ssu_per_psu.

In two-stage mode n_per_psu is the number of ultimate units sampled in a selected PSU, cost_psu is the cost per selected PSU, and cost_ssu is the cost per sampled ultimate unit. The cost of one selected PSU is therefore cost_psu + cost_ssu * n_per_psu. In three-stage mode n_per_psu is the number of SSUs sampled per selected PSU, n_per_ssu is the number of ultimate units sampled per selected SSU, cost_ssu is per sampled SSU, and cost_tsu is per sampled ultimate unit. The corresponding cost is cost_psu + cost_ssu * n_per_psu + cost_tsu * n_per_psu * n_per_ssu.

For indicator \(k\) and stratum \(h\), write \(m_h\) for n_per_psu, \(q_h\) for n_per_ssu, and \(S^2_{hk}\) for var (or sd^2, or p * (1 - p)). The fixed variance multiplier is \(D_{hk}=k_{1,hk}(1+\delta_{1,hk}(m_h-1))\) at two stages and \(D_{hk}=k_{1,hk}\delta_{1,hk}m_hq_h+ k_{2,hk}(1+\delta_{2,hk}(q_h-1))\) at three stages. At three stages var_ratio_ssu is not a free parameter: var_ratio_psu rescales the components' unit variance to the analysis variable and var_ratio_ssu does the same for the within-PSU part, so \(k_{2}=k_{1}(1-\delta_{1})\). That identity is what makes \(D_{hk}\) collapse to \(k_{1}\) at \(m_h=q_h=1\), where no clustering is left to inflate anything. Leaving var_ratio_ssu out of measures applies it. Supplying a value overrides it, which is only meaningful when the two stages' ratios come from different decompositions. With ultimate-unit take \(t_h=m_h\) or \(m_hq_h\), first-stage decision \(a_h\), response rate \(r_{hk}\), and extra design effect \(d_{hk}\), the implemented total-variance contribution is $$N_h^2 S_{hk}^2 D_{hk}d_{hk}/(r_{hk}a_ht_h) - N_h S_{hk}^2 D_{hk}d_{hk}.$$ Thus response inflation is applied once. Leave deff = 1 unless it represents a source not already captured by the stage deltas and var_ratio factors. Deltas may include 0 and 1. The var_ratio values must be positive finite. All stage populations and fixed takes must be positive whole numbers.

The continuous result is a KKT-certified global optimum of the convex, fixed-coefficient problem. $detail$n_int is a deterministic, feasible, locally cleaned operational recommendation. It is not claimed to be the globally optimal integer allocation. Full achieved precision is in $constraints and $operational$constraints. Use prec_alloc() to assess the fitted, operational, or a modified allocation.

A constraint table records .metric, .target, .achieved, .ratio, and .residual = .ratio - 1. A .pass means .ratio <= 1 + .tolerance. .binding identifies a numerically active target. .multiplier is the continuous Lagrange multiplier and .sensitivity is the local derivative of minimum variable cost with respect to the target. Both are NA for a pure precision assessment. Multipliers are also NA when duplicate normalized constraints make an individual split unidentified. The stored feasibility_tolerance and per-row .tolerance are the public numerical acceptance contract.

Fixed budget and a weighted objective

Joint allocation answers two different planning questions, distinguished by whether objective is supplied. Without it, n_alloc() returns the cheapest design meeting every requirement in targets. With objective and budget, it returns the best design that budget can buy: among the allocations that meet every hard target and cost no more than budget, the one minimizing $$Q(n) = \sum_j w_j \, \mathrm{cv}_j^2(n),$$ the priority-weighted sum of the objective components' relative variances. The three roles stay distinct: targets are pass/fail requirements, objective expresses preference among the allocations that pass, and budget is a hard ceiling on variable cost.

InputsMeaning
measures + targetscheapest design meeting every target
measures + objective + budgetbest design the budget can buy
measures + targets + objective + budgetbest design the budget can buy, subject to the targets
measures + objective without budgeterror
measures + targets + budget without objectiveerror

Two consequences of the objective being in squared-CV units are worth stating. Objective rows are always relative-variance, even when the same indicator carries an MOE hard target, so they take no cv or moe column and they need a non-negligible domain total. And priority = c(2, 1) weights variances 2 to 1, not CVs. Scaling every priority by a constant leaves the allocation unchanged, and a zero-priority component does not affect it at all.

The solver appends the objective as one more reciprocal constraint column and root searches the objective bound whose minimum cost equals the budget. The model is convex with fixed coefficients, so this recovers the global continuous optimum with the same KKT certification as the minimum-cost mode. Solving the minimum-cost problem with the added requirement \(Q \le Q^{*}\) returns the same allocation at a cost equal to the budget.

Infeasibility takes three distinct forms, each with its own message: the targets may be unattainable even at the stratum upper bounds. They may be attainable but unaffordable, in which case the cheapest target-feasible cost and the shortfall are reported. Or the continuous problem may be feasible while no whole-unit allocation meets every target inside the budget.

ceiling() is not a safe integer start under a budget, so the operational design is built from several roundings of the continuous optimum, repaired and trimmed to be both target-feasible and affordable, then improved by spending residual budget and by pairwise exchanges. It satisfies $operational$cost <= budget and all($operational$constraints$.pass), and it is a feasible, locally improved recommendation rather than a globally optimal integer allocation. The greedy construction is incomplete: it can report failure on a problem that does have a feasible integer point.

predict() on a fixed-budget result varies budget in newdata and returns the cost-versus-objective frontier, reusing the same solves.

For fieldwork, use $detail$n_int, not the analytical continuous n. Fixed-take count columns are numeric vectors whose values are exact whole numbers. The _int suffix denotes operational integer semantics rather than R's 32-bit integer storage type. Result lists contain diagnostics and are not promised as a long-term serialization format.

Building the frame

The frame is a data frame where each row is one stratum of your target population. It summarizes what you know about each subgroup before sampling. A typical workflow:

  1. Identify strata from a census or register (e.g. provinces, urban/rural areas, age groups).

  2. Look up N: the population count per stratum.

  3. Estimate sd: the standard deviation of your key variable within each stratum (from a pilot survey, a previous census, or expert judgement). If unknown, set sd = 1 everywhere for proportional allocation.

  4. Add mean or p if you want to solve for a target CV.

A minimal frame:

frame <- data.frame(
  stratum = c("Urban", "Rural"),
  N       = c(50000, 120000),
  sd      = c(12, 20)
)

When a design stratifies by several variables (e.g. region \(\times\) urbanicity), cross them into one variable first:

frame$stratum <- interaction(frame$region, frame$urban, drop = TRUE)

This ensures that each row maps to exactly one population cell and that the allocation formulas apply to the correct per-stratum N and sd pairs.

Cluster designs within strata

Adding a icc_psu column to the frame turns the allocation into a stratified two-stage design (e.g. enumeration areas then households within each stratum). Under the cluster variance model the problem reduces to the element allocation above with the stratum SD inflated to sd * sqrt(var_ratio_psu * (1 + icc_psu * (n_per_psu * resp_rate - 1))) and, when stage costs are given, a per-element cost of cost_psu / n_per_psu + cost_ssu. The clustering penalty is paid on the take that responds, n_per_psu * resp_rate, since a unit that does not respond contributes no within-cluster observation; the cost is paid on the gross take, since it is issued either way. The two coincide at resp_rate = 1. The whole-cluster operational search reads the same responding take, so a design it accepts is one the continuous reduction also accepts. All solve modes, allocation methods, and constraints work unchanged. The n, cv, and budget modes keep their meanings.

Cluster-mode columns:

  • icc_psu (required): within-PSU homogeneity per stratum, e.g. from varcomp() with strata.

  • var_ratio_psu (optional, default 1): variance ratio per stratum.

  • n_per_psu (optional): fixes the per-stratum take, in the operational whole-unit design as much as in the continuous one. Any NA entries are replaced by the cost-optimal take sqrt(cost_psu / cost_ssu * (1 - icc_psu) / icc_psu). Holding the take is what n mode gives up its exact total for: the stratum is fielded as a whole number of clusters of that size, so its element count lands on the nearest multiple.

  • cost_psu, cost_ssu (together): per-PSU and per-element costs. These are required for budget mode or when n_per_psu is not fixed. They replace unit_cost, which is not allowed in this mode.

  • N_psu (optional): the number of PSUs available in the stratum.

What N_psu does, and what it does not

N_psu is a feasibility constraint and nothing more. It caps the allocation at N_psu * n_per_psu ultimate units and caps the whole-unit design at N_psu clusters, matching the bound the fixed-take path already applies. Leaving it out preserves the unbounded behaviour, in which the allocation may ask for more PSUs than a stratum contains.

It does not activate a first-stage finite population correction. Precision here uses a with-replacement first stage, so the between-PSU term keeps its full size however large a share of the PSU universe the design takes. Under the equal-take ICC planning model this is conservative: omitting the first-stage correction generally overstates the between-PSU sampling variance once the PSU sampling fraction is appreciable, and print() says so when it is. The claim is tied to that model and is not general to arbitrary PPS or informative cluster designs.

At n_psu == N_psu the model still carries between-PSU variance even though every PSU has been selected. That is deliberately conservative and is no longer a literal variance representation. For the same reason, a target this path reports as unreachable at the PSU bound may be reachable under a finite-population first stage; the error says so rather than claiming the precision is impossible.

Because taking every PSU leaves the within-PSU take in force, it does not enumerate a stratum, and take_all is refused in cluster mode for the same reason the fixed-take path refuses it.

The correction that is applied is the ultimate-unit one, 1 - n / N, consistent with n_cluster()'s variance model.

Because icc_psu already accounts for the clustering, leave deff at 1 unless it captures a different source of design effect (e.g. weighting loss). A clustering deff on top of icc_psu would double-count. The constraints min_n_stratum and max_weight stay in element units. For fielding, use the whole-unit design in n_psu_int and n_per_psu_int (n_int = n_psu_int * n_per_psu_int). Its actual field cost and precision are reported in $operational and, in budget mode, never exceed the budget.

Domains vs. strata

Domains are specified via the domains parameter. Domain columns partition strata into sub-populations. Each domain groups one or more strata. When cv is specified, the algorithm finds the minimum total \(n\) such that the worst-case domain CV meets the target, i.e. every domain achieves the required precision.

In n or budget mode, domains affect reporting only: per-domain precision metrics appear in $domains but the allocation itself treats all strata globally.

Allocation methods

Allocation is controlled by the alloc parameter (same methods as strata_bound()):

  • proportional: \(n_h \propto N_h / r_h\)

  • neyman: \(n_h \propto N_h S_h \sqrt{d_h / r_h}\)

  • optimal: \(n_h \propto N_h S_h \sqrt{d_h / r_h} / \sqrt{c_h}\)

  • power: Bankier (1988), \(n_h \propto S_h N_h^{q}\sqrt{d_h/r_h}\), with exponent alloc_q

The design effect \(d_h\) and response rate \(r_h\) enter only when they vary by stratum. A value shared by every stratum is a constant factor and cancels out of a proportional weighting, leaving the classical rules above it. The three variance-based rules carry \(\sqrt{d_h/r_h}\) because they minimize \(\sum W_h^2S_h^2d_h/(r_hn_h)\) against a constraint on the units drawn, which is what a budget pays for. Proportional carries \(1/r_h\) instead, and no \(d_h\) at all: it is a count rule, not a variance optimum, and its purpose is a self-weighting sample, so it is the responding sample it holds proportional to \(N_h\).

Stratum allocations are rounded to integers using the ORIC method (Cont and Heidari, 2015). Constraints (min_n_stratum, max_weight, take_all) are enforced via recursive Neyman allocation (RNA, Wesolowski et al., 2021).

When budget is specified, the algorithm finds the maximum affordable allocation under unit costs.

References

Valliant, R., Dever, J. A., & Kreuter, F. (2018). Practical Tools for Designing and Weighting Survey Samples (2nd ed.). Springer. Chapters 5 and 9.

Bankier, M. D. (1988). Power allocations: determining sample sizes for subnational areas. The American Statistician, 42(3), 174–177.

Bethel, J. (1989). Sample allocation in multivariate surveys. Survey Methodology, 15(1), 47–57.

Cont, R. and Heidari, M. (2015). Optimal rounding under integer constraints. arXiv preprint arXiv:1501.00014.

Wesolowski, J., Wieczorkowski, R. and Wojciak, W. (2021). Optimality of the recursive Neyman allocation. Journal of Survey Statistics and Methodology, 10(5), 1263–1275.

See also

prec_alloc() for the inverse, strata_bound() for constructing the strata to allocate over, and n_multi() for the unstratified multi-indicator size that joint allocation refines.

Other sample size functions: n_change(), n_cluster(), n_mean(), n_multi(), n_multi_cluster(), n_panel(), n_pooled(), n_prop(), n_twophase()

Examples

frame <- data.frame(
  stratum = c("A", "B", "C"),
  N    = c(4000, 3000, 3000),
  sd   = c(10, 15, 8),
  mean = c(50, 60, 55),
  unit_cost = c(1, 1.5, 1)
)

n_alloc(frame, n = 600)
#> Stratum allocation (neyman, 3 strata)
#> field design: n = 600, cv = 0.0079, cost = 724
#> continuous optimum: n = 600, cv = 0.0079, se = 0.4305
#> (deff = 1)
#> design df = 597
n_alloc(frame, cv = 0.03)
#> Stratum allocation (neyman, 3 strata)
#> field design: n = 46, cv = 0.0294, cost = 56
#> continuous optimum: n = 44.23479, cv = 0.0300, se = 1.6350
#> (deff = 1)
#> design df = 43

frame_constraints <- transform(
  frame,
  max_weight = c(25, 20, NA),
  take_all = c(FALSE, FALSE, TRUE)
)

n_alloc(frame_constraints, budget = 3500, alloc = "optimal", min_n_stratum = 40)
#> 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

frame_domains <- 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)
)

n_alloc(frame_domains, domains = "province",
       cv = 0.04, alloc = "power", alloc_q = 0.3)
#> Stratum allocation (power, 4 strata)
#> field design: n = 112, cv = 0.0270, cost = 112
#> continuous optimum: n = 110.7422, cv = 0.0272, se = 1.4076
#> (deff = 1)
#> design df = 108
#> Domains: 2
#> 
#>  province .domain .n       .se      .moe     .rmoe      .cv    .cost
#>  North    5_North 59.23404 2.032000 3.982647 0.07839856 0.0400 59   
#>  South    5_South 51.50815 1.948447 3.818886 0.07221797 0.0368 52   

# Joint targets for two indicators and an overlapping domain
joint_frame <- data.frame(
  stratum = c("North urban", "North rural", "South urban"),
  region = c("North", "North", "South"),
  N = c(1000, 1800, 1200)
)
joint_measures <- data.frame(
  stratum = rep(joint_frame$stratum, 2),
  name = rep(c("coverage", "income"), each = 3),
  p = c(0.5, 0.4, 0.6, NA, NA, NA),
  mean = c(NA, NA, NA, 50, 55, 60),
  sd = c(NA, NA, NA, 10, 12, 15)
)
joint_targets <- data.frame(
  name = c("coverage", "income"),
  domain = c(".overall", "region"),
  level = c(NA, "North"),
  cv = c(0.06, NA),
  moe = c(NA, 3)
)
joint <- n_alloc(
  joint_frame, measures = joint_measures, targets = joint_targets
)
prec_alloc(joint, n = joint$detail$n_int)
#> Joint allocation precision (2 constraints)
#> targets: all pass

# The best design a fixed budget can buy, subject to the same targets
bought <- n_alloc(
  joint_frame, measures = joint_measures, targets = joint_targets,
  objective = "income", budget = 4000
)
bought$objective
#>         component   name   domain level priority .relvar .cv .contribution
#> 1 income@.overall income .overall  <NA>        1       0   0             0
#>   .share
#> 1     NA

# Unequal priorities, and an objective on a specific domain
priorities <- data.frame(
  name     = c("income", "coverage"),
  domain   = c(".overall", "region"),
  level    = c(NA, "North"),
  priority = c(2, 1)
)
n_alloc(
  joint_frame, measures = joint_measures, targets = joint_targets,
  objective = priorities, budget = 4000
)
#> Joint constrained allocation (Bethel)
#> question: best design affordable within a budget of 4000
#> field design: n = 4000, cost = 4000 (2 targets, all pass)
#> continuous optimum: n = 4000, cost = 4000
#> objective: weighted relative variance 0 continuous, 0 operational
#> the budget is not binding: the allocation sits at its upper bounds
#> 
#>  component             priority .cv .share
#>  income@.overall       2        0   NA    
#>  coverage@region=North 1        0   NA    
#> 
#> active bounds: 0 lower, 3 upper

# What would other budgets buy? (cost-versus-objective frontier)
predict(bought, data.frame(budget = c(3000, 4000, 5000)))
#>   budget    n cost objective_value          cv n_int cost_int .binding
#> 1   3000 3000 3000    3.907646e-06 0.001976777  3000     3000     TRUE
#> 2   4000 4000 4000    0.000000e+00 0.000000000  4000     4000    FALSE
#> 3   5000 4000 4000    0.000000e+00 0.000000000  4000     4000    FALSE
#>   .feasible
#> 1      TRUE
#> 2      TRUE
#> 3      TRUE

# The same target tables with fixed two-stage takes
joint_frame$N_psu <- c(100, 150, 100)
joint_frame$n_per_psu <- c(8, 10, 12)
joint_frame$cost_psu <- c(300, 400, 450)
joint_frame$cost_ssu <- c(25, 30, 35)
joint_measures$icc_psu <- rep(c(0.03, 0.05, 0.08), 2)
joint_measures$var_ratio_psu <- 1
joint_cluster <- n_alloc(
  joint_frame, measures = joint_measures, targets = joint_targets
)
joint_cluster$detail[, c("stratum", "n_psu_int", "n_per_psu", "n_int")]
#>       stratum n_psu_int n_per_psu n_int
#> 1 North urban        11         8    88
#> 2 North rural        17        10   170
#> 3 South urban        11        12   132

# Fixed three-stage takes: PSUs, SSUs within PSU, ultimate units within SSU
joint_frame$N_ssu <- c(600, 1000, 750) # aggregate SSUs by stratum
joint_frame$n_per_ssu <- c(3, 4, 3)
joint_frame$cost_tsu <- c(5, 6, 7)
joint_measures$icc_ssu <- rep(c(0.10, 0.08, 0.12), 2)
joint_measures$var_ratio_ssu <- 1
joint_three <- n_alloc(
  joint_frame, measures = joint_measures, targets = joint_targets,
  min_n_stratum = 20
)
joint_three$detail[, c(
  "stratum", "n_psu_int", "n_per_psu", "n_per_ssu", "n_int"
)]
#>       stratum n_psu_int n_per_psu n_per_ssu n_int
#> 1 North urban         7         8         3   168
#> 2 North rural         9        10         4   360
#> 3 South urban         6        12         3   216
prec_alloc(joint_three, n = joint_three$detail$n_int)
#> Joint allocation precision (2 constraints)
#> targets: all pass

# Stratified two-stage design (EAs then households per stratum)
frame_cluster <- data.frame(
  stratum   = c("Urban", "Rural"),
  N         = c(50000, 150000),
  sd        = c(0.45, 0.48),
  mean      = c(0.35, 0.25),
  icc_psu = c(0.03, 0.08),
  cost_psu  = c(300, 600),
  cost_ssu  = c(40, 60)
)

n_alloc(frame_cluster, cv = 0.05)
#> Stratum allocation (neyman, two-stage, 2 strata)
#> field design: n = 2021, n_psu = 171, cv = 0.0498, cost = 206500
#> continuous optimum: n = 1979.882, cv = 0.0500, se = 0.0138
#> (deff = 1)
#> design df = 169