Answers a different question from the other fitters: not "how well can we predict this person" but "how much did the treatment help, and for whom".
Usage
fit_effects(
data,
y,
treatment,
x,
id,
time = NULL,
scope = "both",
subgroup = NULL,
model = "auto",
estimator = "native",
treatment_type = "auto",
reference = NULL,
propensity = "auto",
trim = 0.05,
n_groups = 4L,
conf_level = 0.95,
test_prop = 0.3,
min_train = 10L,
min_test = 4L,
...
)Arguments
- data
Data frame.
- y
Outcome column name.
- treatment
Treatment column: two levels, several levels, or a dose.
- x
Predictors: names, numeric positions,
a:brange, formula, or data frame.- id
Person/unit ID column.
- time
Optional ordering column.
- scope
"both"(pooled + individual),"pooled","individual","subgroup", or"all"(pooled + subgroup + individual).- subgroup
Optional subgroup mapping: an
find_subgroups()result, a grouping column indata, or a named vector of labels per person.- model
Outcome model used for the response surfaces. Any model from
fit_ml();"auto"picks the simple linear or logistic model for the estimator in use.- estimator
Backend for the outcome and propensity models. See
fit_ml().- treatment_type
One of
"auto","binary","multiarm","continuous".- reference
Arm to contrast against, for a multi-arm treatment. Defaults to the first level.
- propensity
Model for the treatment given the predictors.
"auto"followsestimator. For a dose this is theE[T|X]regression.- trim
Propensity values are held inside
[trim, 1 - trim]so a near-zero denominator cannot dominate the score.- n_groups
Number of sorted effect groups for GATES.
- conf_level
Confidence level for the reported intervals.
- test_prop
Proportion of each person's ordered rows held out.
- min_train
Minimum complete training rows per person.
- min_test
Minimum complete held-out rows per person.
- ...
Passed to the outcome models, e.g.
lambdaork.
Value
An idiographic_effects object, which is also an idiographic_fit: the
usual accessors work, and effects() returns the effect table.
Details
In repeated-measures data the treatment usually varies within a person over
time, so a person-specific treatment effect is estimable. fit_effects()
reports one effect per scope: the pooled effect, the effect inside each
subgroup, and the effect for each individual.
Rows with a missing treatment are removed before temporal splitting. Missing
outcome or predictor values are handled by the shared complete-case split.
Treatment types
The treatment type is detected from the column and can be named outright
with treatment_type:
binaryTwo levels, or 0/1. Estimated by AIPW: two response surfaces and a propensity model are fitted on the training rows and applied to the held-out rows to build one doubly robust score per row. A wrong outcome model or a wrong propensity model can each be tolerated – not both. The reported effect is
ATE.multiarmThree or more unordered arms. The same AIPW score is built for every arm, with a one-versus-rest propensity, and each arm is contrasted against
reference. One set of effects is reported per contrast, distinguished by thecontrastcolumn.continuousA dose. AIPW does not apply – there are no two surfaces to difference – so the estimator is the partially linear double-ML (Robinson) score:
E[Y|X]andE[T|X]are fitted on the training rows and the effect is the regression of one held-out residual on the other. The reported effect isAPE, the change in the outcome per one unit of treatment. Read the note below on what it averages.
What APE averages
If the dose effect is the same for everyone, APE is that effect. If it
varies, APE is not the plain average E[tau(X)]: the partially linear
estimator returns the variance-weighted average
$$\frac{E[\mathrm{Var}(T \mid X)\,\tau(X)]}{E[\mathrm{Var}(T \mid X)]},$$
which leans towards the people whose dose varies most, because they carry the most information about its effect. The difference is not small: with residualized doses of -1, 1, -3, 3 and true effects of 0, 0, 10, 10, the plain average is 5 while this estimator returns 9.
This is the standard target of the partially linear model and it is the
right quantity for a summary. It is simply not the same question as "what
would the average person gain from one more unit". When the dose effect
varies – and GATES in the same output will tell you whether it does –
report it as a projection, not as a population mean.
A numeric treatment with more than two distinct values is read as a dose.
Pass treatment_type = "multiarm" to treat its values as unordered arms
instead.
A two-level non-numeric outcome is modelled on the probability scale, so the reported effect is a risk difference. A 0/1 numeric outcome is modelled as a linear probability, which estimates the same risk difference.
Reported effects
ATE/APEThe average treatment effect, or for a dose the average partial effect per unit.
GATES:g1..gKSorted effect groups. Rows are ranked by their predicted effect and cut into
n_groupsbins;g1is the least-helped bin andgKthe most-helped.GATES:top-bottomis their difference: if its confidence interval excludes zero, the treatment genuinely helps some more than others.BLP:heterogeneityThe slope of the held-out scores on the predicted effect. A significant slope means the predicted heterogeneity is real and not noise.
Examples
set.seed(1)
d <- data.frame(
id = rep(1:6, each = 40), day = rep(1:40, 6),
x1 = rnorm(240), x2 = rnorm(240)
)
d$drug <- rbinom(240, 1, 0.5)
# The drug helps people with a high x1 and does nothing for the rest.
d$mood <- 2 * d$drug * (d$x1 > 0) + 0.5 * d$x1 + rnorm(240, sd = 0.5)
fit <- fit_effects(d, y = "mood", treatment = "drug", x = c("x1", "x2"),
id = "id", time = "day", scope = "pooled")
effects(fit)
#> scope model estimator subject subgroup effect contrast n
#> 1 pooled linear native .all .all ATE 1 vs 0 72
#> 2 pooled linear native .all .all GATES:g1 1 vs 0 18
#> 3 pooled linear native .all .all GATES:g2 1 vs 0 18
#> 4 pooled linear native .all .all GATES:g3 1 vs 0 18
#> 5 pooled linear native .all .all GATES:g4 1 vs 0 18
#> 6 pooled linear native .all .all GATES:top-bottom 1 vs 0 36
#> 7 pooled linear native .all .all BLP:heterogeneity 1 vs 0 72
#> n_people estimate std_error conf_low conf_high statistic p_value
#> 1 6 0.84405642 0.2754044 0.13610681 1.5520060 3.06478888 0.0279491203
#> 2 6 0.02935751 0.3720129 -0.92693200 0.9856470 0.07891531 0.9401609542
#> 3 6 -0.12413935 0.3538335 -1.03369731 0.7854186 -0.35084114 0.7400058248
#> 4 6 1.10952302 0.4013986 0.07769502 2.1413510 2.76414260 0.0396394908
#> 5 6 2.36148449 0.4128149 1.30030995 3.4226590 5.72044362 0.0022831144
#> 6 6 2.33212698 0.2210167 1.76398534 2.9002686 10.55181110 0.0001320564
#> 7 6 1.14360096 0.2190990 0.58038914 1.7068128 5.21956347 0.0034111294
# A dose rather than a switch: the effect is reported per unit of dose.
d$dose <- round(runif(240, 0, 10), 1)
d$sleep <- 0.3 * d$dose + 0.5 * d$x1 + rnorm(240, sd = 0.5)
dose_fit <- fit_effects(d, y = "sleep", treatment = "dose",
x = c("x1", "x2"), id = "id", time = "day",
scope = "pooled")
effects(dose_fit, effect = "APE")
#> scope model estimator subject subgroup effect contrast n n_people
#> 1 pooled linear native .all .all APE per unit 72 6
#> estimate std_error conf_low conf_high statistic p_value
#> 1 0.3158625 0.0182937 0.2688371 0.362888 17.26619 1.193544e-05