Skip to content

Commit

Permalink
Merge pull request #106 from TARGENE/factoria_ates_iates
Browse files Browse the repository at this point in the history
generalize methods to factorialEstimand [BREAKING]
  • Loading branch information
olivierlabayle committed Mar 14, 2024
2 parents 69281c6 + b0e3158 commit 7f7b0a2
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 134 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "TMLE"
uuid = "8afdd2fb-6e73-43df-8b62-b1650cd9c8cf"
authors = ["Olivier Labayle"]
version = "0.14.2"
version = "0.15.0"

[deps]
AbstractDifferentiation = "c29ec348-61ec-40c8-8164-b8c60e9d9f3d"
Expand Down
4 changes: 2 additions & 2 deletions docs/src/user_guide/estimands.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ 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 `factorialATE` function.
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

Expand Down Expand Up @@ -182,7 +182,7 @@ 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 `factorialIATE` function.
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

Expand Down
2 changes: 1 addition & 1 deletion src/TMLE.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ using SplitApplyCombine
export SCM, StaticSCM, add_equations!, add_equation!, parents, vertices
export CM, ATE, IATE
export AVAILABLE_ESTIMANDS
export factorialATE, factorialIATE
export factorialEstimand, factorialEstimands
export TMLEE, OSE, NAIVE
export ComposedEstimand
export var, estimate, pvalue, confint, emptyIC
Expand Down
221 changes: 106 additions & 115 deletions src/counterfactual_mean_based/estimands.jl
Original file line number Diff line number Diff line change
Expand Up @@ -242,36 +242,20 @@ the number of estimands passing the positivity constraint.
unique_treatment_values(dataset, colnames) =
(;(colname => get_treatment_values(dataset, colname) for colname in colnames)...)

get_transitive_treatments_contrasts(treatments_unique_values) =
"""
Generated from transitive treatment switches to create independent estimands.
"""
get_treatment_settings(::Union{typeof(ATE), typeof(IATE)}, treatments_unique_values) =
[collect(zip(vals[1:end-1], vals[2:end])) for vals in values(treatments_unique_values)]

function generateFactorialEstimandFromContrasts(
constructor,
treatments_levels::NamedTuple{names},
outcome;
confounders=nothing,
outcome_extra_covariates=(),
freq_table=nothing,
positivity_constraint=nothing
) where names
treatments_contrasts = get_transitive_treatments_contrasts(treatments_levels)
components = []
for combo Iterators.product(treatments_contrasts...)
treatments_contrast = [NamedTuple{(:control, :case)}(treatment_control_case) for treatment_control_case combo]
Ψ = constructor(
outcome=outcome,
treatment_values=NamedTuple{names}(treatments_contrast),
treatment_confounders = confounders,
outcome_extra_covariates=outcome_extra_covariates
)
if satisfies_positivity(Ψ, freq_table; positivity_constraint=positivity_constraint)
push!(components, Ψ)
end
end
return ComposedEstimand(joint_estimand, Tuple(components))
end
get_treatment_settings(::typeof(CM), treatments_unique_values) =
values(treatments_unique_values)

get_treatment_setting(combo::Tuple{Vararg{Tuple}}) = [NamedTuple{(:control, :case)}(treatment_control_case) for treatment_control_case combo]

get_treatment_setting(combo) = collect(combo)

GENERATE_DOCSTRING = """
FACTORIAL_DOCSTRING = """
The components of this estimand are generated from the treatment variables contrasts.
For example, consider two treatment variables T₁ and T₂ each taking three possible values (0, 1, 2).
For each treatment variable, the marginal transitive contrasts are defined by (0 → 1, 1 → 2). Note that (0 → 2) or (1 → 0) need not
Expand All @@ -295,145 +279,152 @@ A `ComposedEstimand` with causal or statistical components.
If `nothing`, causal estimands are generated.
- `outcome_extra_covariates=()`: The generated components will inherit these `outcome_extra_covariates`.
- `positivity_constraint=nothing`: Only components that pass the positivity constraint are added to the `ComposedEstimand`
- `verbosity=1`: Verbosity level.
"""

"""
factorialATE(
treatments_levels::NamedTuple{names}, outcome;
factorialEstimand(
constructor::Union{typeof(ATE), typeof(IATE)},
treatments_levels::NamedTuple{names},
outcome;
confounders=nothing,
outcome_extra_covariates=(),
freq_table=nothing,
positivity_constraint=nothing
) where names
positivity_constraint=nothing,
verbosity=1
) where names
Generate a `ComposedEstimand` from `treatments_levels`. $FACTORIAL_DOCSTRING
Generate a `ComposedEstimand` of ATEs from the `treatments_levels`. $GENERATE_DOCSTRING
# Examples:
# Example:
Average Treatment Effects:
To generate a causal composed estimand with 3 components:
```@example
factorialEstimand(ATE, (T₁ = (0, 1), T₂=(0, 1, 2)), :Y₁)
```
```@example
factorialATE((T₁ = (0, 1), T₂=(0, 1, 2)), :Y₁)
factorial(ATE, (T₁ = (0, 1, 2), T₂=(0, 1, 2)), :Y₁, confounders=[:W₁, :W₂])
```
To generate a statistical composed estimand with 9 components:
Interactions:
```@example
factorialATE((T₁ = (0, 1, 2), T₂=(0, 1, 2)), :Y₁, confounders=[:W₁, :W₂])
factorialEstimand(IATE, (T₁ = (0, 1), T₂=(0, 1, 2)), :Y₁)
```
```@example
factorialEstimand(IATE, (T₁ = (0, 1, 2), T₂=(0, 1, 2)), :Y₁, confounders=[:W₁, :W₂])
"""
function factorialATE(
treatments_levels::NamedTuple{names}, outcome;
function factorialEstimand(
constructor::Union{typeof(CM), typeof(ATE), typeof(IATE)},
treatments_levels::NamedTuple{names},
outcome;
confounders=nothing,
outcome_extra_covariates=(),
freq_table=nothing,
positivity_constraint=nothing
positivity_constraint=nothing,
verbosity=1
) where names
return generateFactorialEstimandFromContrasts(
ATE,
treatments_levels,
outcome;
confounders=confounders,
outcome_extra_covariates=outcome_extra_covariates,
freq_table=freq_table,
positivity_constraint=positivity_constraint
)
treatments_settings = get_treatment_settings(constructor, treatments_levels)
components = []
for combo Iterators.product(treatments_settings...)
Ψ = constructor(
outcome=outcome,
treatment_values=NamedTuple{names}(get_treatment_setting(combo)),
treatment_confounders = confounders,
outcome_extra_covariates=outcome_extra_covariates
)
if satisfies_positivity(Ψ, freq_table; positivity_constraint=positivity_constraint)
push!(components, Ψ)
else
verbosity > 0 && @warn("Sub estimand", Ψ, " did not pass the positivity constraint, skipped.")
end
end
return ComposedEstimand(joint_estimand, Tuple(components))
end

"""
factorialATE(dataset, treatments, outcome;
confounders=nothing,
outcome_extra_covariates=(),
positivity_constraint=nothing
factorialEstimand(
constructor::Union{typeof(ATE), typeof(IATE)},
dataset, treatments, outcome;
confounders=nothing,
outcome_extra_covariates=(),
positivity_constraint=nothing,
verbosity=1
)
Find all unique values for each treatment variable in the dataset and generate all possible ATEs from these values.
Identifies `treatment_levels` from `dataset` and construct the
factorialEstimand from it.
"""
function factorialATE(dataset, treatments, outcome;
function factorialEstimand(
constructor::Union{typeof(CM), typeof(ATE), typeof(IATE)},
dataset, treatments, outcome;
confounders=nothing,
outcome_extra_covariates=(),
positivity_constraint=nothing
positivity_constraint=nothing,
verbosity=1
)
treatments_levels = unique_treatment_values(dataset, treatments)
freq_table = positivity_constraint !== nothing ? frequency_table(dataset, keys(treatments_levels)) : nothing
return factorialATE(
return factorialEstimand(
constructor,
treatments_levels,
outcome;
confounders=confounders,
outcome_extra_covariates=outcome_extra_covariates,
freq_table=freq_table,
positivity_constraint=positivity_constraint
positivity_constraint=positivity_constraint,
verbosity=verbosity
)
end

"""
factorialIATE(
treatments_levels::NamedTuple{names}, outcome;
confounders=nothing,
outcome_extra_covariates=(),
freq_table=nothing,
positivity_constraint=nothing
) where names
Generates a `ComposedEstimand` of IATE from `treatments_levels`. $GENERATE_DOCSTRING
# Example:
To generate a causal composed estimand with 3 components:
```@example
factorialIATE((T₁ = (0, 1), T₂=(0, 1, 2)), :Y₁)
```
To generate a statistical composed estimand with 9 components:
```@example
factorialIATE((T₁ = (0, 1, 2), T₂=(0, 1, 2)), :Y₁, confounders=[:W₁, :W₂])
```
"""
function factorialIATE(
treatments_levels::NamedTuple{names}, outcome;
factorialEstimands(
constructor::Union{typeof(ATE), typeof(IATE)},
dataset, treatments, outcomes;
confounders=nothing,
outcome_extra_covariates=(),
freq_table=nothing,
positivity_constraint=nothing
) where names
return generateFactorialEstimandFromContrasts(
IATE,
treatments_levels,
outcome;
confounders=confounders,
outcome_extra_covariates=outcome_extra_covariates,
freq_table=freq_table,
positivity_constraint=positivity_constraint
positivity_constraint=nothing,
verbosity=1
)
end
Identifies `treatment_levels` from `dataset` and a factorialEstimand
for each outcome in `outcomes`.
"""
factorialIATE(dataset, treatments, outcome;
confounders=nothing,
outcome_extra_covariates=(),
positivity_constraint=nothing
)
Finds treatments levels from the dataset and generates a `ComposedEstimand` of IATE from them
(see [`factorialIATE(treatments_levels, outcome; confounders=nothing, outcome_extra_covariates=())`](@ref)).
"""
function factorialIATE(dataset, treatments, outcome;
function factorialEstimands(
constructor::Union{typeof(CM), typeof(ATE), typeof(IATE)},
dataset, treatments, outcomes;
confounders=nothing,
outcome_extra_covariates=(),
positivity_constraint=nothing
positivity_constraint=nothing,
verbosity=1
)
estimands = []
treatments_levels = unique_treatment_values(dataset, treatments)
freq_table = positivity_constraint !== nothing ? frequency_table(dataset, keys(treatments_levels)) : nothing
return factorialIATE(
treatments_levels,
outcome;
confounders=confounders,
outcome_extra_covariates=outcome_extra_covariates,
freq_table=freq_table,
positivity_constraint=positivity_constraint
)
for outcome in outcomes
Ψ = factorialEstimand(
constructor,
treatments_levels,
outcome;
confounders=confounders,
outcome_extra_covariates=outcome_extra_covariates,
freq_table=freq_table,
positivity_constraint=positivity_constraint,
verbosity=verbosity-1
)
if length.args) > 0
push!(estimands, Ψ)
else
verbosity > 0 && @warn(string(
"ATE for outcome, ", outcome,
" has no component passing the positivity constraint, skipped."
))
end
end
return estimands
end

joint_levels::StatisticalIATE) = Iterators.product(values.treatment_values)...)
Expand Down
Loading

2 comments on commit 7f7b0a2

@olivierlabayle
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register

Release notes:

Add factorialEstimand and factorialEstimands for all CM, ATE and IATE.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/102854

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.15.0 -m "<description of version>" 7f7b0a2daec6c872241376429e3dd173d338df9b
git push origin v0.15.0

Please sign in to comment.