svyplan provides a toolkit for survey sample size
determination. It covers sample sizes for proportions and means,
precision analysis, multistage cluster allocation, multi-indicator
optimization, strata boundary optimization, and power analysis. The core
sizing and precision functions use paired S3 interfaces for
round-trip conversions. Sensitivity analysis via predict()
is available for supported result classes.
Function ecosystem
The package exports 32 functions organized into six families. The diagram below shows how they relate to each other.

| Family | Functions | Purpose |
|---|---|---|
| Plan | svyplan |
Reusable profile for design defaults |
| Sample size |
n_prop, n_mean,
n_ratio, n_cluster, n_multi,
n_alloc, n_twophase, n_change,
n_panel, n_pooled
|
“How many units do I need?” |
| Precision |
prec_prop, prec_mean,
prec_ratio, prec_cluster,
prec_multi, prec_alloc,
prec_twophase, prec_change,
prec_panel, prec_pooled
|
“What precision does n achieve?” |
| Power |
power_prop, power_mean,
power_did
|
“Can I detect a difference?” |
| Design |
varcomp, design_effect,
effective_n, design_df,
design_rotation, design_overlap,
design_schedule
|
Variance components, design effects, degrees of freedom, and rotation patterns |
| Strata | strata_bound |
Optimal stratification boundaries |
Which function
The same functions read by problem rather than by family. Eleven
problems, each with the function or pair that answers it. Every
n_ has a prec_ reading the same design back
the other way, so a row names one thing to learn rather than two.
| I want to size, or evaluate | Functions |
|---|---|
| a proportion, a mean or a ratio of two totals |
n_prop()/prec_prop(),
n_mean()/prec_mean(),
n_ratio()/prec_ratio()
|
| several indicators at once, taking the most demanding |
n_multi()/prec_multi()
|
| a two- or three-stage cluster design, for one indicator or a table of them |
n_cluster()/prec_cluster()
|
| an allocation across strata or domains, under a budget or a CV target |
n_alloc()/prec_alloc(),
strata_bound()
|
| a two-phase design that screens or follows up |
n_twophase()/prec_twophase()
|
| a change between two occasions of a repeated survey |
n_change()/prec_change()
|
| the average of several occasions of a repeated survey |
n_pooled()/prec_pooled()
|
| a panel that must still deliver a sample after attrition |
n_panel()/prec_panel()
|
| the overlap and field schedule a rotation pattern produces |
design_rotation(),
design_overlap(), design_schedule()
|
| the power of a two-group comparison or a difference-in-differences |
power_prop(), power_mean(),
power_did()
|
| the design effect, effective size or degrees of freedom of a plan |
design_effect(),
effective_n(), design_df(),
varcomp()
|
Most planning functions return structured S3 objects. Available methods depend on the result class. See the relevant help page for details.
The n_*/prec_* duality
The most distinctive feature of svyplan is the bidirectional link
between sample size and precision functions. Every n_*
function has a prec_* counterpart, and each pair supports
round-trip conversion. Under the same formula and continuous design
assumptions, converting a result to its counterpart and back reproduces
the original target. Changing methods or using an operational integer
allocation can change the result.

Because n_* and prec_* are S3 generics, you
can pass a result object directly to its inverse. All design parameters
travel with the object:
# Compute sample size for a proportion
s <- n_prop(p = 0.3, moe = 0.05, deff = 1.5)
s
#> Sample size for proportion (wald)
#> n = 485 (p = 0.30, moe = 0.050, deff = 1.50)
#> expected cases = 145.2
# What precision does this n achieve?
p <- prec_prop(s)
p
#> Sampling precision for proportion (wald)
#> n = 485
#> se = 0.0255, moe = 0.0500, cv = 0.0850, rmoe = 0.1667
#> expected cases = 145.2
# Recover the original sample size
n_prop(p)
#> Sample size for proportion (wald)
#> n = 485 (p = 0.30, moe = 0.050, deff = 1.50)
#> expected cases = 145.2You can also override a parameter on the way back:
n_prop(p, cv = 0.08)
#> Sample size for proportion (wald)
#> n = 547 (p = 0.30, cv = 0.080, deff = 1.50)
#> expected cases = 164.1Sample sizes for proportions and means
Proportions
Estimate vaccination coverage (expected ~70%) with a 5 percentage point margin of error:
n_prop(p = 0.70, moe = 0.05)
#> Sample size for proportion (wald)
#> n = 323 (p = 0.70, moe = 0.050, deff = 1)
#> expected cases = 225.9Four method options control how the margin of error is computed:
-
"wald"(default): the textbook normal approximation -
"wilson": Wilson score interval, better coverage near 0 or 1 -
"logodds": log-odds transformation. Its back-transformed limits bound the proportion, or equivalently its odds. They do not bound an odds ratio or size a logistic regression. -
"beta": Korn-Graubard interval, for a proportion near a boundary where the expected count in the nearer category is small. It stays inside [0, 1] by construction, and adfargument widens it for a variance estimated from few clusters.
n_prop(p = 0.05, moe = 0.02, method = "wald")
#> Sample size for proportion (wald)
#> n = 457 (p = 0.05, moe = 0.020, deff = 1)
#> expected cases = 22.8
n_prop(p = 0.05, moe = 0.02, method = "wilson")
#> Sample size for proportion (wilson)
#> n = 469 (p = 0.05, moe = 0.020, deff = 1)
#> expected cases = 23.4
n_prop(p = 0.05, moe = 0.02, method = "logodds")
#> Sample size for proportion (logodds)
#> n = 475 (p = 0.05, moe = 0.020, deff = 1)
#> expected cases = 23.7
n_prop(p = 0.05, moe = 0.02, method = "beta", df = 25)
#> Sample size for proportion (beta)
#> n = 558 (p = 0.05, moe = 0.020, deff = 1)
#> expected cases = 27.9The four differ materially only when the expected count is small. For
sizing that difference is rarely worth acting on, but for assessing an
achieved design it often is. ?n_prop has the full
comparison.
Target a coefficient of variation instead of a margin of error:
n_prop(p = 0.70, cv = 0.05)
#> Sample size for proportion (wald)
#> n = 172 (p = 0.70, cv = 0.050, deff = 1)
#> expected cases = 120.0Or state the margin of error as a fraction of the proportion rather
than in percentage points. rmoe = 0.07 at
p = 0.70 asks for the same interval as
moe = 0.049, and says it in the units UNICEF’s Multiple
Indicator Cluster Surveys (MICS) state a requirement in:
n_prop(p = 0.70, rmoe = 0.07)
#> Sample size for proportion (wald)
#> n = 336 (p = 0.70, rmoe = 0.070, deff = 1)
#> expected cases = 235.2The three are targets on one design, not three designs.
moe and rmoe fix the interval the chosen
method builds, cv fixes the sampling variance
underneath it, and every result reports all of them back whichever one
you supplied.
Means
Estimate average household expenditure (variance = 40,000, mean = 800) with a margin of error of 30:
n_mean(var = 40000, moe = 30)
#> Sample size for mean
#> n = 171 (var = 40000.00, moe = 30.000, deff = 1)Common parameters
Most single-stage n_*() and prec_*()
functions share these parameters:
| Parameter | Meaning |
|---|---|
deff |
Design effect multiplier (default 1) |
N |
Finite population size (enables FPC correction) |
alpha |
Significance level (default 0.05) |
resp_rate |
Expected response rate (inflates n by
1/resp_rate) |
Dividing by resp_rate sets an expected respondent count
and evaluates variance at that net size. It assumes response is
ignorable under the planned adjustment. Inflation does not remove
nonresponse bias or include variation from response weights. Use
sensitivity analysis over plausible rates, and see the allocation
article for explicit nonresponse follow-up.
n_prop(p = 0.70, moe = 0.05, deff = 1.5, N = 50000, resp_rate = 0.85)
#> Sample size for proportion (wald)
#> n = 564 gross (net: 480) (p = 0.70, moe = 0.050, deff = 1.50, resp_rate = 0.85)
#> expected cases = 335.6The design effect can be less than 1 for efficient designs
(e.g. well-stratified or PPS samples). svyplan accepts any
deff > 0.
n_cluster() / prec_cluster() use
cluster-specific inputs instead (stage_cost,
icc, unit_relvar, and optionally
resp_rate_psu), and n_alloc() /
prec_alloc() have their own allocation-oriented
interfaces.
Survey plan profiles
When the same deff, N,
resp_rate, or alpha apply across many
single-stage calls, bundle them into a svyplan() profile to
avoid repetition:
plan <- svyplan(deff = 1.5, resp_rate = 0.85, N = 50000)
# Pass as a named argument
n_prop(p = 0.3, moe = 0.05, plan = plan)
#> Sample size for proportion (wald)
#> n = 564 gross (net: 480) (p = 0.30, moe = 0.050, deff = 1.50, resp_rate = 0.85)
#> expected cases = 143.8
n_mean(var = 100, moe = 2, plan = plan)
#> Sample size for mean
#> n = 169 gross (net: 144) (var = 100.00, moe = 2.000, deff = 1.50, resp_rate = 0.85)
power_prop(p1 = 0.30, p2 = 0.35, plan = plan)
#> Power analysis for proportions (solved for sample size)
#> n = 2329 (net: 1979, per group), power = 0.800, effect = 0.0500
#> (p1 = 0.300, p2 = 0.350, alpha = 0.05, deff = 1.50, resp_rate = 0.85)The profile can also hold cluster context (stage_cost,
icc) for n_cluster():
cl_plan <- svyplan(stage_cost = c(500, 50), icc = 0.05, resp_rate = 0.85)
n_cluster(cv = 0.05, plan = cl_plan)
#> Optimal 2-stage allocation
#> field design: n_psu = 52 | n_per_psu = 14 -> total n = 728 (net: 619)
#> (resp_rate = 0.85)
#> cv = 0.0500, cost = 62400
#> continuous optimum: n_psu = 49.9018 | n_per_psu = 14.9509 (cv = 0.0500, cost = 62255)
#> design df = 51Piping works with both positional and named arguments:
plan |> n_prop(0.3, moe = 0.05)
#> Sample size for proportion (wald)
#> n = 564 gross (net: 480) (p = 0.30, moe = 0.050, deff = 1.50, resp_rate = 0.85)
#> expected cases = 143.8
plan |> n_prop(p = 0.3, moe = 0.05)
#> Sample size for proportion (wald)
#> n = 564 gross (net: 480) (p = 0.30, moe = 0.050, deff = 1.50, resp_rate = 0.85)
#> expected cases = 143.8
plan |> power_mean(200, effect = 5)
#> Power analysis for means (solved for sample size)
#> n = 221 (net: 188, per group), power = 0.800, effect = 5.0000
#> (alpha = 0.05, deff = 1.50, resp_rate = 0.85)Explicit arguments always override plan defaults:
# plan has deff = 1.5, but deff = 2.0 wins here
n_prop(p = 0.3, moe = 0.05, plan = plan, deff = 2.0)
#> Sample size for proportion (wald)
#> n = 750 gross (net: 638) (p = 0.30, moe = 0.050, deff = 2.00, resp_rate = 0.85)
#> expected cases = 191.1Ratios of two totals
Some headline indicators are neither a mean nor a proportion but a
ratio of two totals estimated from the same sample: consumption per
person, yield per hectare, spending per pupil. n_ratio()
sizes for the ratio itself.
Give it the anticipated ratio, the coefficient of variation of each component, and the correlation between them across units:
# Per-capita household consumption: total consumption over total members
n_ratio(r = 420, cv_num = 1.20, cv_den = 0.45, component_cor = 0.65, cv = 0.05)
#> Sample size for ratio (linearization)
#> n = 377 (r = 420, unit_relvar = 0.941, cv = 0.050, deff = 1)The printed unit_relvar is the coefficient the size is
driven by. It is the relative variance of the linearized variable e = y - Rx, and the correlation is what makes
it small: a numerator that moves with its denominator produces a stable
ratio even when both components are individually variable. Moving
component_cor from 0.30 to 0.90, with everything else held,
nearly halves the required sample:
predict(
n_ratio(r = 420, cv_num = 1.20, cv_den = 0.45, component_cor = 0.65, cv = 0.05),
data.frame(component_cor = c(0.30, 0.65, 0.90))
)
#> component_cor n se moe cv rmoe
#> 1 0.30 527.4 21 41.15924 0.05 0.0979982
#> 2 0.65 376.2 21 41.15924 0.05 0.0979982
#> 3 0.90 268.2 21 41.15924 0.05 0.0979982Targets work on the ratio’s own scale. moe = 20 asks for
an interval of plus or minus 20 currency units per person, and
rmoe states the same thing relative to r:
n_ratio(r = 420, cv_num = 1.20, cv_den = 0.45, component_cor = 0.65,
moe = 20, deff = 1.3, resp_rate = 0.85)
#> Sample size for ratio (linearization)
#> n = 2437 gross (net: 2072) (r = 420, unit_relvar = 0.941, moe = 20.000, deff = 1.30, resp_rate = 0.85)prec_ratio() is the inverse, and the pair round-trips
like every other:
size <- n_ratio(r = 420, cv_num = 1.20, cv_den = 0.45,
component_cor = 0.65, cv = 0.05)
prec_ratio(size)
#> Sampling precision for ratio (linearization)
#> n = 377
#> se = 21.0000, moe = 41.1592, cv = 0.0500, rmoe = 0.0980What this is, and is not: the estimand is R
= T_y / T_x, with numerator and denominator observed on the same
units. It is not the mean of the unit ratios y_i / x_i, which is an ordinary mean and
belongs in n_mean(). A rate whose denominator is a
subpopulation count, such as coverage among an eligible group, is
usually easier to plan as a domain proportion with
n_prop(). n_ratio() earns its place when the
denominator is a continuous per-unit quantity, where there is no domain
share to inflate by.
One warning is worth knowing about. When the two coefficients of variation are close and the correlation is near its bound, the ratio is nearly constant and the size collapses toward zero. The result then rests entirely on a correlation almost nobody knows to three decimals, so the function says so:
tryCatch(
n_ratio(r = 420, cv_num = 1.20, cv_den = 1.19, component_cor = 0.9999,
cv = 0.05),
warning = function(w) conditionMessage(w)
)
#> [1] "the ratio's unit relative variance (0.000386) is far below its components': the plan rests on 'component_cor' = 0.9999 being known to that precision. Vary it with predict() before committing to this size"Clustered ratios
For a multistage design, the quantity that matters is the homogeneity
of the linearized variable, not of either component. They can differ by
an order of magnitude in the same population, so substituting the
numerator’s icc is not a conservative approximation but a
different design.
Compute e = y - Rx from pilot data
and pass its components to n_cluster():
set.seed(1)
psu <- rep(1:30, each = 20)
x <- rlnorm(600, 1, 0.4)
y <- 3 * x + rnorm(600, 0, 0.8)
R <- mean(y) / mean(x)
e <- y - R * x
vc <- suppressWarnings(varcomp(e, stage_id = list(psu)))
n_cluster(stage_cost = c(500, 50), icc = vc$icc,
unit_relvar = var(e) / mean(y)^2,
var_ratio = vc$var_ratio, cv = 0.05)
#> Warning: the design selects a single PSU, so no between-PSU variance can be
#> estimated. Raise the budget, the PSU count, or the number of stages
#> Optimal 2-stage allocation
#> field design: n_psu = 1 | n_per_psu = 5 -> total n = 5
#> cv = 0.0471, cost = 750
#> continuous optimum: n_psu = 0.3799752 | n_per_psu = 15.88247 (cv = 0.0500, cost = 492)
#> design df = 0e has mean zero by construction, so
varcomp() warns that its relative variances are infinite.
Its icc and var_ratio do not depend on the
mean and stay valid, which is why they are taken from the object while
unit_relvar is supplied separately. Passing the whole
vc object would carry the infinite value in with it.
Evaluating precision
The prec_*() functions answer the reverse question:
given a fixed sample size, what precision can you achieve?
prec_prop(p = 0.3, n = 400)
#> Sampling precision for proportion (wald)
#> n = 400
#> se = 0.0229, moe = 0.0449, cv = 0.0764, rmoe = 0.1497
#> expected cases = 120.0
prec_mean(var = 40000, n = 300, mu = 800)
#> Sampling precision for mean
#> n = 300
#> se = 11.5470, moe = 22.6317, cv = 0.0144, rmoe = 0.0283Design parameters work identically:
prec_prop(p = 0.3, n = 400, deff = 1.5, resp_rate = 0.85)
#> Sampling precision for proportion (wald)
#> n = 400 (net: 340)
#> se = 0.0304, moe = 0.0597, cv = 0.1015, rmoe = 0.1989
#> expected cases = 102.0Solving for the level
n_prop() and prec_prop() are two readings
of one equation relating the proportion, the sample size, and the
precision. Fixing any two returns the third. Supplying cv
in place of p takes the remaining direction and returns the
smallest proportion the design measures that precisely:
prec_prop(n = 1500, cv = 0.10, N = 2e6)
#> Sampling precision for proportion (wald, solved for p)
#> n = 1500
#> p = 0.06246
#> se = 0.0062, moe = 0.0122, cv = 0.1000, rmoe = 0.1960
#> expected cases = 93.7Because se(p) / p falls as p rises, the
answer is a floor: every larger proportion meets the target, and
everything below it is too rare for this design to report at that
precision. It is the planning form of a publication threshold, and
expected_cases is printed beside it so the precision floor
and the min_cases floor n_prop() applies can
be read together.
predict() traces the floor across designs, varying
cv rather than the proportion it now solves for:
predict(prec_prop(n = 1500, cv = 0.10, N = 2e6),
expand.grid(n = c(500, 1500, 5000)))
#> n p se moe cv rmoe
#> 1 500 0.16663201 0.016663201 0.032659274 0.1 0.1959964
#> 2 1500 0.06245608 0.006245608 0.012241167 0.1 0.1959964
#> 3 5000 0.01955979 0.001955979 0.003833649 0.1 0.1959964prec_mean() takes cv the same way. The
standard error of a mean does not involve the mean, so that inversion is
a division rather than a search, and it returns the magnitude:
prec_mean(var = 25, n = 1500, cv = 0.10)$params$mu
#> [1] 1.290994Supplying rmoe asks the same question of the interval
instead of the variance: the smallest proportion the design can report
at that relative half-width, under the interval method it will actually
use. Unlike the cv solve, this one moves with
method:
prec_prop(n = 1500, rmoe = 0.20, method = "wald", N = 2e6)$params$p
#> [1] 0.06012947
prec_prop(n = 1500, rmoe = 0.20, method = "beta", N = 2e6)$params$p
#> [1] 0.06327848Two properties bound that search, and both are reported rather than
approximated past. The Wilson and Korn-Graubard half-widths do not
shrink to zero as p approaches 1, so their relative margin
of error has a positive floor and a target below it is refused with the
floor named. The back-transformed log-odds half-width turns upward once
the logit spread outgrows logit(p), near
p = 0.999 at n = 1500 but as low as
p = 0.97 at n = 30, so under
"logodds" the search brackets at that turn and returns its
lower root.
Confidence intervals
confint() extracts the expected confidence interval from
sizing and precision results for proportions, means, ratios, changes,
and pooled estimates. Cluster, allocation, power, and multi-indicator
results have no single interval to report and are not supported. A mean
needs mu, since an interval has to be centered
somewhere:
s <- n_prop(p = 0.3, moe = 0.05)
confint(s)
#> 2.5 % 97.5 %
#> 0.25 0.35
pr <- prec_prop(p = 0.3, n = 400)
confint(pr)
#> 2.5 % 97.5 %
#> 0.2550916 0.3449084
confint(pr, level = 0.99)
#> 0.5 % 99.5 %
#> 0.2409803 0.3590197
# a mean carries no center of its own, so supply mu
confint(n_mean(var = 100, moe = 2, mu = 50))
#> 2.5 % 97.5 %
#> 48 52
# a ratio always carries its center r
confint(n_ratio(r = 420, cv_num = 1.20, cv_den = 0.45,
component_cor = 0.65, cv = 0.05))
#> 2.5 % 97.5 %
#> 378.8408 461.1592
# a repeated-survey result, the pooled level across two occasions
confint(prec_pooled(var = 100, n = 300, occasions = 2, mu = 50))
#> 2.5 % 97.5 %
#> 49.19985 50.80015For proportions, Wald endpoints are truncated to [0, 1] when needed. The other methods stay within the parameter space by construction.
Explore planning assumptions
A fitted result keeps its planning assumptions.
predict() evaluates compatible parameter grids while
holding the remaining assumptions, including the interval method,
fixed:
baseline <- n_prop(p = 0.3, moe = 0.05, deff = 1.5)
predict(baseline, data.frame(resp_rate = c(0.7, 0.8, 0.9, 1.0)))
#> resp_rate n se moe cv rmoe
#> 1 0.7 691.4626 0.02551067 0.05 0.08503558 0.1666667
#> 2 0.8 605.0298 0.02551067 0.05 0.08503558 0.1666667
#> 3 0.9 537.8042 0.02551067 0.05 0.08503558 0.1666667
#> 4 1.0 484.0238 0.02551067 0.05 0.08503558 0.1666667Continue with the focused articles
The following detailed articles are available on the package website:
Scope
svyplan answers one question in several forms: how many
units does a planned design need, and what precision does a given design
achieve. Some neighbouring topics are deliberately outside that
boundary. They are listed here so their absence reads as a decision
rather than an oversight.
Totals. There is no n_total(). A total
is a mean rescaled by N, so the CV target is identical and a
margin of error on the total is moe / N on the mean.
?n_mean has a worked section on sizing for a total.
Ratio estimation as a variance-reduction technique.
Using an auxiliary variable to sharpen an estimate of a mean or total is
analysis-stage work, and is not the same thing as planning for a ratio
estimand, which n_ratio() covers. Its planning effect is a
smaller residual variance, so supply it directly: fit the ratio model,
take the residual variance, and pass it as n_mean(var = )
with the mean of the analysis variable. A target stated as a CV then
gives the same size as sizing for the ratio itself, because the two have
the same relative variance.
Regression coefficients. Sizing for a regression
slope is a general statistical problem rather than a survey one, covered
by packages such as pwr. The survey-specific part, the
variance inflation from a complex design, is already the
deff argument. There is no n_reg().
Two-phase designs beyond stratification and
follow-up. n_twophase() allocates a two-phase
sample, phase 1 measuring a variable the frame does not carry and phase
2 measuring the variable of interest on a subsample of the phase-1
units. Double sampling for stratification and nonresponse follow-up are
the same problem in that function, differing only in which strata are
marked take_all. What is not covered is the third
member of the family, double sampling for a regression or ratio
estimator, where the phase-1 variable is continuous rather than a
stratifier. That is a two-phase allocation question and is separate from
n_ratio(), which sizes a single-phase design for a ratio
estimand. Its variance effect is expressible today as a design effect
below 1, since a regression estimator multiplies the variance by 1 - \rho^2, but the cost trade-off between
the phases is not built in.
More than three stages. Two- and three-stage designs are supported. Surveys rarely go deeper, and the cost and variance models for a fourth stage would carry more assumptions than they would earn.
Attrition, and choosing a rotation.
n_panel() is arithmetic on declared rates. It says what
recruitment yields under a response rate and a retention curve, not
where those rates come from. Attrition modelling, attrition weighting,
and any adjustment for informative dropout are analysis-stage work and
outside the boundary. Two consequences are worth stating. A unit lost at
one wave is treated as lost for good, so a panel whose nonrespondents
return later does better than planned here, and planning this way
over-issues. Also, design_rotation() records a rotation
that has already been chosen. It does not solve the inverse problem of
finding one that delivers a target overlap while limiting how many times
a unit may be interviewed.
Targeting nonresponse follow-up. Deciding how
much follow-up to do is answered by n_twophase().
Deciding which cases to pursue is not, and is out of scope:
prioritizing by response propensity, or adapting effort as fieldwork
proceeds, needs paradata from the field that no planning stage has.
PPS selection inside joint allocation.
varcomp() estimates variance components under PPS
first-stage selection, but the joint allocator (n_alloc()
with measures and targets) uses an anticipated
variance model. Feeding it components estimated under PPS is therefore
an approximation, and the more unequal the sizes, the rougher it is. A
two-stage joint allocation can identify certainty PSUs from a register.
It does not support take_all in a fixed-take multistage
frame because a census of PSUs still leaves within-PSU sampling.
Domains not identifiable on the frame. Targets can
be set per domain when domain membership is known before fielding, as it
is for region or urban/rural. For domains realized only after contact,
such as age or sex subgroups, the realized subsample is random and has
to be inflated by hand from the expected domain share. The same line
runs through design_df(): a domain that is a union of whole
strata gets an exact degrees-of-freedom count in $domains,
while one cutting across strata does not, because counting the PSUs that
contain its members needs frame information a plan does not carry.
Converting between listing units and analysis units. Sizing works in the units the precision target refers to. Converting a size in eligible persons to a number of households to list, given a mean household size and an eligibility rate, is arithmetic the user does explicitly. The MICS and DHS section above and the README both show the calculation.