Skip to content

Commit

Permalink
Merge 4fbd86a into 5d4a8e9
Browse files Browse the repository at this point in the history
  • Loading branch information
olivierlabayle authored May 27, 2024
2 parents 5d4a8e9 + 4fbd86a commit 175e427
Show file tree
Hide file tree
Showing 16 changed files with 466 additions and 399 deletions.
32 changes: 22 additions & 10 deletions docs/src/user_guide/estimands.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,6 @@ statisticalΨ = ATE(
)
```

- Factorial Treatments

It is possible to generate a `ComposedEstimand` containing all linearly independent IATEs from a set of treatment values or from a dataset. For that purpose, use the `factorialEstimand` function.

## The Interaction Average Treatment Effect

- Causal Question:
Expand Down Expand Up @@ -182,13 +178,11 @@ statisticalΨ = IATE(

- Factorial Treatments

It is possible to generate a `ComposedEstimand` containing all linearly independent IATEs from a set of treatment values or from a dataset. For that purpose, use the `factorialEstimand` function.

## Composed Estimands
It is possible to generate a `JointEstimand` containing all linearly independent IATEs from a set of treatment values or from a dataset. For that purpose, use the `factorialEstimand` function.

As a result of Julia's automatic differentiation facilities, given a set of predefined estimands ``(\Psi_1, ..., \Psi_k)``, we can automatically compute an estimator for $f(\Psi_1, ..., \Psi_k)$. This is done via the `ComposedEstimand` type.
## Joint And Composed Estimands

For example, the difference in ATE for a treatment with 3 levels (0, 1, 2) can be defined as follows:
A `JointEstimand` is simply a list of one dimensional estimands that are grouped together. For instance for a treatment `T` taking three possible values ``(0, 1, 2)`` we can define the two following Average Treatment Effects and a corresponding `JointEstimand`:

```julia
ATE₁ = ATE(
Expand All @@ -201,5 +195,23 @@ ATE₂ = ATE(
treatment_values = (T = (control = 1, case = 2),),
treatment_confounders = [:W]
)
ATEdiff = ComposedEstimand(-, (ATE₁, ATE₂))
joint_estimand = JointEstimand(ATE₁, ATE₂)
```

You can easily generate joint estimands corresponding to Counterfactual Means, Average Treatment Effects or Average Interaction Effects by using the `factorialEstimand` function.

To estimate a joint estimand you can use any of the estimators defined in this package exactly as you would do it for a one dimensional estimand.

There are two main use cases for them that we now describe.

### Joint Testing

In some cases, like in factorial analyses where multiple versions of a treatment are tested, it may be of interest to know if any version of the versions has had an effect. This can be done via a Hotelling's T2 Test, which is simply a multivariate generalisation of the Student's T test. This is the default returned by the `significance_test` function provided in TMLE.jl and the result of the test is also printed to the REPL for any joint estimate.

### Composition

Once you have estimated a `JointEstimand` and have a `JointEstimate`, you may be interested to ask further questions. For instance whether two treatment versions have the same effect. This question is typically answered by testing if the difference in Average Treatment Effect is 0. Using the Delta Method and Julia's automatic differentiation, you don't need to explicitly define a semi-parametric estimator for it. You can simply call `compose`:

```julia
ATEdiff = compose(-, joint_estimate)
```
38 changes: 25 additions & 13 deletions docs/src/user_guide/estimation.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,18 +218,20 @@ nothing # hide

All nuisance functions have been reused, only the fluctuation is fitted!

## Composing Estimands
## Joint Estimands and Composition

By leveraging the multivariate Central Limit Theorem and Julia's automatic differentiation facilities, we can estimate any estimand which is a function of already estimated estimands. By default, TMLE.jl will use [Zygote](https://fluxml.ai/Zygote.jl/latest/) but since we are using [AbstractDifferentiation.jl](https://github.com/JuliaDiff/AbstractDifferentiation.jl) you can change the backend to your favorite AD system.
As explained in [Joint And Composed Estimands](@ref), a joint estimand is simply a collection of estimands. Here, we will illustrate that an Average Interaction Effect is also defined as a difference in partial Average Treatment Effects.

For instance, by definition of the ``IATE``, we should be able to retrieve:
More precisely, we would like to see if the left-hand side of this equation is equal to the right-hand side:

```math
IATE_{T_1=0 \rightarrow 1, T_2=0 \rightarrow 1} = ATE_{T_1=0 \rightarrow 1, T_2=0 \rightarrow 1} - ATE_{T_1=0, T_2=0 \rightarrow 1} - ATE_{T_1=0 \rightarrow 1, T_2=0}
```

For that, we need to define a joint estimand of three components:

```@example estimation
first_ate = ATE(
ATE₁ = ATE(
outcome=:Y,
treatment_values=(
T₁=(case=true, control=false),
Expand All @@ -239,9 +241,7 @@ first_ate = ATE(
T₂=[:W₂₁, :W₂₂],
),
)
first_ate_result, cache = tmle(first_ate, dataset, cache=cache, verbosity=0);
second_ate = ATE(
ATE₂ = ATE(
outcome=:Y,
treatment_values=(
T₁=(case=false, control=false),
Expand All @@ -251,15 +251,27 @@ second_ate = ATE(
T₂=[:W₂₁, :W₂₂],
),
)
second_ate_result, cache = tmle(second_ate, dataset, cache=cache, verbosity=0);
joint_estimand = JointEstimand(Ψ₃, ATE₁, ATE₂)
```

composed_iate_result = compose(
(x, y, z) -> x - y - z,
result₃, first_ate_result, second_ate_result
)
where the interaction `Ψ₃` was defined earlier. This joint estimand can be estimated like any other estimand using our estimator of choice:

```@example estimation
joint_estimate, cache = tmle(joint_estimand, dataset, cache=cache, verbosity=0);
joint_estimate
```

The printed output is the result of a Hotelling's T2 Test which is the multivariate counterpart of the Student's T Test. It tells us whether any of the component of this joint estimand is different from 0.

Then we can formally test our hypothesis by leveraging the multivariate Central Limit Theorem and Julia's automatic differentiation.

```@example estimation
composed_result = compose((x, y, z) -> x - y - z, joint_estimate)
isapprox(
estimate(result₄),
estimate(composed_iate_result),
first(estimate(composed_result)),
atol=0.1
)
```

By default, TMLE.jl will use [Zygote](https://fluxml.ai/Zygote.jl/latest/) but since we are using [AbstractDifferentiation.jl](https://github.com/JuliaDiff/AbstractDifferentiation.jl) you can change the backend to your favorite AD system.
6 changes: 3 additions & 3 deletions src/TMLE.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export CM, ATE, IATE
export AVAILABLE_ESTIMANDS
export factorialEstimand, factorialEstimands
export TMLEE, OSE, NAIVE
export ComposedEstimand
export JointEstimand, ComposedEstimand
export var, estimate, pvalue, confint, emptyIC
export significance_test, OneSampleTTest, OneSampleZTest, OneSampleHotellingT2Test
export compose
Expand All @@ -48,8 +48,8 @@ include("utils.jl")
include("scm.jl")
include("adjustment.jl")
include("estimands.jl")
include("estimators.jl")
include("estimates.jl")
include("estimators.jl")
include("treatment_transformer.jl")
include("estimand_ordering.jl")

Expand All @@ -61,6 +61,6 @@ include("counterfactual_mean_based/clever_covariate.jl")
include("counterfactual_mean_based/gradient.jl")

include("configuration.jl")

include("testing.jl")

end
Loading

0 comments on commit 175e427

Please sign in to comment.