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.