Source code for dowhy.causal_identifier.identified_estimand
import copy
from typing import List, Optional
import sympy as sp
from dowhy.utils.api import parse_state
[docs]class IdentifiedEstimand:
"""Class for storing a causal estimand, typically as a result of the identification step."""
def __init__(
self,
identifier, # This field will be deprecated in the future
treatment_variable,
outcome_variable,
estimand_type=None,
estimands=None,
backdoor_variables=None,
instrumental_variables=None,
frontdoor_variables=None,
mediator_variables=None,
mediation_first_stage_confounders=None,
mediation_second_stage_confounders=None,
default_backdoor_id=None,
identifier_method=None,
no_directed_path=False,
):
self.identifier = identifier
self.treatment_variable = parse_state(treatment_variable)
self.outcome_variable = parse_state(outcome_variable)
self.backdoor_variables = backdoor_variables
self.instrumental_variables = parse_state(instrumental_variables)
self.frontdoor_variables = parse_state(frontdoor_variables)
self.mediator_variables = parse_state(mediator_variables)
self.mediation_first_stage_confounders = mediation_first_stage_confounders
self.mediation_second_stage_confounders = mediation_second_stage_confounders
self.estimand_type = estimand_type
self.estimands = estimands
self.default_backdoor_id = default_backdoor_id
self.identifier_method = identifier_method
self.no_directed_path = no_directed_path
[docs] def set_identifier_method(self, identifier_name: str):
self.identifier_method = identifier_name
[docs] def get_backdoor_variables(self, key: Optional[str] = None):
"""Return a list containing the backdoor variables.
If the calling estimator method is a backdoor method, return the
backdoor variables corresponding to its target estimand.
Otherwise, return the backdoor variables for the default backdoor estimand.
"""
if key is None:
if self.identifier_method and self.identifier_method.startswith("backdoor"):
return self.backdoor_variables[self.identifier_method]
elif self.backdoor_variables is not None and len(self.backdoor_variables) > 0:
return self.backdoor_variables[self.default_backdoor_id]
else:
return []
else:
return self.backdoor_variables[key]
[docs] def set_backdoor_variables(self, bdoor_variables_arr: List, key: Optional[str] = None):
if key is None:
key = self.identifier_method
self.backdoor_variables[key] = bdoor_variables_arr
[docs] def get_frontdoor_variables(self):
"""Return a list containing the frontdoor variables (if present)"""
return self.frontdoor_variables
[docs] def get_instrumental_variables(self):
"""Return a list containing the instrumental variables (if present)"""
return self.instrumental_variables
def __deepcopy__(self, memo):
return IdentifiedEstimand(
self.identifier, # not deep copied
copy.deepcopy(self.treatment_variable),
copy.deepcopy(self.outcome_variable),
estimand_type=copy.deepcopy(self.estimand_type),
estimands=copy.deepcopy(self.estimands),
backdoor_variables=copy.deepcopy(self.backdoor_variables),
instrumental_variables=copy.deepcopy(self.instrumental_variables),
frontdoor_variables=copy.deepcopy(self.frontdoor_variables),
mediator_variables=copy.deepcopy(self.mediator_variables),
default_backdoor_id=copy.deepcopy(self.default_backdoor_id),
identifier_method=copy.deepcopy(self.identifier_method),
)
def __str__(self, only_target_estimand: bool = False, show_all_backdoor_sets: bool = False):
if self.no_directed_path:
s = "No directed path from {0} to {1} in the causal graph.".format(
self.treatment_variable, self.outcome_variable
)
s += "\nCausal effect is zero."
return s
s = "Estimand type: {0}\n".format(self.estimand_type)
i = 1
has_valid_backdoor = sum("backdoor" in key for key in self.estimands.keys())
for k, v in self.estimands.items():
if show_all_backdoor_sets:
# Do not show backdoor key unless it is the only backdoor set.
if k == "backdoor" and has_valid_backdoor > 1:
continue
else:
# Just show the default backdoor set
if k.startswith("backdoor") and k != "backdoor":
continue
if only_target_estimand and k != self.identifier_method:
continue
s += "\n### Estimand : {0}\n".format(i)
s += "Estimand name: {0}".format(k)
if k == self.default_backdoor_id:
s += " (Default)"
s += "\n"
if v is None:
s += "No such variable(s) found!\n"
else:
sp_expr_str = sp.pretty(v["estimand"], use_unicode=True)
s += "Estimand expression:\n{0}\n".format(sp_expr_str)
j = 1
for ass_name, ass_str in v["assumptions"].items():
s += "Estimand assumption {0}, {1}: {2}\n".format(j, ass_name, ass_str)
j += 1
i += 1
return s