Overview
Before drawing a sample, you need to answer two questions: how many
units to select, and how to divide them across strata. The
svyplan package provides tools for both.
samplyr integrates with svyplan so that
planning results flow directly into draw() and
execute().
The three packages form a pipeline:
- svyplan determines sample sizes, evaluates precision, and computes strata boundaries (planning).
- samplyr specifies and executes the sampling design (selection).
- sondage provides the low-level sampling algorithms (engine).
This vignette uses the ken_enterprises dataset, a
synthetic frame of 17,004 Kenyan establishments with revenue, employee
counts, and export status.
data(ken_enterprises)
glimpse(ken_enterprises)
#> Rows: 17,004
#> Columns: 9
#> $ enterprise_id <chr> "KEN_00001", "KEN_00002", "KEN_00003", "KEN_00004", "…
#> $ county <fct> Kiambu, Kiambu, Kiambu, Kirinyaga, Murang'a, Murang'a…
#> $ region <fct> Central, Central, Central, Central, Central, Central,…
#> $ sector <fct> Chemicals & Chemical Products, Chemicals & Chemical P…
#> $ size_class <fct> Medium, Small, Small, Small, Medium, Small, Small, La…
#> $ employees <int> 43, 15, 6, 17, 44, 13, 9, 179, 42, 10, 8, 11, 2000, 6…
#> $ revenue_millions <dbl> 108.0, 45.7, 4.4, 40.0, 67.3, 25.6, 32.9, 265.5, 55.2…
#> $ year_established <int> 1994, 2014, 2008, 1992, 2016, 1982, 2002, 1982, 2005,…
#> $ exporter <lgl> FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,…Sample Size for a Proportion
Suppose we want to estimate the proportion of exporting firms. For this demonstration, we use the proportion in the synthetic frame as the planning value. We want a margin of error of 3 percentage points at 95% confidence.
p_export <- mean(ken_enterprises$exporter)
round(p_export, 3)
#> [1] 0.137
n_export <- n_prop(p = p_export, moe = 0.03)
n_export
#> Sample size for proportion (wald)
#> n = 504 (p = 0.14, moe = 0.030)The result is a svyplan_n object. You can pass it
directly to draw():
samp <- sampling_design() |>
draw(n = n_export) |>
execute(ken_enterprises, seed = 1)
nrow(samp)
#> [1] 504Sample Size for a Mean
We also want to estimate mean revenue with a margin of error of 30 million KES. We compute the population variance from the frame:
rev_var <- var(ken_enterprises$revenue_millions)
n_rev <- n_mean(var = rev_var, moe = 30)
n_rev
#> Sample size for mean
#> n = 611 (var = 143091.44, moe = 30.000)Multiple Indicators
In practice, a survey measures several things at once.
n_multi() takes the binding constraint across
indicators:
targets <- data.frame(
name = c("exporter_rate", "mean_revenue"),
p = c(p_export, NA),
var = c(NA, rev_var),
moe = c(0.03, 30)
)
n_survey <- n_multi(targets)
n_survey
#> Multi-indicator sample size
#> n = 611 (binding: mean_revenue)
#> ---
#> name .n .cv_target .cv_achieved .binding
#> exporter_rate 504 0.1119441 0.1016724
#> mean_revenue 611 NA NA *The sample size is driven by whichever indicator requires the most
units. Pass it to draw() like any other sample size:
samp <- sampling_design() |>
draw(n = n_survey) |>
execute(ken_enterprises, seed = 2)
nrow(samp)
#> [1] 611Per-domain targets
With domains, n_multi() sizes each
reporting domain separately. The result hands draw() a
per-domain table, so the design needs a matching
stratify_by():
targets_dom <- data.frame(
name = "exporter_rate",
size_class = c("Small", "Medium", "Large"),
p = c(0.10, 0.22, 0.45),
cv = 0.10
)
n_dom <- n_multi(targets_dom, domains = "size_class")
samp_dom <- sampling_design() |>
stratify_by(size_class) |>
draw(n = n_dom) |>
execute(ken_enterprises, seed = 2)
table(samp_dom$size_class)
#>
#> Small Medium Large
#> 900 355 123Power Analysis
If the goal is to detect a difference (for example, comparing
exporter rates between two regions), power_prop() gives the
required per-group sample size:
n_power <- power_prop(p1 = p_export, p2 = p_export + 0.08, power = 0.8)
n_power
#> Power analysis for proportions (solved for sample size)
#> n = 353 (per group), power = 0.800, effect = 0.0800
#> (p1 = 0.137, p2 = 0.217, alpha = 0.05)This also works directly with draw():
samp <- sampling_design() |>
draw(n = n_power) |>
execute(ken_enterprises, seed = 3)
nrow(samp)
#> [1] 353Response Rate Adjustment
All svyplan functions accept a resp_rate parameter. The
sample size is inflated by 1 / resp_rate to account for
expected non-response:
n_prop(p = p_export, moe = 0.03, resp_rate = 0.85)
#> Sample size for proportion (wald)
#> n = 593 (net: 504) (p = 0.14, moe = 0.030, resp_rate = 0.85)The net sample size (after non-response) matches the original target.
This works in n_mean(), n_multi(),
n_cluster(), and power_*() functions as
well.
Precision Analysis
Given a fixed sample size, how precise will the estimates be? The
prec_*() functions are the inverse of
n_*():
prec_prop(p = p_export, n = 600)
#> Sampling precision for proportion (wald)
#> n = 600
#> se = 0.0140, moe = 0.0275, cv = 0.1026
prec_mean(var = rev_var, n = 300, mu = mean(ken_enterprises$revenue_millions))
#> Sampling precision for mean
#> n = 300
#> se = 21.8397, moe = 42.8050, cv = 0.2485Round-trip between size and precision
All n_*() and prec_*() functions are S3
generics. Pass a precision result to n_*() to recover the
sample size, or a sample size result to prec_*() to compute
the achieved precision:
s <- n_prop(p = p_export, moe = 0.03)
p <- prec_prop(s)
p
#> Sampling precision for proportion (wald)
#> n = 504
#> se = 0.0153, moe = 0.0300, cv = 0.1119
# Recover the original n
n_prop(p)
#> Sample size for proportion (wald)
#> n = 504 (p = 0.14, moe = 0.030)You can override a parameter on the way back:
n_prop(p, cv = 0.10)
#> Sample size for proportion (wald)
#> n = 632 (p = 0.14, cv = 0.100)Sensitivity Analysis
How sensitive is a sample size to assumptions about the design effect
or response rate? The predict() method evaluates any
svyplan result at new parameter combinations:
x <- n_prop(p = p_export, moe = 0.03, deff = 1.5)
predict(x, expand.grid(
deff = c(1.0, 1.5, 2.0, 2.5),
resp_rate = c(0.8, 0.9, 1.0)
))
#> deff resp_rate n se moe cv
#> 1 1.0 0.8 629.7685 0.0153064 0.03 0.1119441
#> 2 1.5 0.8 944.6527 0.0153064 0.03 0.1119441
#> 3 2.0 0.8 1259.5370 0.0153064 0.03 0.1119441
#> 4 2.5 0.8 1574.4212 0.0153064 0.03 0.1119441
#> 5 1.0 0.9 559.7942 0.0153064 0.03 0.1119441
#> 6 1.5 0.9 839.6913 0.0153064 0.03 0.1119441
#> 7 2.0 0.9 1119.5884 0.0153064 0.03 0.1119441
#> 8 2.5 0.9 1399.4855 0.0153064 0.03 0.1119441
#> 9 1.0 1.0 503.8148 0.0153064 0.03 0.1119441
#> 10 1.5 1.0 755.7222 0.0153064 0.03 0.1119441
#> 11 2.0 1.0 1007.6296 0.0153064 0.03 0.1119441
#> 12 2.5 1.0 1259.5370 0.0153064 0.03 0.1119441This works for all svyplan object types: sample size, precision, cluster, and power.
Multistage Cluster Planning
For cluster designs, n_cluster() finds the optimal
allocation across stages given costs and the degree of clustering.
Variance components
If a frame with cluster identifiers is available,
varcomp() estimates the between-cluster and within-cluster
variance components:
vc <- varcomp(
revenue_millions ~ county,
data = ken_enterprises
)
vc
#> Variance components (2-stage)
#> varb = 16.7566, varw = 308.5545
#> delta = 0.0515
#> k = 17.5626
#> Unit relvariance = 18.5229
as.data.frame(vc)
#> stages varb varw delta k rel_var
#> 1 2 16.75656 308.5545 0.05150933 17.5626 18.52295The delta value (measure of homogeneity) feeds directly
into n_cluster() and design_effect().
Optimal allocation
# Minimize cost to achieve CV = 0.10
n_cluster(stage_cost = c(500, 50), delta = vc, cv = 0.10)
#> Optimal 2-stage allocation
#> field design: n_psu = 3880 | psu_size = 14 -> total n = 54320
#> cv = 0.1000, cost = 4656000
#> continuous optimum: n_psu = 3949.488 | psu_size = 13.5698 (cv = 0.1000, cost = 4654432)
# Maximize precision within a budget
n_cluster(stage_cost = c(500, 50), delta = vc, budget = 30000)
#> Optimal 2-stage allocation
#> field design: n_psu = 25 | psu_size = 14 -> total n = 350
#> cv = 1.2457, cost = 30000
#> continuous optimum: n_psu = 25.4563 | psu_size = 13.5698 (cv = 1.2456, cost = 30000)The precision-target result is diagnostic: its required number of county PSUs far exceeds the 47 available in this frame, so a 10% CV is infeasible under these assumptions. The budget-constrained result is feasible and is the plan used below.
A cluster plan feeds a two-stage design directly. In a design with
cluster_by(), draw(n = cl) hands stage 1 the
number of PSUs and stage 2 the per-cluster take, using the plan’s
integerized field design (as.integer(cl), which svyplan
re-optimizes to whole units rather than rounding each stage up):
cl <- n_cluster(stage_cost = c(500, 50), delta = vc, budget = 30000)
as.integer(cl)
#> [1] 25 14
samp <- sampling_design() |>
cluster_by(county) |>
draw(n = cl) |>
add_stage() |>
draw(n = cl) |>
execute(ken_enterprises, seed = 5)
length(unique(samp$county))
#> [1] 25
nrow(samp)
#> [1] 350In a flat, unclustered single-stage design the same object
contributes its operational element total
(prod(as.integer(cl))) instead, for sizing an element
sample.
Stratification Boundaries
Enterprise surveys typically stratify by size. Rather than using
predefined size classes, strata_bound() finds optimal
boundaries on a continuous variable. The cumulative square root of
frequency method minimizes the coefficient of variation for a given
number of strata and total sample size:
bounds <- strata_bound(
ken_enterprises$revenue_millions,
n_strata = 4,
method = "cumrootf",
cv = 0.05
)
bounds
#> Strata boundaries (Dalenius-Hodges, 4 strata)
#> Boundaries: 40.0, 165.0, 745.0
#> n = 127, cv = 0.0498
#> Allocation: neyman
#> ---
#> stratum lower upper N share sd n
#> 1 1.1 40 12262 0.721 9.3 16
#> 2 40.0 165 3464 0.204 31.8 16
#> 3 165.0 745 929 0.055 145.9 19
#> 4 745.0 10104 349 0.021 1553.5 76The result includes jointly optimized boundaries and stratum sample
sizes. The predict() method assigns each frame unit to a
stratum:
labels <- paste0("S", 1:4)
ken_enterprises$rev_stratum <- predict(
bounds,
ken_enterprises$revenue_millions,
labels = labels
)
table(ken_enterprises$rev_stratum)
#>
#> S1 S2 S3 S4
#> 12262 3464 929 349Use the stratum allocations from strata_bound() directly
as a named vector for draw(). The
as.data.frame() method exposes the boundary table. This
preserves the joint optimization between boundaries and allocation:
n_alloc <- setNames(ceiling(as.data.frame(bounds)$n), labels)
n_alloc
#> S1 S2 S3 S4
#> 16 16 19 76
samp <- sampling_design() |>
stratify_by(rev_stratum) |>
draw(n = n_alloc) |>
execute(ken_enterprises, seed = 4)
samp |>
count(rev_stratum, name = "n_sampled")
#> # A tibble: 4 × 2
#> rev_stratum n_sampled
#> <fct> <int>
#> 1 S1 16
#> 2 S2 16
#> 3 S3 19
#> 4 S4 76Note that the boundaries and allocation are coupled. The
cumrootf method optimizes them together to achieve the
target CV. Using strata_bound() for boundaries but a
different allocation in samplyr (for example, proportional
instead of Neyman) might break this optimality. If you need a different
allocation method, compute boundaries and allocation separately.
Stratified Allocation
When strata are defined by administrative or domain boundaries rather
than optimal boundaries from strata_bound(), use
n_alloc() to compute the allocation. It supports Neyman,
proportional, optimal (cost-weighted), and power allocation with
optional constraints on minimum stratum size.
First, summarize the frame to get stratum population sizes and variabilities:
alloc_frame <- ken_enterprises |>
summarize(
N = n(),
sd = sd(revenue_millions),
mean = mean(revenue_millions),
.by = sector
) |>
rename(stratum = sector)
alloc_frame
#> # A tibble: 7 × 4
#> stratum N sd mean
#> <fct> <int> <dbl> <dbl>
#> 1 Chemicals & Chemical Products 282 878. 273.
#> 2 Construction 2343 315. 63.4
#> 3 Food 856 698. 228.
#> 4 Hotels and Restaurants 3065 190. 48.9
#> 5 Other Manufacturing 2113 537. 177.
#> 6 Other Services 6187 246. 58.2
#> 7 Retail 2158 440. 87.8Then compute the allocation. Here we use Neyman allocation targeting 600 units with a minimum of 20 per stratum:
alloc <- n_alloc(alloc_frame, n = 600, alloc = "neyman", min_n = 20)
alloc
#> Stratum allocation (neyman, 7 strata)
#> field design: n = 600, cv = 0.1543, cost = 600
#> continuous optimum: n = 600, cv = 0.1543, se = 13.5612
#> (min_n = 20)
as.data.frame(alloc)[, c("stratum", "N", "n_int", "weight")]
#> stratum N n_int weight
#> 1 Chemicals & Chemical Products 282 26 10.95380
#> 2 Construction 2343 77 30.54684
#> 3 Food 856 62 13.79354
#> 4 Hotels and Restaurants 3065 60 50.63273
#> 5 Other Manufacturing 2113 118 17.91983
#> 6 Other Services 6187 158 39.08919
#> 7 Retail 2158 99 21.84907The result passes directly to draw(). The per-stratum
allocations are matched by stratum name to the values of the
stratification variable:
samp <- sampling_design() |>
stratify_by(sector) |>
draw(n = alloc) |>
execute(ken_enterprises, seed = 10)
samp |>
count(sector, name = "n_sampled")
#> # A tibble: 7 × 2
#> sector n_sampled
#> <fct> <int>
#> 1 Food 62
#> 2 Chemicals & Chemical Products 26
#> 3 Other Manufacturing 118
#> 4 Construction 77
#> 5 Retail 99
#> 6 Hotels and Restaurants 60
#> 7 Other Services 158Because n_alloc() already provides per-stratum sizes, do
not combine it with alloc in stratify_by().
The two allocation mechanisms are mutually exclusive: either let
svyplan allocate (via n_alloc()) or let
samplyr allocate (via alloc in
stratify_by()), but not both.
Budget-constrained allocation
When field costs vary across strata, n_alloc() can
optimize under a budget constraint using cost-weighted (optimal)
allocation:
alloc_frame_cost <- alloc_frame |>
mutate(unit_cost = c(120, 200, 150, 130, 80, 100, 90))
budget_alloc <- n_alloc(
alloc_frame_cost,
budget = 50000,
alloc = "optimal",
min_n = 20
)
budget_alloc
#> Stratum allocation (optimal, 7 strata)
#> field design: n = 450, cv = 0.1812, cost = 49960
#> continuous optimum: n = 450.2486, cv = 0.1811, se = 15.9212
#> (min_n = 20)
as.data.frame(budget_alloc)[, c("stratum", "N", "unit_cost", "n_int", "weight")]
#> stratum N unit_cost n_int weight
#> 1 Chemicals & Chemical Products 282 120 20 14.10000
#> 2 Construction 2343 200 42 55.44653
#> 3 Food 856 150 40 21.68276
#> 4 Hotels and Restaurants 3065 130 41 74.09623
#> 5 Other Manufacturing 2113 80 103 20.57178
#> 6 Other Services 6187 100 123 50.17066
#> 7 Retail 2158 90 81 26.60402
samp_budget <- sampling_design() |>
stratify_by(sector) |>
draw(n = budget_alloc) |>
execute(ken_enterprises, seed = 11)
samp_budget |>
count(sector, name = "n_sampled")
#> # A tibble: 7 × 2
#> sector n_sampled
#> <fct> <int>
#> 1 Food 40
#> 2 Chemicals & Chemical Products 20
#> 3 Other Manufacturing 103
#> 4 Construction 42
#> 5 Retail 81
#> 6 Hotels and Restaurants 41
#> 7 Other Services 123Stratified Two-Stage Designs
The workhorse of household surveys stratifies by region, samples
enumeration areas within each region, then households within each EA.
n_alloc() plans this design when the frame carries a
delta_psu column. The whole plan then drives both stages of
the samplyr design.
Build the planning frame from the EA frame’s own aggregates. The
homogeneity delta_psu typically comes from a previous
survey round (via varcomp() with its strata
argument). Here we use a flat 0.05:
frame_bfa <- bfa_eas |>
summarise(
N = sum(households),
cost_psu = round(mean(fieldwork_cost)),
.by = region
) |>
left_join(bfa_eas_variance, by = "region") |>
mutate(
stratum = region,
sd = sqrt(var),
delta_psu = 0.05,
cost_ssu = 15
)
plan_2stage <- n_alloc(
frame_bfa[, c("stratum", "N", "sd", "delta_psu", "cost_psu", "cost_ssu")],
n = 6000,
alloc = "neyman"
)
head(as.data.frame(plan_2stage)[, c("stratum", "n_int", "n_psu_int", "psu_size_int")])
#> stratum n_int n_psu_int psu_size_int
#> 1 Boucle du Mouhoun 558 31 18
#> 2 Cascades 221 13 17
#> 3 Centre 835 167 5
#> 4 Centre-Est 587 587 1
#> 5 Centre-Nord 527 31 17
#> 6 Centre-Ouest 580 58 10Each region gets a whole-unit cluster take
(psu_size_int) and a number of EAs
(n_psu_int), integerized together so their product stays on
target. Execute stage 1 against the EA frame, then stage 2 within each
EA. Note the cluster_by(ea_id) even though rows already are
EAs: it declares the cluster structure the plan assumes.
design_2stage <- sampling_design() |>
stratify_by(region) |>
cluster_by(ea_id) |>
draw(n = plan_2stage) |>
add_stage() |>
stratify_by(region) |>
draw(n = plan_2stage)
eas_selected <- execute(design_2stage, bfa_eas, stages = 1, seed = 7)
nrow(eas_selected)
#> [1] 1802Stage 2 runs later as a continuation, once households have been
listed in the selected EAs (the listing must carry the
region column):
eas_selected |>
execute(household_listing, seed = 8)Two conventions to keep in mind. First, the meaning of n
shifts across stages: at stage 1 a value is the stratum total
of PSUs, while at stage 2 it is the take per PSU. Second,
per-stratum values at stage 2 need the explicit
stratify_by(region) shown above. Strata do not carry over
from stage 1, and the second stratify_by() only routes the
takes (every EA lies in one region), so weights are unaffected.
Design Effects After Sampling
After executing a design, you can evaluate its efficiency using
design_effect() and effective_n(). These are
re-exported from svyplan and work directly on
tbl_sample objects.
Kish Design Effect
The Kish design effect measures the loss of precision due to unequal weights. It equals 1 for self-weighting designs:
design_effect(samp)
#> Design effect (Kish)
#> overall = 1.1826
effective_n(samp)
#> [1] 507.3536The effective sample size is the number of observations an SRS would need to achieve the same precision.
Spencer Method
The Spencer method accounts for the relationship between the outcome
variable and the selection probabilities. Pass the outcome as a bare
column name. The overall selection probabilities are extracted
automatically from .weight:
design_effect(samp, y = revenue_millions, method = "spencer")
#> Design effect (Spencer)
#> overall = 0.8048Henry Method
The Henry method also accounts for a calibration covariate. Both
y and x_cal are column names in the
sample:
design_effect(samp, y = revenue_millions, x_cal = employees,
method = "henry")
#> Design effect (Henry)
#> overall = 0.3553Chen-Rust Decomposition
For stratified or clustered designs, the Chen-Rust (CR) method
decomposes the design effect into weighting (deff_w),
clustering (deff_c), and stratification
(deff_s) components:
cr <- design_effect(samp, y = revenue_millions, method = "cr")
as.data.frame(cr)
#> method stratum n_h cv2_w deff_w deff_s overall
#> 1 cr Food 62 0 1 0.005694302 0.8029937
#> 2 cr Chemicals & Chemical Products 26 0 1 0.020842530 0.8029937
#> 3 cr Other Manufacturing 118 0 1 0.162686244 0.8029937
#> 4 cr Construction 77 0 1 0.006871757 0.8029937
#> 5 cr Retail 99 0 1 0.373122266 0.8029937
#> 6 cr Hotels and Restaurants 60 0 1 0.004257742 0.8029937
#> 7 cr Other Services 158 0 1 0.229518818 0.8029937
as.double(cr)
#> [1] 0.8029937The tbl_sample method extracts stratification and
clustering variables from the stored design metadata. You only need to
supply the outcome.
Cluster Planning
Before collecting data, you can anticipate the design effect for a
cluster design using the survey-planning homogeneity measure
delta and cluster size. This delta is not a
generic mixed-model ICC. The calculation uses
svyplan::design_effect() directly, not a
tbl_sample method:
design_effect(delta = 0.05, psu_size = 10)
#> Design effect (Cluster)
#> overall = 1.4500
design_effect(delta = 0.15, psu_size = 10)
#> Design effect (Cluster)
#> overall = 2.3500With a delta of 0.05 and 10 units per cluster, the
design effect is 1.45. A delta of 0.15 raises it to 2.35.
This helps decide the number of clusters and units per cluster during
the planning stage.
Planning the Next Wave
An executed wave records a frame digest: the population counts and
the realized allocation the selection resolved.
frame_summary() returns them as tibbles, and its pool
detail is a valid frame input for n_alloc().
The next wave can therefore be planned from the previous one, without
going back to the sampling frame.
wave1 <- sampling_design(title = "Wave 1") |>
stratify_by(region, alloc = "proportional") |>
draw(n = 300) |>
execute(ken_enterprises, seed = 71)
pools <- frame_summary(wave1, detail = "pool")
pools
#> # A tibble: 6 × 13
#> stage pool_id replicate parent_unit N n_target n_expected n_realized scope
#> <int> <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <chr>
#> 1 1 1 1 NA 1170 21 21 21 univ…
#> 2 1 2 1 NA 2331 41 41 41 univ…
#> 3 1 3 1 NA 680 12 12 12 univ…
#> 4 1 4 1 NA 10246 181 181 181 univ…
#> 5 1 5 1 NA 749 13 13 13 univ…
#> 6 1 6 1 NA 1828 32 32 32 univ…
#> # ℹ 4 more variables: chance_status <chr>, chance <dbl>, take_rate <dbl>,
#> # region <fct>The digest deliberately records no variances, costs, or homogeneity
measures. Those are planning assumptions, not sampling facts, so they
stay explicit inputs. With a constant sd the allocation is
proportional to size. Replace it with prior-wave estimates when you have
them.
plan_frame <- pools |>
transmute(stratum = as.character(region), N, sd = 1)
wave2_alloc <- n_alloc(plan_frame, n = 450)
wave2 <- sampling_design(title = "Wave 2") |>
stratify_by(region) |>
draw(n = wave2_alloc) |>
execute(ken_enterprises, seed = 72)
wave2
#> # A tbl_sample: 450 × 15 | Wave 2
#> # Sampling: 1 stage | 450/17,004 units
#> # Weights: 37.79 [37.45, 38.08]
#> enterprise_id county region sector size_class employees revenue_millions
#> * <chr> <fct> <fct> <fct> <fct> <int> <dbl>
#> 1 KEN_00557 Nyandarua Central Hotels… Small 7 2.6
#> 2 KEN_00525 Nyandarua Central Hotels… Medium 90 64.9
#> 3 KEN_00131 Nyandarua Central Constr… Small 7 13.3
#> 4 KEN_00901 Nyandarua Central Other … Medium 48 62.2
#> 5 KEN_00922 Nyandarua Central Other … Small 17 13.8
#> 6 KEN_00388 Kiambu Central Hotels… Medium 51 117.
#> 7 KEN_00627 Kiambu Central Other … Small 7 6.6
#> 8 KEN_00012 Nyeri Central Chemic… Small 11 31.4
#> 9 KEN_00893 Murang'a Central Other … Small 10 18.8
#> 10 KEN_00997 Nyeri Central Other … Small 15 19.6
#> # ℹ 440 more rows
#> # ℹ 8 more variables: year_established <int>, exporter <lgl>,
#> # rev_stratum <fct>, .weight <dbl>, .sample_id <int>, .stage <int>,
#> # .weight_1 <dbl>, .fpc_1 <dbl>A design file written from an executed sample carries the digest inside its execution receipt, so the same loop works from the file alone. The confidential frame never has to travel with it:
path <- tempfile(fileext = ".json")
write_design(wave1, path, frame = ken_enterprises)
restored <- read_design(path)
frame_summary(restored)
#> # A tibble: 1 × 12
#> stage unit_level scope chance_kind probabilities storage n_pools N
#> <int> <chr> <chr> <chr> <chr> <chr> <int> <dbl>
#> 1 1 element universe inclusion_proba… exact consta… 6 17004
#> # ℹ 4 more variables: n_target <dbl>, n_expected <dbl>, n_realized <dbl>,
#> # take_rate <dbl>Summary
The typical planning workflow can be:
- Determine sample size with
n_prop(),n_mean(),n_multi(), orpower_prop()/power_mean(). - Evaluate precision at the chosen n with
prec_prop(),prec_mean(). - Run sensitivity analysis with
predict()to check robustness. - Optionally create stratification boundaries with
strata_bound()and assign strata withpredict(). - Allocate across strata with
n_alloc()(Neyman, proportional, optimal, or power allocation with constraints). - For cluster designs, estimate variance components with
varcomp()and optimize the allocation withn_cluster(). - Pass the sample size or allocation to
draw()and execute the design. - Evaluate the realized design with
design_effect()andeffective_n().
All svyplan sample size objects work directly with
draw(), and the handoff is stage-aware.
n_alloc() results become per-stratum allocations. Their
stratified two-stage variant feeds PSU counts to a clustered stage 1 and
per-cluster takes to stage 2. n_cluster() results
contribute the matching stage value in a clustered design (and their
operational total in a flat single-stage one). n_multi()
domain plans become per-domain tables for a design stratified on the
domain variables. Scalar objects (n_prop(),
n_mean(), power_*()) contribute their
operational total. The stable tabular form of any plan is available via
as.data.frame() when you need the numbers yourself.