Skip to contents

A DesignSeqOneByOneKK14 extension that replaces KK14's unweighted Mahalanobis matching distance with a response-weighted squared distance: at each assignment, a per-covariate weight vector is re-estimated from the responses observed so far (weighting each covariate by the estimated strength of its association with the response, e.g. an absolute standardized regression coefficient), and the new subject is matched to its nearest reservoir subject under that weighted distance rather than the raw (unweighted) Mahalanobis distance KK14 uses. Weighting the match by outcome association means matching effort is spent preferentially on prognostic covariates (those that actually explain outcome variance) rather than equally on every covariate, which is intended to improve estimator efficiency beyond what outcome-agnostic matching achieves. morrison = TRUE additionally switches to Morrison and Owen's (2025) alternative calibration of the matching-acceptance threshold (differing in the fixed- vs. variable-\(n\) settings) and removes KK14's burn-in wait before matching begins.

Weight estimation. compute_weights() dispatches on response_type to a corresponding kk21_*_weights_cpp() backend that fits a per-covariate simple regression of the response on that covariate (given all responses observed so far) and returns the absolute t-statistic (coefficient over its standard error) as that covariate's weight: OLS for "continuous", logistic for "incidence", negative-binomial (or, if count_use_speedup = TRUE, OLS on log(y + 1)) for "count", beta regression (or OLS on the logit scale if proportion_use_speedup = TRUE) for "proportion", Weibull/lognormal/ log-logistic AFT (or OLS on log(y) if survival_use_speedup_for_no_censoring = TRUE and there is no censoring yet) for "survival", and proportional-odds (or OLS on the numeric-coerced level if ordinal_use_speedup = TRUE) for "ordinal". Weights are normalized to sum to 1 and cached per-iteration in private$iteration_weights (retrievable via get_iteration_weights()); the *_use_speedup flags trade weight accuracy for speed by substituting a fast continuous-regression proxy for the response-type-appropriate GLM/AFT/ordinal fit on every single assignment call.

Weighted matching test. The weighted squared distance from the new subject to every reservoir subject is computed (compute_weighted_sqd_distances_cpp()), and the match is accepted only if the minimum weighted distance falls below the private$compute_lambda() quantile of a bootstrapped reference distribution of weighted pairwise distances among subjects enrolled so far (compute_bootstrapped_weighted_sqd_distances_cpp(), num_boot resamples) — a nonparametric, simulation-based acceptance threshold, in contrast to KK14's closed-form F-distribution threshold. As in KK14, an accepted match receives the opposite treatment of its match; a rejected (or empty-reservoir) draw is randomized and added to the reservoir.

Fallback to KK14. Before enough responses have accumulated to fit the weight-estimation regressions reliably (fewer than 2 * (ncol(X) + 2) non-missing responses — two observations per regression parameter, at minimum ncol(X) + 2), assign_wt() falls back to the inherited (unweighted) KK14 assignment rule entirely, via super$assign_wt().

References

Kapelner, A., and Krieger, A. M. (2014). "Matching on-the-fly: Sequential allocation with higher power and efficiency." Biometrics, 70(2), 378-388, doi:10.1111/biom.12148 , for the base matching-on-the-fly algorithm this class extends with outcome-weighted distances (Kapelner and Krieger, 2021); see also Morrison, T., and Owen, A. B. (2025) for the alternative morrison = TRUE threshold calibration referenced by the morrison argument.

Super classes

Design -> DesignSeqOneByOne -> DesignSeqOneByOneKK14 -> DesignSeqOneByOneKK21

Methods

+ inherited public methods from DesignSeqOneByOneKK14
+ inherited public methods from DesignSeqOneByOne
+ inherited public methods from Design


DesignSeqOneByOneKK21$new()

Initialize a matching-on-the-fly sequential experimental design which matches based on Kapelner and Krieger (2021) with option to use matching parameters of Morrison and Owen (2025)

Usage

DesignSeqOneByOneKK21$new(
  response_type,
  prob_T = 0.5,
  include_is_missing_as_a_new_feature = TRUE,
  n = NULL,
  verbose = FALSE,
  lambda = NULL,
  t_0_pct = NULL,
  morrison = FALSE,
  p = NULL,
  num_boot = NULL,
  count_use_speedup = TRUE,
  proportion_use_speedup = TRUE,
  survival_use_speedup_for_no_censoring = TRUE,
  ordinal_use_speedup = TRUE,
  missingness_method = "impute",
  design_formula = ~.,
  seed = NULL,
  ...
)

Arguments

response_type

The data type of response values which must be one of the following: "continuous", "incidence", "proportion", "count", "survival". This package will enforce that all added responses via the add_one_subject_response method will be of the appropriate type.

prob_T

The probability of the treatment assignment. This defaults to 0.5.

include_is_missing_as_a_new_feature

If missing data is present in a variable, should we include another dummy variable for its missingness in addition to imputing its value? If the feature is type factor, instead of creating a new column, we allow missingness to be its own level. The default is TRUE.

n

The sample size (if fixed). Default is NULL for not fixed.

verbose

A flag indicating whether messages should be displayed to the user. Default is FALSE.

lambda

The quantile cutoff of the subject distance distribution for determining matches. If unspecified and morrison = FALSE, default is 10%.

t_0_pct

The percentage of total sample size n where matching begins. If unspecified and morrison = FALSE, default is 35%.

morrison

Default is FALSE which implies matching via the KK14 algorithm using lambda and t_0_pct matching. If TRUE, we use Morrison and Owen (2025)'s formula for lambda which differs in the fixed n versus variable n settings and matching begins immediately with no wait for a certain reservoir size like in KK14.

p

The number of covariate features. Must be specified when morrison = TRUE otherwise do not specify this argument.

num_boot

the number of bootstrap samples taken to approximate the subject-distance distribution. Default is 500.

count_use_speedup

Should we speed up the estimation of the weights in the response = count case via a continuous regression on log(y + 1). instead of a negative binomial regression each time? This is at the expense of the weights being less accurate. Default is TRUE.

proportion_use_speedup

Should we speed up the estimation of the weights in the response = proportion case via a continuous regression on log(y / (1 - y)) instead of a beta regression each time? This is at the expense of the weights being less accurate. Default is TRUE.

survival_use_speedup_for_no_censoring

Should we speed up the estimation of the weights in the response = survival case via a continuous regression on log(y) instead of a Weibull AFT regression each time, but only when there is no censoring in the data collected so far? This is at the expense of the weights being less accurate when censoring is present. Default is TRUE.

ordinal_use_speedup

Should we speed up the estimation of the weights in the response = ordinal case via a continuous regression on the ordinal levels coerced to numeric. instead of a proportional odds model each time? This is at the expense of the weights being less accurate. Default is TRUE.

missingness_method

How to handle missing values in covariates.

design_formula

A formula object.

seed

Integer seed for reproducibility.

...

Extra arguments passed to the DesignSeqOneByOneKK14 superclass.

Returns

A new `DesignSeqOneByOneKK21` object

Examples

seq_des = DesignSeqOneByOneKK21$new(n = 6, response_type = "continuous")
seq_des$add_one_subject_to_experiment_and_assign(data.frame(x = rnorm(1)))


DesignSeqOneByOneKK21$get_iteration_weights()

Retrieve the full history of normalized covariate weight vectors computed by compute_weights() across every assignment call so far (see class documentation), keyed by subject index t, for inspecting how the outcome-informed weighting evolved as data accrued.

Usage

DesignSeqOneByOneKK21$get_iteration_weights()

Returns

A list of numeric weight vectors (one per assignment call at which weights were computed), each summing to 1 and named by covariate.


DesignSeqOneByOneKK21$get_covariate_weights()

Retrieve the normalized covariate weight vector from the most recent assignment call (see class documentation for how weights are estimated).

Usage

DesignSeqOneByOneKK21$get_covariate_weights()

Returns

A numeric vector of weights, one per covariate, summing to 1 and named by covariate; NULL if weights have not yet been computed (e.g. still in the KK14 fallback regime).


DesignSeqOneByOneKK21$assign_wt()

Draw the next subject's treatment assignment via the KK21 outcome-weighted matching-on-the-fly rule (see class documentation): falls back to unweighted KK14 matching if too few responses have accumulated to estimate covariate weights, otherwise re-estimates weights, matches to the nearest reservoir subject under the weighted distance if it clears the bootstrapped acceptance threshold (assigning the opposite treatment), or randomizes into the reservoir otherwise.

Usage

DesignSeqOneByOneKK21$assign_wt()

Returns

The treatment assignment (0 or 1) for the next subject.


DesignSeqOneByOneKK21$clone()

The objects of this class are cloneable with this method.

Usage

DesignSeqOneByOneKK21$clone(deep = FALSE)

Arguments

deep

Whether to make a deep clone.

Examples

seq_des = DesignSeqOneByOneKK21$new(n = 6, response_type = 'continuous')
seq_des$add_one_subject_to_experiment_and_assign(data.frame(x1 = rnorm(1)))
#> [1] 1

## ------------------------------------------------
## Method `DesignSeqOneByOneKK21$new()`
## ------------------------------------------------

# \donttest{
seq_des = DesignSeqOneByOneKK21$new(n = 6, response_type = "continuous")
seq_des$add_one_subject_to_experiment_and_assign(data.frame(x = rnorm(1)))
#> [1] 0
# }