Model accessors for an inverse probability weighted result
Source:R/ipw-accessors.R
ipw-accessors.RdThe accessors a fitted model answers to, defined for the class new_ipw()
constructs.
coef()returns the effect estimates.vcov()returns the covariance of those estimates.confint()returns their confidence limits.nobs()returns the number of observations the outcome model was fitted on.df.residual()returns the residual degrees of freedom the fitted variance object reports.weights()returns the weights the outcome model was fitted with.model.frame()returns the outcome model's model frame.estimand()returns the estimand the weights targeted.
Usage
# S3 method for class 'ipw'
coef(object, ..., effects = NULL)
# S3 method for class 'ipw'
vcov(object, ..., effects = NULL)
# S3 method for class 'ipw'
confint(object, parm, level = 0.95, ..., effects = NULL)
# S3 method for class 'ipw'
nobs(object, ...)
# S3 method for class 'ipw'
df.residual(object, ...)
# S3 method for class 'ipw'
weights(object, ...)
# S3 method for class 'ipw'
model.frame(formula, ...)
# S3 method for class 'ipw'
estimand(x, ...)Arguments
- object
An
ipwobject.- ...
Further arguments. These methods ignore them.
- effects
The reading to report, either
"marginal"or"conditional".NULL, the default, reports the reading the result records; any other value overrides it for the one call and leaves the result as it is.- parm
The rows to report an interval for, given either as the labels of the reported surface or as their positions in it. Missing means all of them.
- level
The confidence level. At the level the result stores, its own limits are returned; at any other level they are recomputed.
- formula
An
ipwobject. Themodel.frame()generic in stats names its first argumentformula, and the method matches it.- x
An
ipwobject. Theestimand()generic names its first argumentx, and the method matches it.
Value
coef() returns a named numeric vector of effect estimates, one element per
row of the estimates frame, named by effect label. In the conditional
reading it returns the outcome model's coefficients as that model reports
them, names included.
vcov() returns the square numeric covariance matrix of those estimates,
with the effect labels as dimnames on both margins, exactly as the fitting
package attached it. A result that records no such matrix raises an error of
class causalgenerics_no_vcov rather than returning one built from the
standard errors, which would report the effects as uncorrelated. In the
conditional reading it returns the corrected covariance of the outcome
model's coefficients, in the order coef() reports them whatever order it was
attached in. An outcome model that was never wrapped raises an error of class
causalgenerics_no_conditional_vcov, a wrapped model that no longer carries
the covariance raises an error of class causalgenerics_no_vcov_ipw_model,
and a covariance whose labels cannot be paired with the coefficients raises an
error of class causalgenerics_conditional_vcov_mismatch.
confint() returns a matrix with one row per effect parm selects, in the
order parm gives them, and two columns holding the lower and upper limit.
The rows are named by effect label and the columns by the two tail
probabilities as percentages, the way the confint() methods in stats
name theirs. A character parm that names an effect the result does not
report raises an error of class causalgenerics_invalid_argument. In the
conditional reading the rows are the outcome model's coefficients, which
parm names and indexes in the same two ways, and the limits are the normal
ones built from the corrected covariance at every level, since the limits the
result stores belong to the effects the marginal reading reports.
coef(), vcov(), and confint() raise an error of class
causalgenerics_invalid_argument_effects when effects names neither
reading, and when the result's own field does.
nobs() returns a single integer, the number of observations the outcome
model was fitted on, which is the number the estimates were computed from and
not necessarily the number the weighting model saw.
df.residual() returns the residual degrees of freedom the fitted variance
object reports, as an integer when that number is a whole one an integer can
hold and as the double the object gave when it is not. A fractional count is
what a penalized or smooth fit spends, and it comes back as it stands rather
than truncated to a count the fit did not spend. NA_integer_ comes back
when the result records no fitted variance object or that object reports no
residual degrees of freedom.
weights() returns the outcome model's weights as its model frame stores
them, so a concrete weight class such as psw comes back as itself, or
NULL when the outcome model was fitted unweighted.
model.frame() returns the outcome model's model frame, which is the data
the reported estimates were computed from. The (weights) column a weighted
frame carries is deliberately dropped, since tooling that reads the columns
of a frame as the variables a model was fitted on, such as prediction and
averaging packages, would otherwise treat the estimation weights as one of
them; weights() is where those are reported. A model whose own method has
no frame to give raises that model's error rather than one of this package's.
estimand() returns the estimand the result records, which is the one the
weights the estimates were computed under targeted. There is deliberately no
estimand<-() method for a result: assigning a new estimand would relabel
those estimates rather than recompute them, so a caller who writes the
assignment is told that no method exists.
Details
These methods live here for the reason print() does. Two packages each
registering coef.ipw() would collide in the shared S3 method table, and a
caller writing against a result would then get whichever package was
installed last rather than the contract.
Everything they read is part of the new_ipw() contract: the estimates
frame, the ipw_vcov attribute attached to it, estimand, outcome_mod,
fit, and the effects field the section below describes.
They never branch on se_method, since the fields they read already hold the
result of whatever computation that names, and they reach into fit only
through ordinary S3 dispatch. A package whose variance object is a bare list
rather than a fitted model therefore uses them unchanged.
Rows are named by the effect labels the new_ipw() contract defines, so the
name coef() gives an estimate is the one vcov() and confint() use for
it and the one print() labels its row with.
The reading these methods report
coef(), vcov(), and confint() report the surface the result's effects
field names, and take an effects argument that names one for a single call.
The marginal reading is the causal contrast estimates, which is what these
methods reported before the field existed and what a result that records no
mode still reports. The conditional reading is the outcome model's
coefficient surface: coef() gives the model's coefficients, vcov() gives
their covariance, and confint() bounds them.
The covariance the conditional reading reports is the block of the joint
estimation that the fitting package attached with new_ipw_model(), never
the one the outcome model computed for itself. A model fitted as though its
weights were fixed understates its uncertainty, because the weights were
estimated from the same data, so there is nothing to fall back on when no
corrected block is there. vcov() and confint() refuse the reading instead,
and name which of the two ways the block can be missing they met. An outcome
model that was never wrapped raises an error of class
causalgenerics_no_conditional_vcov, which the package that produced the
result answers by wrapping the outcome model with new_ipw_model() before it
builds the result. An outcome model that carries the wrapper class with no
covariance behind it raises an error of class
causalgenerics_no_vcov_ipw_model, since a model that is already wrapped has
nothing to gain from being wrapped again and the object itself is what is
wrong. All of these carry the general class causalgenerics_no_vcov. coef()
needs no such block and reports the coefficients either way.
The block and the coefficients are paired by name, and the block is reported
in coefficient order whatever order it was attached in, so the variance read
beside a coefficient is that coefficient's. A block whose labels cannot be
paired with the coefficients raises an error of class
causalgenerics_conditional_vcov_mismatch. A block of another size, one
labeled with the parameter names of a stacked system, and a model whose
coefficients carry no names are the three ways the pairing fails. Reading such
a block by position instead would report the covariance of other parameters
under this model's coefficient names.
nobs(), df.residual(), weights(), model.frame(), and estimand()
describe the fit rather than a surface of it, so they answer the same way in
either reading and take no effects argument.
See also
new_ipw() for the result class and the fields these methods read.
Examples
dat <- data.frame(
x = rep(c(-1.5, -0.5, 0.5, 1.5), each = 5),
z = rep(c(0, 1), 10),
y = rep(c(0, 1, 1, 0, 1), 4)
)
ps <- fitted(glm(z ~ x, family = binomial(), data = dat))
wts <- ifelse(dat$z == 1, 1 / ps, 1 / (1 - ps))
# Written out literally, in the shape the `ipw()` return contract documents.
estimates <- data.frame(
effect = c("rd", "log(rr)"),
estimate = c(0.199882, 0.560414),
std.err = c(0.092425, 0.273519),
z = c(2.1626, 2.0489),
ci.lower = c(0.018732, 0.024326),
ci.upper = c(0.381032, 1.096502),
conf.level = 0.95,
p.value = c(0.030570, 0.040470)
)
# The covariance of the two effects, which a method attaches to the estimates
# it returns. Both are computed from the same weighted means, so the
# off-diagonal entry is far from zero.
attr(estimates, "ipw_vcov") <- matrix(
c(0.008542, 0.022753, 0.022753, 0.074813),
nrow = 2,
dimnames = list(estimates$effect, estimates$effect)
)
res <- new_ipw(
estimand = "ate",
wt_mod = glm(z ~ x, family = binomial(), data = dat),
outcome_mod = glm(y ~ z, family = quasibinomial(), data = dat, weights = wts),
estimates = estimates,
se_method = "linearization",
fit = NULL
)
coef(res)
#> rd log(rr)
#> 0.199882 0.560414
vcov(res)
#> rd log(rr)
#> rd 0.008542 0.022753
#> log(rr) 0.022753 0.074813
# At the stored level the stored limits come back.
confint(res)
#> 2.5 % 97.5 %
#> rd 0.018732 0.381032
#> log(rr) 0.024326 1.096502
# At any other level they are recomputed.
confint(res, parm = "rd", level = 0.9)
#> 5 % 95 %
#> rd 0.0478564 0.3519076
nobs(res)
#> [1] 20
# The linearization path records no fitted variance object.
df.residual(res)
#> [1] NA
head(weights(res))
#> [1] 1.785796 2.272594 1.785796 2.272594 1.785796 2.083669
# The data the estimates were computed from, without the `(weights)` column
# the fitting machinery stored beside the variables the formula named.
head(model.frame(res))
#> y z
#> 1 0 0
#> 2 1 1
#> 3 1 0
#> 4 0 1
#> 5 1 0
#> 6 0 1
estimand(res)
#> [1] "ate"
# The conditional reading reports the outcome model's coefficient surface
# rather than the effects.
coef(res, effects = "conditional")
#> (Intercept) z
#> 3.919028e-01 1.012353e-15
# Its covariance is the corrected block a fitting package attaches to the
# outcome model. This one carries none, and the covariance the model computed
# for itself is not a substitute: it treats the estimated weights as fixed.
try(vcov(res, effects = "conditional"))
#> Error in vcov.ipw(res, effects = "conditional") :
#> The conditional reading reports the covariance the joint estimation of the weights and the outcome implies, and this result's outcome model records none; the package that produced the result attaches one by wrapping the model with `new_ipw_model()`.
outcome_mod <- glm(y ~ z, family = quasibinomial(), data = dat, weights = wts)
# Standing in for the outcome block of a stacked sandwich, which a method
# computes from the estimating equations of both steps.
corrected <- vcov(outcome_mod) * 1.4
conditional <- as_conditional(new_ipw(
estimand = "ate",
wt_mod = glm(z ~ x, family = binomial(), data = dat),
outcome_mod = new_ipw_model(outcome_mod, corrected),
estimates = estimates,
se_method = "linearization",
fit = NULL
))
# A result that records the conditional reading answers in it with nothing
# named at the call site.
coef(conditional)
#> (Intercept) z
#> 3.919028e-01 1.012353e-15
vcov(conditional)
#> (Intercept) z
#> (Intercept) 0.646421 -0.646421
#> z -0.646421 1.292842
confint(conditional)
#> 2.5 % 97.5 %
#> (Intercept) -1.183914 1.967720
#> z -2.228542 2.228542