| Title: | Model-Averaged Propensity Scores Selected by Prognostic-Score Balance |
|---|---|
| Description: | Constructs a model-averaged propensity score as a convex combination of candidate propensity score models, with mixing weights selected on a simplex grid to optimize covariate or prognostic-score balance, implementing the method of Kabata, Stuart and Shintani (2024) <doi:10.1186/s12874-024-02350-y>. Prognostic scores follow Hansen (2008) <doi:10.1093/biomet/asn004>: outcome models are fit on untreated units only. The resulting score is designed to be supplied directly to the matchit() function of 'MatchIt' as a distance measure or to the weightit() function of 'WeightIt' as a propensity score, with balance assessment via 'cobalt'. |
| Authors: | Daijiro Kabata [aut, cre, cph] |
| Maintainer: | Daijiro Kabata <[email protected]> |
| License: | GPL (>= 2) |
| Version: | 1.0.1 |
| Built: | 2026-07-10 21:50:21 UTC |
| Source: | https://github.com/cran/psAve |
A method for cobalt::bal.tab(): assesses balance on the covariates of a
psave() fit under the implied inverse-probability weights, with the
model-averaged propensity score and (when available) the model-averaged
prognostic score supplied as distance measures – the prognostic-score
balance diagnostic of Stuart, Lee and Leacy (2013).
## S3 method for class 'psave' bal.tab(x, ...)## S3 method for class 'psave' bal.tab(x, ...)
x |
A |
... |
Further arguments passed on to |
The call delegates to the default cobalt machinery as
cobalt::bal.tab(<covariates>, treat = x$treat, weights = x$weights, s.d.denom = x$s.d.denom, distance = data.frame(ps = x$ps, prog = x$prog), ...), so all the usual cobalt arguments (un, stats,
thresholds, ...) are available, and display conventions are
cobalt's own (the selection criterion inside psave() uses the
paper's uniform sample-SD standardization instead; see
psave_criteria()).
A bal.tab object; see cobalt::bal.tab().
Stuart EA, Lee BK, Leacy FP (2013). Prognostic score-based balance measures can be a useful diagnostic for propensity score methods in comparative effectiveness research. Journal of Clinical Epidemiology, 66(8), S84-S90. doi:10.1016/j.jclinepi.2013.01.013
psave(), cobalt::bal.tab(), plot.psave()
data("lalonde", package = "MatchIt") fit <- psave(treat ~ age + educ + married + re74, data = lalonde, outcome = ~ re78, ps.methods = "glm", prog.methods = "glm") cobalt::bal.tab(fit, un = TRUE)data("lalonde", package = "MatchIt") fit <- psave(treat ~ age + educ + married + re74, data = lalonde, outcome = ~ re78, ps.methods = "glm", prog.methods = "glm") cobalt::bal.tab(fit, un = TRUE)
fitted() is the canonical extractor for the model-averaged scores of a
psave() fit; weights() extracts the inverse-probability weights implied
by the averaged propensity score at the fitted estimand.
## S3 method for class 'psave' fitted(object, type = c("ps", "prog"), ...) ## S3 method for class 'psave' weights(object, ...)## S3 method for class 'psave' fitted(object, type = c("ps", "prog"), ...) ## S3 method for class 'psave' weights(object, ...)
object |
A |
type |
|
... |
Ignored. |
For fitted(), a numeric vector named by the rownames of the
analyzed data. For weights(), the numeric vector object$weights
(weights at the fitted estimand only; for other estimands use
WeightIt::get_w_from_ps(fitted(object), object$treat, estimand = ...)).
Three diagnostic displays for a psave() fit:
"balance"a Love plot of covariate and prognostic-score balance
before/after weighting, via cobalt::love.plot() (dispatched through
bal.tab.psave(); cobalt is an Import, so always available).
"distribution"the distribution of the propensity scores by treatment group: grey curves for the candidate models, a colored curve for the selected average (base graphics). Inspect this for extreme candidate scores.
"criterion"the selection criterion over the grid:
exact for up to three candidates (a curve for M = 2, a colored grid map
for M = 3); for M > 3, one profile per candidate (the minimum criterion
value attainable at each value of ). Requires the stored
path (keep.path = TRUE and a grid of at most 100,000 rows).
## S3 method for class 'psave' plot(x, type = c("balance", "distribution", "criterion"), ...)## S3 method for class 'psave' plot(x, type = c("balance", "distribution", "criterion"), ...)
x |
A |
type |
One of |
... |
For |
For "balance", the ggplot object from cobalt::love.plot()
(invisibly, after printing); otherwise x, invisibly.
psave(), bal.tab.psave(), cobalt::love.plot()
Computes the model-averaged propensity score (or prognostic score) for new
observations by applying the stored candidate fits to newdata, clipping
candidate propensity scores as at fit time, and combining them with the
selected mixing weights. Requires psave(..., keep.fits = TRUE).
## S3 method for class 'psave' predict(object, newdata, type = c("ps", "prog"), ...)## S3 method for class 'psave' predict(object, newdata, type = c("ps", "prog"), ...)
object |
A |
newdata |
A data frame containing the variables of the propensity
score formula (for |
type |
|
... |
Ignored. |
A numeric vector with one score per row of newdata, named by its
rownames. If newdata is missing, the in-sample fitted scores are
returned (equivalent to fitted.psave()).
Prints a one-screen summary of a fitted psave() object: estimand and
criterion, the selected mixing weights and as
labeled text bars, the criterion value, a three-row balance preview (the
worst covariates plus the prognostic score), and then the literal next
call – echoing the formula and data name from your own psave() call –
that hands the averaged score to MatchIt::matchit() or
WeightIt::weightit().
## S3 method for class 'psave' print(x, digits = 3, ...)## S3 method for class 'psave' print(x, digits = 3, ...)
x |
A |
digits |
Number of significant digits to print. Default 3. |
... |
Ignored. |
x, invisibly.
psave() constructs a model-averaged propensity score
: a convex combination of
candidate propensity score models whose mixing weights are
selected on a simplex grid to optimize a balance criterion – by default the
weighted absolute standardized mean difference of a model-averaged
prognostic score (the "Prog (Ave)" estimator of Kabata, Stuart and
Shintani 2024). The result is deliberately modest: a numeric score vector
designed to be handed to MatchIt::matchit() as distance, or to
WeightIt::weightit() as ps, with balance assessment via cobalt.
psave( formula, data, outcome = NULL, estimand = c("ATT", "ATE"), criterion = c("prog", "smd", "ks", "logloss"), prog.target = "average", ps.methods = c("glm", "rpart", "ranger", "xgboost"), prog.methods = c("glm", "rpart", "ranger", "xgboost"), ps.matrix = NULL, prog.matrix = NULL, ps.append = NULL, prog.append = NULL, average = TRUE, family = gaussian(), step = 0.05, clip = c(0.01, 0.99), s.d.denom = "treated", cv = 5L, control = list(), keep.fits = FALSE, keep.path = TRUE, verbose = FALSE, ... )psave( formula, data, outcome = NULL, estimand = c("ATT", "ATE"), criterion = c("prog", "smd", "ks", "logloss"), prog.target = "average", ps.methods = c("glm", "rpart", "ranger", "xgboost"), prog.methods = c("glm", "rpart", "ranger", "xgboost"), ps.matrix = NULL, prog.matrix = NULL, ps.append = NULL, prog.append = NULL, average = TRUE, family = gaussian(), step = 0.05, clip = c(0.01, 0.99), s.d.denom = "treated", cv = 5L, control = list(), keep.fits = FALSE, keep.path = TRUE, verbose = FALSE, ... )
formula |
A two-sided formula |
data |
A data frame containing the variables in |
outcome |
The outcome specification for the prognostic score: a
one-sided formula |
estimand |
|
criterion |
The selection criterion for |
prog.target |
Only used when |
ps.methods |
Character vector of candidate propensity score learners.
The fixed, explicit default is |
prog.methods |
Character vector of candidate learners for the
untreated-only prognostic models |
ps.matrix |
Optional n x M numeric matrix of user-supplied candidate
propensity scores (values strictly in (0, 1), column names required).
Overrides |
prog.matrix |
Optional n x K numeric matrix of user-supplied candidate
prognostic scores (column names required). Overrides |
ps.append |
Optional extra candidate propensity scores appended
AFTER the candidates from |
prog.append |
Optional extra candidate prognostic scores appended
AFTER the candidates from |
average |
If |
family |
The prognostic-model family: |
step |
The simplex-grid increment for BOTH |
clip |
Length-2 numeric: each candidate propensity score column is
clipped to |
s.d.denom |
The group whose unweighted standard deviation standardizes
mean differences in the ASMD-based criteria, passed to
|
cv |
Number of cross-validation folds |
control |
A named list of per-learner hyperparameter overrides, e.g.
|
keep.fits |
If |
keep.path |
If |
verbose |
If |
... |
Reserved for future use; supplying unused arguments triggers a warning. |
Candidate propensity scores. Each learner in ps.methods is fit on
all units and predicts
in-sample; each column is clipped to clip before averaging. Extra
user-supplied candidates given via ps.append are validated, clipped
identically, and appended AFTER the base candidates (from ps.methods
or ps.matrix), so the first-minimum tie-break favors the base set.
Model-averaged prognostic score. Each learner in prog.methods is
fit on the untreated units only and predicts
for all units; prog.append columns are appended after these
candidates. The mixing weights minimize the
unweighted untreated-set mean squared error
over simplex_grid(K, step); .
Model-averaged propensity score. The mixing weights
minimize criterion over simplex_grid(M, step), where each grid row
implies and the weights
below.
With the averaged propensity score:
These are identical to WeightIt::get_w_from_ps() at the same estimand.
At each grid row :
logloss
(finite by clipping).
smdthe mean over covariate columns of
where is the unweighted sample SD of in the
s.d.denom group (the treated group for both estimands, per the
paper's supplement).
ksthe mean over covariates of the proper weighted-eCDF
Kolmogorov-Smirnov statistic
with
;
for binary columns this equals the absolute difference in weighted
proportions.
progthe formula applied to the single
column (prog.target = "average") or
(prog.target names a learner). THE DEFAULT.
When prog.target names a single prognostic candidate, criterion.value
is the weighted ASMD of that candidate's prognostic score, whereas the
prog column of the diagnostics table always refers to the
model-averaged prognostic score ; the two values therefore
need not coincide, and summary.psave() prints a footnote to this effect.
Faithful to the published method, the smd and prog criteria standardize
all columns – including binary ones – by the plain unweighted sample
SD (uniform sample-SD standardization; bin.vars = FALSE is passed to
cobalt::col_w_smd() for every column). The display-oriented balance
component and bal.tab.psave() use cobalt's native conventions
instead; see vignette("method-details", "psAve").
Both grids are enumerated by simplex_grid() in integer arithmetic
(every valid grid point is present by construction) in a documented order:
the first component descends from 1 to 0, then the second on the remainder,
and so on – the first row puts all weight on the first candidate. Ties in
any argmin take the FIRST row attaining the minimum (within a 1e-9 relative
numerical tolerance), so ties favor learners listed earlier in
ps.methods/prog.methods; candidates appended via
ps.append/prog.append always come last and therefore lose ties to the
base candidates. The tolerance is deliberate: the criterion
values come from floating-point matrix algebra whose lowest-order bits can
differ across BLAS implementations, so an exact bitwise which.min()
would not be reproducible across machines, whereas the tolerant
first-minimum rule is. gamma always minimizes the unweighted
untreated-set MSE; these rules are fixed and not arguments.
Prognostic models see the outcomes of untreated units only, and the
criterion compares weighted covariate-like summaries (the prognostic
score) between arms – it never uses a treated-untreated outcome contrast
(Hansen 2008; Stuart, Lee and Leacy 2013). This is the same argument under
which prognostic-score balance diagnostics are recommended for propensity
score analyses; see vignette("method-details", "psAve").
There is no seed argument: call set.seed() before psave() –
stochastic learners are ranger and xgboost. info$learners records the
resolved hyperparameters and engine package versions.
WeightIt::method_super with SL.method = "method.balance" (Pirracchio
and Carone 2018) selects a SuperLearner combination by covariate balance
and is weighting-only. psave() targets prognostic-score balance on an
exhaustive simplex grid, and returns a score vector equally usable for
matching and weighting. See psave-details and
vignette("method-details", "psAve") for the differences from the paper's
reference implementation.
An object of class "psave": a list with components
psnumeric(n), named by rownames(data): the model-averaged
propensity score – the deliverable for
MatchIt::matchit(distance = ) / WeightIt::weightit(ps = ).
prognumeric(n), named: the model-averaged prognostic score
; NULL when outcome was not supplied (possible only for
the outcome-free criteria "smd", "ks", and "logloss").
lambdanamed numeric(M): the selected PS mixing weights, where
M counts the base candidates plus any ps.append columns.
gammanamed numeric(K) (or NULL): the selected prognostic
mixing weights, where K counts any prog.append columns.
weightsnumeric(n): the IPW at estimand implied by ps.
ps.candidatesn x M matrix of clipped candidate propensity
scores (fitted or user-supplied; ps.append columns come last).
prog.candidatesn x K matrix of candidate prognostic predictions
for all n units (or NULL); prog.append columns come last.
criterion, criterion.value
the criterion used and its value at
the selected (paper-faithful standardization).
diagnostics(M+1) x 4 data frame: all four criteria
(psave_criteria()) for each candidate and for the selected average –
the "was averaging worth it?" table. Its prog column always uses the
model-averaged prognostic score , even when prog.target
names a single candidate (see Details); it then need not equal
criterion.value.
pathdata frame of the full grid (M columns +
value + logical selected) in grid order, or NULL.
gamma.msenamed numeric(K+1): untreated MSE per prognostic
candidate and for the selected average (or NULL).
balancedata frame (covariates + prog): unweighted and
weighted SMD and KS (cobalt display conventions).
treatinteger(n) 0/1 treatment as used.
covsnumeric n x p balance-covariate matrix with
attr(, "bin.vars").
estimand, s.d.denom, prog.target, average
scalars, as resolved.
outcome.namename of the outcome variable, or NA.
formula, data
as supplied (they power psave_match(),
psave_weight() and print.psave(); note the memory cost of storing
data).
fitslist of fitted learners iff keep.fits = TRUE (enables
predict.psave()); otherwise NULL.
infolist: step, clip, cv, family, grid.size, n,
learners (labels, resolved hyperparameters, engine versions),
psAve.version.
callthe matched call.
Kabata D, Stuart EA, Shintani A (2024). Prognostic score-based model averaging approach for propensity score estimation. BMC Medical Research Methodology, 24, 228. doi:10.1186/s12874-024-02350-y
Hansen BB (2008). The prognostic analogue of the propensity score. Biometrika, 95(2), 481-488. doi:10.1093/biomet/asn004
Stuart EA, Lee BK, Leacy FP (2013). Prognostic score-based balance measures can be a useful diagnostic for propensity score methods in comparative effectiveness research. Journal of Clinical Epidemiology, 66(8), S84-S90. doi:10.1016/j.jclinepi.2013.01.013
Xie Y, Zhu Y, Cotton CA, Wu P (2019). A model averaging approach for estimating propensity scores by optimizing balance. Statistical Methods in Medical Research, 28(1), 84-101. doi:10.1177/0962280217715487
psave_match(), psave_weight(), simplex_grid(),
psave_criteria(), bal.tab.psave(), plot.psave(), summary.psave(),
predict.psave()
if (requireNamespace("MatchIt", quietly = TRUE)) { data("lalonde", package = "MatchIt") # Paper-headline "Prog (Ave)", ATT. For speed, this example restricts the # candidate learners; the default is # ps.methods = prog.methods = c("glm", "rpart", "ranger", "xgboost"). meths <- if (requireNamespace("rpart", quietly = TRUE)) c("glm", "rpart") else "glm" fit <- psave(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, outcome = ~ re78, ps.methods = meths, prog.methods = meths) fit summary(fit) # hand off to MatchIt (canonical explicit call): m <- MatchIt::matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, distance = fit$ps) # or, reusing the stored formula and data (no retyping hazard): m2 <- psave_match(fit) }if (requireNamespace("MatchIt", quietly = TRUE)) { data("lalonde", package = "MatchIt") # Paper-headline "Prog (Ave)", ATT. For speed, this example restricts the # candidate learners; the default is # ps.methods = prog.methods = c("glm", "rpart", "ranger", "xgboost"). meths <- if (requireNamespace("rpart", quietly = TRUE)) c("glm", "rpart") else "glm" fit <- psave(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, outcome = ~ re78, ps.methods = meths, prog.methods = meths) fit summary(fit) # hand off to MatchIt (canonical explicit call): m <- MatchIt::matchit(treat ~ age + educ + race + married + nodegree + re74 + re75, data = lalonde, distance = fit$ps) # or, reusing the stored formula and data (no retyping hazard): m2 <- psave_match(fit) }
Evaluates, for an arbitrary propensity score vector, the four selection
criteria used by psave(): the treatment-assignment log loss, the mean
weighted absolute standardized mean difference (ASMD) of the covariates,
the mean weighted Kolmogorov-Smirnov (KS) statistic of the covariates, and
the weighted ASMD of a prognostic score. This function powers the
diagnostics table of a psave object (the "was averaging worth it?"
comparison) and is exported as a methods-research utility.
psave_criteria( ps, treat, covs, prog = NULL, estimand = c("ATT", "ATE"), s.d.denom = "treated", bin.vars = NULL )psave_criteria( ps, treat, covs, prog = NULL, estimand = c("ATT", "ATE"), s.d.denom = "treated", bin.vars = NULL )
ps |
Numeric vector of propensity scores, strictly inside (0, 1). |
treat |
Treatment vector; coerced to 0/1 like the left-hand side of
the |
covs |
Numeric matrix (or all-numeric data frame) of covariates, one
row per unit. Factors must already be expanded to dummy columns (as in
the |
prog |
Optional numeric vector: a prognostic score. If |
estimand |
|
s.d.denom |
Group whose (unweighted) standard deviation standardizes
the mean differences: |
bin.vars |
Optional logical vector flagging binary columns of |
Weights are the inverse-probability weights implied by ps at estimand:
for the ATT, for treated units and for
untreated units; for the ATE, and . The four
criteria are:
logloss.
smdthe mean over covariates of
, where
is the weighted mean of in arm and is the
unweighted sample SD of in the s.d.denom group. Computed via
cobalt::col_w_smd().
ksthe mean over covariates of the proper weighted-eCDF KS
statistic , computed via
cobalt::col_w_ks(); for binary columns this is the absolute
difference in weighted proportions.
progthe same weighted ASMD formula applied to the single
column prog.
Faithful to the published method (and its reference implementation), the
smd and prog criteria standardize every column, including binary
ones, by the plain unweighted sample SD (sd(), the formula) of
the s.d.denom group – i.e., bin.vars = FALSE is passed to
cobalt::col_w_smd() for all columns. cobalt's own display convention
(binary columns standardized by ) is used only in the
display-oriented balance component of a psave object and in
bal.tab.psave(). For the KS criterion the two conventions coincide.
A named numeric vector with elements logloss, smd, ks, and
prog (the last is NA when prog = NULL).
Kabata D, Stuart EA, Shintani A (2024). Prognostic score-based model averaging approach for propensity score estimation. BMC Medical Research Methodology, 24, 228. doi:10.1186/s12874-024-02350-y
Hansen BB (2008). The prognostic analogue of the propensity score. Biometrika, 95(2), 481-488. doi:10.1093/biomet/asn004
Xie Y, Zhu Y, Cotton CA, Wu P (2019). A model averaging approach for estimating propensity scores by optimizing balance. Statistical Methods in Medical Research, 28(1), 84-101. doi:10.1177/0962280217715487
psave(), simplex_grid(), cobalt::col_w_smd(),
cobalt::col_w_ks()
set.seed(1) n <- 200 x1 <- rnorm(n); x2 <- rbinom(n, 1, 0.4) a <- rbinom(n, 1, plogis(-0.5 + x1 + 0.5 * x2)) ps <- pmin(pmax(fitted(glm(a ~ x1 + x2, family = binomial())), 0.01), 0.99) g <- 1 + 0.5 * x1 - 0.2 * x2 # a (toy) prognostic score psave_criteria(ps, a, cbind(x1 = x1, x2 = x2), prog = g, estimand = "ATT")set.seed(1) n <- 200 x1 <- rnorm(n); x2 <- rbinom(n, 1, 0.4) a <- rbinom(n, 1, plogis(-0.5 + x1 + 0.5 * x2)) ps <- pmin(pmax(fitted(glm(a ~ x1 + x2, family = binomial())), 0.01), 0.99) g <- 1 + 0.5 * x1 - 0.2 * x2 # a (toy) prognostic score psave_criteria(ps, a, cbind(x1 = x1, x2 = x2), prog = g, estimand = "ATT")
Convenience pass-through to MatchIt::matchit(): matches on the
model-averaged propensity score of a psave() fit, reusing the formula and
data stored in the object. Equivalent to the canonical explicit call
MatchIt::matchit(<formula>, data = <data>, distance = fit$ps, ...)
but with no opportunity for row misalignment between the two steps. All
... arguments are forwarded verbatim; the return value is an ordinary
matchit object, so the full MatchIt/cobalt toolkit applies.
psave_match(x, ...)psave_match(x, ...)
x |
A |
... |
Arguments forwarded verbatim to |
A matchit object; see MatchIt::matchit().
psave(), psave_weight(), MatchIt::matchit()
data("lalonde", package = "MatchIt") fit <- psave(treat ~ age + educ + married + re74, data = lalonde, outcome = ~ re78, ps.methods = "glm", prog.methods = "glm") m <- psave_match(fit, method = "nearest", caliper = 0.2) cobalt::bal.tab(m, distance = data.frame(prog = fit$prog))data("lalonde", package = "MatchIt") fit <- psave(treat ~ age + educ + married + re74, data = lalonde, outcome = ~ re78, ps.methods = "glm", prog.methods = "glm") m <- psave_match(fit, method = "nearest", caliper = 0.2) cobalt::bal.tab(m, distance = data.frame(prog = fit$prog))
Convenience pass-through to WeightIt::weightit(): constructs balancing
weights from the model-averaged propensity score of a psave() fit at the
fitted estimand, reusing the stored formula and data. Equivalent to the
canonical explicit call
WeightIt::weightit(<formula>, data = <data>, ps = fit$ps, estimand = fit$estimand, ...)
All ... arguments are forwarded verbatim; the return value is an ordinary
weightit object.
psave_weight(x, ...)psave_weight(x, ...)
x |
A |
... |
Arguments forwarded verbatim to |
A weightit object; see WeightIt::weightit().
psave(), psave_match(), WeightIt::weightit(),
WeightIt::get_w_from_ps()
data("lalonde", package = "MatchIt") fit <- psave(treat ~ age + educ + married + re74, data = lalonde, outcome = ~ re78, ps.methods = "glm", prog.methods = "glm") w <- psave_weight(fit) cobalt::bal.tab(w, distance = data.frame(prog = fit$prog))data("lalonde", package = "MatchIt") fit <- psave(treat ~ age + educ + married + re74, data = lalonde, outcome = ~ re78, ps.methods = "glm", prog.methods = "glm") w <- psave_weight(fit) cobalt::bal.tab(w, distance = data.frame(prog = fit$prog))
psAve implements the published method of Kabata, Stuart and Shintani (2024) rather than reproducing its reference code line by line. Five documented defects/quirks of the reference implementation are deliberately fixed (each fix follows the paper's stated definitions):
Integer simplex grid. The reference code enumerated the mixing-weight
grid with expand.grid() and kept rows passing an exact floating-point
rowSums(gr) == 1 test, silently dropping about 10.6% of the valid grid
points for M = 4 candidates at step 0.05 (1,584 of 1,771 kept).
simplex_grid() enumerates integer compositions, so every valid point
is present by construction, and the enumeration order makes the
first-minimum tie-break a reproducible rule.
Proper weighted-eCDF KS statistic. The reference Fks computed
ks.test() on covariate values multiplied by the weights, which is
not the paper's weighted-eCDF definition. psAve computes
with weighted empirical CDFs in each
arm (as cobalt::col_w_ks() does).
Proper binomial family. The reference code fit binary-response
SuperLearner models with gaussian(link = "logit"); psAve uses
binomial() throughout for treatment models.
No per-set scale(). The reference code standardized fitting and
prediction sets separately, an inconsistent transformation. psAve
passes raw covariates to all engines.
Strict complete-case alignment. The reference Fasmd applied
na.omit() to a covariate while using full-length treatment and weight
vectors, silently misaligning rows in the presence of missing data.
psAve refuses missing values in any used variable (error, never
drop), and names all returned score vectors by rownames(data).
Candidate propensity scores are clipped to clip (default
[0.01, 0.99], the paper's constants) before averaging. The average is
never re-clipped: a convex combination of values inside the clipping
interval cannot leave it.
The smd/prog selection criteria standardize all columns (including
binary ones) by the plain unweighted sample SD of the s.d.denom group –
the paper's convention (its reference Fasmd uses sd()), implemented by
passing bin.vars = FALSE for every column to cobalt::col_w_smd(). The
display-oriented balance component and bal.tab.psave() follow
cobalt's native conventions (binary columns use
). For criterion = "prog" the denominator is a
positive constant across the grid, so the selected
is invariant to this choice; the reported
criterion.value uses the paper's convention. For the KS criterion the
two conventions coincide on binary columns.
The closest existing functionality is WeightIt::method_super with
SL.method = "method.balance" (Pirracchio and Carone 2018): a
covariate-balance-targeted SuperLearner for weighting only. psAve
differs in targeting prognostic-score balance (Hansen 2008; Stuart,
Lee and Leacy 2013), in searching an exhaustive simplex grid with a
documented tie-break instead of a convex-optimization meta-learner, and in
returning a plain score vector equally usable for matching
(MatchIt::matchit(distance = )) and weighting. No other package
implements propensity score model averaging in the lineage of Xie et al.
(2019).
There is no seed argument (setting the global RNG inside a function is an
R anti-pattern): call set.seed() before psave() when stochastic
learners (ranger, xgboost) are among the candidates. The resolved
hyperparameters and engine package versions are recorded in
fit$info$learners.
Kabata D, Stuart EA, Shintani A (2024). Prognostic score-based model averaging approach for propensity score estimation. BMC Medical Research Methodology, 24, 228. doi:10.1186/s12874-024-02350-y
Hansen BB (2008). The prognostic analogue of the propensity score. Biometrika, 95(2), 481-488. doi:10.1093/biomet/asn004
Stuart EA, Lee BK, Leacy FP (2013). Prognostic score-based balance measures can be a useful diagnostic for propensity score methods in comparative effectiveness research. Journal of Clinical Epidemiology, 66(8), S84-S90. doi:10.1016/j.jclinepi.2013.01.013
Xie Y, Zhu Y, Cotton CA, Wu P (2019). A model averaging approach for estimating propensity scores by optimizing balance. Statistical Methods in Medical Research, 28(1), 84-101. doi:10.1177/0962280217715487
Pirracchio R, Carone M (2018). The Balance Super Learner: A robust adaptation of the Super Learner to improve estimation of the average treatment effect in the treated based on propensity score matching. Statistical Methods in Medical Research, 27(8), 2504-2518. doi:10.1177/0962280216682055
psave(), simplex_grid(), psave_criteria(),
vignette("method-details", "psAve")
Enumerates all points of the probability simplex
on a regular grid with
increment step, using integer arithmetic: with
steps, every integer composition with
and is listed and returned as
. This is the grid over which psave() searches for the
propensity score mixing weights and the prognostic mixing
weights .
simplex_grid(M, step = 0.05)simplex_grid(M, step = 0.05)
M |
Integer; the number of mixture components (grid columns). |
step |
Numeric; the grid increment. Must evenly divide 1 (checked in
integer arithmetic: with |
The number of grid points is exactly ; e.g.,
M = 4, step = 0.05 gives choose(23, 3) = 1771 points. Because the grid
is built from integer compositions, every valid point is present by
construction; the reference implementation of the paper instead filtered
expand.grid() rows with a floating-point rowSums(gr) == 1 test, which
silently dropped about 10.6% of the valid points for M = 4,
step = 0.05.
Enumeration order (the tie-breaking rule). Rows are generated by
recursive descent: runs from down to 0; within each value
of , runs from the remainder down to 0; and so on. The
first row is therefore and the last row is
. All grid searches in psave() resolve ties by
taking the first row attaining the minimum, within a 1e-9 relative
tolerance of the minimum, so ties favor learners listed earlier in
ps.methods / prog.methods. The tolerance is deliberate: criterion
values are computed with floating-point matrix algebra whose lowest-order
bits can differ across BLAS implementations, so an exact bitwise
which.min() would not be reproducible across machines, whereas the
tolerant first-minimum rule is.
A numeric matrix with rows and M
columns; each row sums to 1 exactly (in integer arithmetic before the
single final division by ).
Kabata D, Stuart EA, Shintani A (2024). Prognostic score-based model averaging approach for propensity score estimation. BMC Medical Research Methodology, 24, 228. doi:10.1186/s12874-024-02350-y
simplex_grid(2, step = 0.25) nrow(simplex_grid(4, step = 0.05)) # choose(23, 3) = 1771 # first row = all weight on the first component; last = on the last: head(simplex_grid(3, step = 0.25), 3) tail(simplex_grid(3, step = 0.25), 3)simplex_grid(2, step = 0.25) nrow(simplex_grid(4, step = 0.05)) # choose(23, 3) = 1771 # first row = all weight on the first component; last = on the last: head(simplex_grid(3, step = 0.25), 3) tail(simplex_grid(3, step = 0.25), 3)
Produces (a) the selected mixing-weight tables and
, (b) the diagnostics table (all four selection criteria for
every candidate propensity score and for the selected average – the "was
averaging worth it?" comparison), and (c) the full balance table (all
covariates plus the prognostic score; unweighted vs. weighted SMD and KS,
with a * marker at weighted SMD > 0.1).
## S3 method for class 'psave' summary(object, un = TRUE, candidates = TRUE, ...) ## S3 method for class 'summary.psave' print(x, digits = 3, ...)## S3 method for class 'psave' summary(object, un = TRUE, candidates = TRUE, ...) ## S3 method for class 'summary.psave' print(x, digits = 3, ...)
object |
A |
un |
If |
candidates |
If |
... |
Ignored. |
x |
A |
digits |
Number of significant digits to print. Default 3. |
For summary.psave(), an object of class "summary.psave": a
list with elements lambda, gamma, gamma.mse, diagnostics,
balance, criterion, criterion.value, prog.target, estimand,
average, nn, and call. print.summary.psave() returns x
invisibly.
psave(), print.psave(), bal.tab.psave()