Skip to contents

The two readings of a result, defined for the class new_ipw() constructs and for the pooled results pool_ipw() returns.

  • as_marginal() returns the result reporting the causal contrast estimates.

  • as_conditional() returns the result presenting the outcome model's coefficient surface.

Usage

as_marginal(x, ...)

as_conditional(x, ...)

Arguments

x

An ipw or ipw_pooled object. These generics dispatch on this argument.

...

Arguments passed to methods.

Value

x presenting the reading asked for. The methods on ipw change the effects field and nothing else, so every other field comes back as it went in, the covariance attached to estimates and the set of readings the result supports included. A result that does not support the reading asked for raises an error rather than returning one. The methods on ipw_pooled exchange estimates, pooling, and the recorded mode with the reading stored under alternate, so the reading that was presented is what the returned result stores there, and the components shared by both readings come back as they went in. A pooled result that does not carry the reading asked for raises an error rather than returning one.

Details

Both surfaces exist on an ipw result that supports both readings, so on one of those these generics record which one the result presents rather than computing anything. They set the effects field of the new_ipw() contract and read the readings field beside it, which makes them the supported way to move a result between the two readings: a caller writes as_conditional(res) rather than assigning to the field, and a result that does not support the reading asked for is refused rather than left recording one it has no surface for.

The methods on ipw hold for a result that supports both readings, which is what a result records unless the package that built it said otherwise. Asking such a result for either reading is always answerable: the methods never error on one, asking twice says what asking once said, and a result that goes out to the other reading and back is the result that went in.

A result that supports one reading is refused the other rather than moved to it. The readings field says which ones a result can present, and a fitting package records one of them when the other has no meaning for the analysis it ran. Asking for a reading outside that set raises an error of class causalgenerics_unsupported_reading_marginal or causalgenerics_unsupported_reading_conditional, and of the general class causalgenerics_unsupported_reading, which carries the reading asked for under effects and the set the result records under readings. The reading such a result does support is the no-op it is on a result carrying both.

A result built before the fields existed carries six or seven fields rather than eight, since the two were added one at a time. It reads as marginal where it records no mode, which is the mode every method produced then, and as supporting both readings where it records no set, which is what every result was assumed to support when none of them recorded otherwise.

The generics live here for the reason print() does. Two packages each registering as_conditional.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. There is no marginal or conditional reading of an object that is not an IPW result, so the default method signals an error rather than inventing one.

The readings a pooled result carries

pool_ipw() pools both readings of one set of results whenever it can compute both, and stores the one the call did not name whole, under the alternate component. The methods on ipw_pooled therefore swap which reading the result presents rather than setting a field: the pooled estimates, the pooling diagnostics, and the recorded mode move together, and the components that describe the pooled analyses rather than a reading of them stay where they are.

Where both readings were pooled, the properties above hold. Asking a pooled result for the reading it already presents gives that result back, and a result taken out to the other reading and back is the result that went in. Totality is what these methods cannot keep. A reading the pooling could not compute is recorded as unavailable rather than computed, and asking for it raises an error of class causalgenerics_pool_missing_surface_marginal or causalgenerics_pool_missing_surface_conditional, and of the general class causalgenerics_pool_missing_surface. That condition carries the reading under effects and, under reason, the refusal that reading raised when it was pooled, which is the same wording the caller would have seen from pool_ipw() had they asked for it there.

A result pooled before both readings were kept records no alternate at all. Asking such a result for the other reading is refused with the same two classes and a reason of NULL, and the message says that pooling the results again gives a result carrying both.

See also

new_ipw() for the result class and the field these generics set, and pool_ipw() for the pooled result and the reading it stores beside the one it presents.

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)
)

# Written out literally, in the shape the `ipw()` return contract documents.
estimates <- data.frame(
  effect = "rd",
  estimate = 0.199882,
  std.err = 0.092425,
  z = 2.1626,
  ci.lower = 0.018732,
  ci.upper = 0.381032,
  conf.level = 0.95,
  p.value = 0.030570
)

res <- new_ipw(
  estimand = "ate",
  wt_mod = glm(z ~ x, family = binomial(), data = dat),
  outcome_mod = glm(y ~ z, family = quasibinomial(), data = dat),
  estimates = estimates,
  se_method = "linearization",
  fit = NULL
)

# A method that names no mode reports marginal effects.
res$effects
#> [1] "marginal"

as_conditional(res)$effects
#> [1] "conditional"

# Asking for the reading a result already reports gives the result back, and
# the two readings round-trip.
identical(as_marginal(res), res)
#> [1] TRUE
identical(as_marginal(as_conditional(res)), res)
#> [1] TRUE

# A result that supports one reading is refused the other rather than moved
# to it.
marginal_only <- new_ipw(
  estimand = "ate",
  wt_mod = glm(z ~ x, family = binomial(), data = dat),
  outcome_mod = glm(y ~ z, family = quasibinomial(), data = dat),
  estimates = estimates,
  se_method = "linearization",
  fit = NULL,
  readings = "marginal"
)

try(as_conditional(marginal_only))
#> Error in as_conditional.ipw(marginal_only) : 
#>   This result supports the marginal reading only, so there is no conditional reading of it to report; the package that produced it records the readings it supports when it builds the result.

# Neither reading exists for an object that is not an IPW result.
try(as_marginal(1:3))
#> Error in as_marginal.default(1:3) : 
#>   No `as_marginal()` method for an object of class <integer>.