| Title: | High-Performance Bounded Survey Weight Calibration |
|---|---|
| Description: | Drop-in replacement for autumn::harvest() providing multiple bounded survey-weight calibration solvers: ORIS (box-constrained Sinkhorn block-coordinate descent), raking, Sinkhorn, Greenkhorn, GREG and logit (Deville-Sarndal linear and logit duals), Chebyshev interior-point, and Newton-KL. Adds min_weight and max_weight bounds; shares a single C++17 core with the Python bindings. |
| Authors: | Dennis Alexis Valin Dittrich [aut, cre] |
| Maintainer: | Dennis Alexis Valin Dittrich <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.1 |
| Built: | 2026-09-17 06:30:05 UTC |
| Source: | https://github.com/davdittrich/leafblower |
Maps anesrake parameter names to harvest(). Unknown params produce warnings.
anesrake( inputter, targets, weightvec = NULL, caseid = NULL, pctlim = 0.05, cap = 5, choosemethod = "rake", type = "pctlim", nlim = 500L, iterate = TRUE, threads = 1L, ... )anesrake( inputter, targets, weightvec = NULL, caseid = NULL, pctlim = 0.05, cap = 5, choosemethod = "rake", type = "pctlim", nlim = 500L, iterate = TRUE, threads = 1L, ... )
inputter |
Data frame (maps to |
targets |
Named list of target proportions. |
weightvec |
Starting weights or NULL. |
caseid |
Ignored (row label not used by calibration). |
pctlim |
Deprecated; maps to |
cap |
Upper weight cap; maps to |
choosemethod |
Algorithm; |
type |
Ignored. |
nlim |
Max iterations; maps to |
iterate |
Ignored (always iterates). |
threads |
Ignored (single-threaded v1). |
... |
Unknown params produce a warning then are ignored. |
Same as harvest().
When called with only weights, computes the Kish (1965) weighting design effect
deff_K(w) = n * sum(w^2) / sum(w)^2.
design_effect(weights, outcome = NULL, data = NULL, target = NULL)design_effect(weights, outcome = NULL, data = NULL, target = NULL)
weights |
Numeric vector of calibrated weights. Must be finite, no NA, sum > 0. |
outcome |
Numeric outcome vector (optional; 4-arg form only). Must match
|
data |
Data frame with the calibration covariate columns named in |
target |
Named list of target proportions per categorical calibration margin
(one named entry per margin variable in |
When called with all four arguments, computes the Henry & Valliant (2015) calibration design effect (Eq 3.5, zero-correlation approximation):
where are the GREG residuals from a survey-
weighted least-squares fit of outcome on the calibration covariate matrix
(built from data + target), and
are survey-weighted population unit variances per Eq 2.7. is a constant column
followed by reference-coded dummies (first level dropped per calibration margin): the
constant is what makes a true GREG residual and bounds
, since the constant-only model is then nested in the
calibration model. Falls back to deff_K(w)
when target is empty, when is zero, or when calibration
margins are rank-deficient (with warning).
All numerical computation is performed in C++17 via .Call(C_rk_design_effect, ...);
R + Python wrappers produce byte-identical output by construction.
Numeric scalar design effect (deff_K for 1-arg, deff_H for 4-arg).
Kish, L. (1965). Survey Sampling. Wiley.
Henry, K.A. and Valliant, R. (2015). A design effect measure for calibration weighting in single-stage samples. Survey Methodology, 41(2), 315-331. Statistics Canada Catalogue No. 12-001-X. Equation 3.5.
Returns a data frame comparing original and weighted marginals to targets.
diagnose_weights(data, target, weights)diagnose_weights(data, target, weights)
data |
Data frame used in calibration. |
target |
Named list of target proportions (same format as harvest()). |
weights |
Numeric vector of calibrated weights, length nrow(data). |
An "NA" entry in target is treated as the missing-data
bin: a row counts toward it if it is NA or its value is the
literal string "NA" (conflation), matching how
harvest(add_na_proportion = TRUE) encodes the injected NA bin. For a
hand-built target that names a real category "NA" without
add_na_proportion, genuinely-missing rows are also counted there (a
documented collision; see ?harvest).
Data frame with columns: variable, level, prop_original, prop_weighted, target, error_original, error_weighted.
Computes length(weights) / design_effect(weights) (Kish 1965 form).
effective_sample_size(weights)effective_sample_size(weights)
weights |
Numeric vector of calibrated weights. Must be finite, no NA, sum > 0. |
Numeric scalar effective sample size.
Kish, L. (1965). Survey Sampling. Wiley.
Current calibration miss (matching autumn's export name)
get_current_miss(data, target, weights)get_current_miss(data, target, weights)
data |
Data frame. |
target |
Named list of target proportions. |
weights |
Numeric weight vector. |
An "NA" entry in target is treated as the missing-data
bin: a row counts toward it if it is NA or its value is the
literal string "NA" (conflation), matching how
harvest(add_na_proportion = TRUE) encodes the injected NA bin. For a
hand-built target that names a real category "NA" without
add_na_proportion, genuinely-missing rows are also counted there (a
documented collision; see ?harvest).
Named numeric vector of max absolute errors per variable.
Generate calibrated weights (drop-in for autumn::harvest)
harvest( data, target, min_weight = 0, max_weight = 5, capacity_penalty = NULL, alm_penalty = NULL, method = "oris", verbose = 0, max_iterations = 500, start_weights = NULL, attach_weights = TRUE, weight_column = "weights", convergence = list(), sor = NULL, bounds_mode = "cell", homotopy_levels = 1L, homotopy_start_factor = 1, homotopy_end_factor = 1, homotopy_budget_p = 0.5, scheduler = c("round_robin", "greedy"), eta_schedule = c("fixed", "tang_dynamic"), eta_start = 1, eta_end = 1, eta_schedule_power = 0.5, accelerate = FALSE, add_na_proportion = FALSE, auto_collapse = FALSE, collapse_vars = NULL, target_map = NULL, design_weights = NULL, newton_tsvd_ratio = 1e-08, ridge_lambda = 0, ... )harvest( data, target, min_weight = 0, max_weight = 5, capacity_penalty = NULL, alm_penalty = NULL, method = "oris", verbose = 0, max_iterations = 500, start_weights = NULL, attach_weights = TRUE, weight_column = "weights", convergence = list(), sor = NULL, bounds_mode = "cell", homotopy_levels = 1L, homotopy_start_factor = 1, homotopy_end_factor = 1, homotopy_budget_p = 0.5, scheduler = c("round_robin", "greedy"), eta_schedule = c("fixed", "tang_dynamic"), eta_start = 1, eta_end = 1, eta_schedule_power = 0.5, accelerate = FALSE, add_na_proportion = FALSE, auto_collapse = FALSE, collapse_vars = NULL, target_map = NULL, design_weights = NULL, newton_tsvd_ratio = 1e-08, ridge_lambda = 0, ... )
data |
A data frame containing columns to calibrate. |
target |
A named list of named numeric vectors (variable -> proportions). |
min_weight |
Lower bound on weights. Default 0 (no lower bound). |
max_weight |
Upper bound on weights. Default 5. |
capacity_penalty |
Numeric, controls how strongly capacity bounds are
enforced during ALM optimization in |
alm_penalty |
Numeric or |
method |
Calibration method (ORIS: Over-Relaxed Iterative Scaling). One of
|
verbose |
Integer verbosity: 0=silent, 1=progress, 2=debug. |
max_iterations |
Maximum inner BCD iterations per outer step. Default 500.
Under |
start_weights |
Starting weights vector or NULL (uniform). |
attach_weights |
If TRUE, return data frame with weights column. Default TRUE. |
weight_column |
Name of weight column. Default "weights". |
convergence |
Named list controlling the stopping criterion. Accepted keys:
Shorthand keys (each sets metric+rule+tol simultaneously):
Shorthands and explicit keys may be combined; explicit keys override shorthand
defaults.
chi2 cross-solver note: chi2 is not directly comparable
across methods. ORIS uses unnormalized cell mass as chebyshev note: |
sor |
Named list for SOR adaptive under-relaxation (ORIS and raking).
Default
|
bounds_mode |
One of "cell" (default) or "unit". Controls per-observation vs cell-aggregate bound enforcement. |
homotopy_levels |
Number of homotopy levels (default 1 = disabled). Values > 1 progressively tighten max_weight from homotopy_start_factor to homotopy_end_factor across n levels. |
homotopy_start_factor |
Starting max_weight multiplier (default 1.0 = no change). |
homotopy_end_factor |
Ending max_weight multiplier (default 1.0 = no change). |
homotopy_budget_p |
Budget split fraction across homotopy levels (default 0.5). |
scheduler |
Margin sweep scheduler: "round_robin" (default) or "greedy" (Greenkhorn priority). |
eta_schedule |
ALM penalty schedule: "fixed" (default) or "tang_dynamic". |
eta_start |
Starting ALM penalty multiplier (default 1.0). |
eta_end |
Ending ALM penalty multiplier (default 1.0). |
eta_schedule_power |
Power for Tang-eta schedule interpolation (default 0.5). |
accelerate |
Logical. Enable Safeguarded Regularized Anderson Acceleration
(SRAA-m, window m=5) for For oris and oris_soft, SRAA-m operates in log-factor space (dimension n_cats_total ~= 50-500, not M_cell), so history matrices are small. Expected benefit: >=30% fewer outer iterations on tight-bounds problems (K>=6, max_weight<3, or skewed margins). Behavioral changes when accelerate=TRUE for oris/oris_soft:
|
add_na_proportion |
Logical (default FALSE). When TRUE, NA observations
in each margin column are encoded as an explicit |
auto_collapse |
When |
collapse_vars |
Optional character vector of target variable names to
collapse. When supplied it always takes effect (rare categories in
those variables are merged into |
target_map |
Passed through for data-frame target format handling. |
design_weights |
Optional design weights vector. When non-NULL and
|
newton_tsvd_ratio |
Positive finite scalar (default |
ridge_lambda |
Tikhonov ridge penalty on the dual lambda (default 0.0 = off).
Adds |
... |
Additional arguments ignored. |
When to use oris_soft vs oris: Use
method="oris_soft" when oris gives max_error > 1e-3
and many observations are near max_weight. oris_soft is
roughly 10-30% slower than oris but finds a better constrained
optimum by temporarily relaxing bounds during optimization, then projecting
to exact feasibility at exit.
Greenkhorn: method="greenkhorn" implements the
Altschuler-Weed-Rigollet (2017) greedy coordinate-descent Sinkhorn variant.
At each step greenkhorn selects the single margin with the largest
violation and updates only that row/column – unlike standard raking which
sweeps all K margins every round. Pass accelerate=TRUE to enable
SRAA-m outer-loop acceleration on top of greenkhorn.
Logit calibration: method="logit" implements Deville-Sarndal
(1992) logit-distance Newton calibration. Bounds are enforced analytically
through the logit link – no post-hoc weight clamping is required. The logit
method typically converges in 10-20 Newton steps and is equivalent to
autumn::calibrate() with a logit distance function.
data frame with weights column if attach_weights=TRUE, else a numeric
vector of length n. In both cases the object carries attributes:
resultNamed list of solver diagnostics. Key fields:
status: integer status code. 0=converged (RK_OK);
1=did not converge (RK_ERR_NOCONV); 2=infeasible (RK_ERR_INFEAS);
3=bad argument (RK_ERR_BADARG); 4=budget exhausted – loss still
decreasing, increase max_iterations (RK_ERR_BUDGET);
5=loss plateau at constrained optimum, weights are valid (RK_ERR_STALL).
iterations: number of outer iterations completed.
max_error: maximum marginal error at the returned weights.
l1_weight_change: L1-normalized weight change sum|dw|/n.
Measured over the last convergence-check interval: sum|dX|/W_input
(total input weight) for oris, and sum|dw|/n for raking
and sinkhorn. Computed start-to-final (calibrated minus input,
sum|dw|/n) for logit. Reported as 0 for greg,
chebyshev, newton_kl, and greenkhorn (not tracked
by those solvers).
grake_norm: survey-grake normalized residual
max_k|misfit|/(1+|pop|).
convergence_used: Named list with metric, rule,
tol, and fired_at_iter documenting which criterion fired.
best_error: the convergence-metric value (the
convergence_used$metric family) associated with the returned result.
For the acceleration / ORIS finalize path this is RECOMPUTED on the
finalized, bound-clamped, n-normalized weights that are actually
returned (y2ks.13) – it reflects the returned solution and is no longer a
minimum-across-iterates value, so it need not equal the metric at
best_weights. Surfaced verbatim in the budget/stall warning.
best_weights: numeric vector (length n, sum normalized to
n) at the best iterate the solver tracked, finalized through the same
sum(w)=n + bounds contract. It is a DIFFERENT solution from the returned
weights and from the best_error reference above. It is all-zero when
the solver recorded no best iterate (exited before the first convergence
check); guard with if (sum(attr(r, "result")$best_weights) > 0)
before use.
convergence_used$convergence_reason: Character.
Why the solver exited: "criterion" (improvement criterion satisfied),
"budget" (budget exhausted – increase max_iterations),
"stall_kl" (weight KL plateau – at constrained KL minimum),
"stall_wchange" (SRAA-m weight-change plateau – at constrained optimum),
"infeasible", "error", or "legacy".
alm_capacity_mu_final: final ALM penalty after adaptive scaling (0 if not oris_soft).
alm_n_growth_events: adaptive penalty growth fire count.
alm_max_dual_norm: max absolute Lagrange dual at solver exit.
alm_sum_drift: |sum(weights) - n| after final projection (bounded by 1e-6 * n).
sraa_demoted: logical; TRUE iff SRAA-m
acceleration was requested with scheduler="greedy" and the
greedy scheduler was demoted to round-robin (greedy is incompatible
with SRAA's fixed-point geometry). FALSE for non-oris /
non-raking solvers and whenever no demotion occurred.
algorithmCharacter name of the solver used.
iterationsConvenience alias for result$iterations.
## Not run: df <- data.frame(sex = factor(sample(c("M","F"), 500, TRUE))) tgt <- list(sex = c(M = 0.5, F = 0.5)) # Greenkhorn (greedy coordinate-descent IPF) r_grk <- harvest(df, tgt, method = "greenkhorn") # Logit-distance Newton calibration (Deville-Sarndal 1992) r_logit <- harvest(df, tgt, method = "logit") ## End(Not run)## Not run: df <- data.frame(sex = factor(sample(c("M","F"), 500, TRUE))) tgt <- list(sex = c(M = 0.5, F = 0.5)) # Greenkhorn (greedy coordinate-descent IPF) r_grk <- harvest(df, tgt, method = "greenkhorn") # Logit-distance Newton calibration (Deville-Sarndal 1992) r_logit <- harvest(df, tgt, method = "logit") ## End(Not run)
Weighted proportions
weighted_pct(x, weights)weighted_pct(x, weights)
x |
Factor or character vector. |
weights |
Numeric weights, same length as x. |
Named numeric vector of weighted proportions summing to 1.