step_round(method = "balanced") takes formula, balancing on the calibration design
rather than on the crossed cells of by.
step_model_calibration() and step_pseudoweight() take by, fitting and calibrating
one domain at a time.
step_cre() takes n_groups, so the equal-representation target comes from the design
and not from the responding sample, and rescale_previous for a previous wave whose
population differs from the current one.
jackknife_weights() takes groups and seed, for a delete-a-group jackknife, and
max_replicates, which stops a run before it builds more replicates than intended.
ci_type and df on the estimation entry points, for Student t intervals when the
design degrees of freedom are few.
print() on a bootstrap object reports the Monte Carlo error of its standard errors.
A reference_sample() kept its weights in positional order: reordering or subsetting
its rows misaligned them silently. They now travel with the rows.
Warnings and diagnostics across the pseudo-weighting, panel, trimming and calibration
steps now fire where they were silent, stop firing on sound models, and state what a
variance assumes about a reference survey. In particular a non-probability recipe is
no longer accepted in silence when it adjusts nothing, pseudo-weights twice, or is
exported to survey as if it had a sampling design. Report values are escaped in the
HTML.
The non-probability article was rewritten around the multipurpose-calibration argument: what one weight for many variables costs, why a flexible working model is needed, and the rank ceiling on the number of control totals a linear in-sample model can carry. The rotating-panel, longitudinal-panel, composite-estimation and quick-start articles were rebuilt around the same figures.
Vignette figures now ship as standalone .svg files embedded as data URIs. Inline SVG
did not survive the HTML post-processing: re-parsing the rendered page converts attribute
names to lower case, which is correct for HTML but fatal for SVG (viewBox becomes
viewbox), and a figure with a relative width and no explicit height collapsed to zero
height. A data URI is a single attribute value and is copied verbatim.
Rotating and pure panels. panel_design() reads the unit x wave structure and
the rotation calendar -- and, given a cluster, the overlap at the cluster level
too, which is the one the rotation calendar describes (the unit-level figure is
that overlap net of within-household churn); panel_merge() builds the
longitudinal file; panel_pr() and step_panel_overlap() apply the
panel-selection probability; step_attrition() is the panel-facing nonresponse
adjustment and takes the full argument list of step_nonresponse(), crossfit
included; and step_longitudinal() / step_cross_sectional() declare which weight
a recipe is building.
Net change with a coordinated variance. wave_bootstrap() and
wave_jackknife() resample PSUs coordinately across waves, so the sample
overlap shows up as covariance instead of being ignored. change_estimate()
(with change_mean() / change_total(), absolute or relative, optionally by
domain), level_estimate() and panel_estimate(contrast = ) read the estimates
off those replicates. Both engines take lonely_psu as bootstrap_weights() does,
because a stratum with a single PSU contributes no variance to a change either;
the collapse map is built over all the waves at once so the replicate pairing
survives it.
Chained production: wave_step(), wave_carry(), wave_contrast(). One
period at a time, as an office actually publishes: each run leaves a compact
carry that the next one needs, and wave_contrast() estimates any linear
combination over a chain (a rolling quarter, an annual average) from the saved
carries alone.
Composite (regression composite) estimation: step_cre(). The MR1/MR2
estimator of the Canadian LFS and Uruguay's ECH, including the equal-representation
constraints across rotation groups, with Zhat re-estimated inside every
replicate. The step reports the quality of the wave-to-wave link it depends on --
the overlap rate, the births, and how many non-birth units failed to link -- and
warns when that failure rate gets high, because a broken linkage key attenuates the
estimated change rather than announcing itself.
Gross flows. transition_matrix(), boot_transition() and boot_flows()
give the weighted flow between states across waves, as conditional or joint
distributions and as population totals with standard errors and net flows.
A declarative estimation grammar. step_domain(), step_filter(),
step_estimate() and collect_estimates() run over a saved replicate object, so
the expensive replicate build happens once and many estimates are read off it.
Units whose domain value is missing are reported rather than quietly dropped, so
it is visible when the cells will not add up to the overall figure. Every verb of
the statistic DSL validates its arguments against the wave data, because a
replicate object will put a standard error and a confidence interval on whatever
number it is handed: an estimand must give one numeric or logical value per unit
(so prop() takes a condition, prop(status == "unemployed"), and refuses a
factor rather than averaging its integer codes), p in quantile(var, p) must be
a probability in [0, 1] (0.5, not 50), level must be a proportion, and
ratio(num, den) is taken over the domain where both are observed -- matching
survey::svyratio(na.rm = TRUE) exactly -- rather than dropping the missing rows
of each sum independently. A domain column may not be named after one of the
result table's own columns.
report_panel(), a self-contained HTML quality report for a panel run, and
new panel alerts (PN-01, PN-02, PN-06, PN-07, PN-08) plus an alert when a
nonresponse adjustment stops preserving the eligible total.
Exact multinomial PSU resampling is the default in the panel engines
(resample = "multinom"): the per-stratum resample counts sum to m_h, which
removes an ~8-10% inflation of the composite change SE seen in simulation. Within a
stratum the PSUs are ordered by how many waves they appear in, so the units that
carry the overlap get the exactly-coordinated draws and the answer does not depend
on which PSU ids happen to rotate out.
Smaller additions: bounds and calfun for step_model_calibration(),
step_trim_calibrated() after a model-calibration step, and refit_steps to
choose which steps are re-run per replicate.
Four new vignettes: Rotating panels, Pure panels, Coordinated replication
and Composite estimation. The change variance is now also validated against an
analytic estimator from a different family (Berger and Priam 2016, via
ReGenesees::svyDelta()), with the reference values frozen in the test suite.
These are fixes against 1.2.0 as published. The panel layer is new in this release, so nothing about it appears here: what it does and what it checks is described under New features.
Response-propensity models could diverge under production design weights: a binomial GLM reads the prior weights as the number of trials, so large design weights started the fit at the separation boundary and the fitted propensities collapsed. Model weights are now normalized to mean 1 wherever a weighted binomial model is fitted, which leaves the estimates invariant.
Recipe files are safe to exchange. read_recipe() accepts only a whitelist of
data-manipulation calls in the conditions and formulas a recipe stores, unless
allow_code = TRUE; before, a hand-edited condition still ran on the first
prep(). And write_recipe() no longer serializes a population = data frame
value by value: it is stored as a descriptor and asked back through references =,
as reference_sample() already was.
Adjustment cells are keyed unambiguously. A by value containing the " | "
separator, or equal to the "(missing)" sentinel, used to collapse two different
cells into one -- with the wrong adjustment factor for both, and the "cell with no
respondents" alert suppressed because the empty half was absorbed by the full one.
Two publication gates that failed open are closed: disclosure_risk() silently
skipped cells with a missing value or a non-positive median weight, leaving those
units out of the screen for weight dominance, and as_sae_input() rated both a
missing-domain area and a zero-variance domain as publishable.
step_round("preserve_total") breaks ties in the fractional part at random rather
than by row order, which was moving mass systematically towards whatever the file
was sorted by while reporting the grand total as preserved. Call set.seed()
before prep() for a reproducible result, as method = "balanced" already
required.
The model-assisted and propensity steps are steadier under cross-fitting: whether a
y_model() is a regression or a classification is decided once over the whole
sample instead of inside each fold (and an explicit family is respected),
propensities at the 0/1 boundary warn instead of being clamped in silence, and a
fold whose training set has only respondents says so rather than failing inside the
model engine.
The HTML reports describe the active weights, negatives included, in the weight-distribution card and the matching Status check; a column name containing a space no longer aborts the whole report; and the trimming note names the direction the weight total moved when the bounds could not be met.
boot_total() warns that the two-phase total variance is conservative, which it
was silently before, and the wave engines reject NA or blank in strata / psu,
as bootstrap_weights() has since 1.2.0 -- those rows paste into one design key
and were resampled as a single pseudo-PSU, understating the variance.
Stricter validation and robustness fixes for degenerate and edge-case inputs across the cascade, trimming and the auxiliary outputs, each with a regression test.
step_subsample() records a second phase and the
bootstrap returns the variance split into its two components (V = V1 + V2).step_pseudoweight() (pseudo-weighting, mass
imputation and doubly robust estimators), step_nr_sensitivity() for
nonignorable nonresponse, and data_defect() for the Meng (2018) view.reference_sample(), propagating the
reference's own sampling error.write_recipe() / read_recipe() serialize the method (not
the data) to a versionable YAML manifest.collect_replicate_weights(scramble = TRUE),
disclosure_risk(), and as_sae_input() for small-area models.weighting_alerts() / has_alerts() as a single programmatic
channel, domain_summary(min_n_eff = ) as an explicit publication gate, and
stable ids on every step.fpc) and t / percentile confidence intervals
in the replicate estimators.by-based steps are now built over the units still active, so units
dropped earlier no longer raise spurious (missing) cells.survey.?weightflow-concepts (argument correspondence with survey) and
?weightflow-alerts (the alert catalogue).report_weighting() gains a per-step card reading
the bias-variance trade-off of each adjustment, with quality alerts.crossfit now raises an alert.collect_propensities(), collect_step_detail() and
domain_summary().by in step_trim_calibrated()), and jackknife export
through collect_replicate_weights().step_nonresponse(method = "calibration")) and
unweighted propensity models (weight_model = FALSE).step_trim_calibrated()): trim calibrated weights into an
absolute interval while preserving the calibration totals.bootstrap_weights() and
jackknife_weights(), and a redistribute argument for step_trim_weights().report_weighting() becomes a full methodological quality report (GSBPM 5.6 /
ESS style), with a narrative mode, a per-domain reliability card and new charts.step_trim_weights() now trims
negative weights, and the report's before/after scatter is reproducible.step_calibrate(), alongside the classic
margins/totals inputs.by) and the exponential (raking)
distance (calfun = "raking") for linear calibration.prep(), an R-indicator of response representativity,
and a weight-distribution summary in the report.n_selected), external
consistency totals for model calibration (x_totals), and a new vignette on
preparing the sample.report_weighting() flags calibration steps that did not converge.step_calibrate(equal_within_cluster = TRUE) now implements the genuine
Lemaitre-Dufour (1987) integrative method.First release. The weighting cascade as a declarative recipe:
step_unknown_eligibility(), step_drop_ineligible(),
step_select_within(), step_nonresponse(), step_calibrate() (raking,
post-stratification, linear/GREG, bounded and integrative),
step_model_calibration(), step_trim(), step_trim_weights(), step_round(),
step_rescale() and step_assert().summary(), plot(), weight_factors(),
design_effect() and a self-contained HTML report from report_weighting().bootstrap_weights() (Rao-Wu rescaling, re-applying the
whole recipe on each replicate), boot_mean() / boot_total(), and bridges to
survey / srvyr through as_svydesign(), as_svrepdesign() and
collect_replicate_weights().population, sample_survey and sample_one.