def construct_mediation_estimand(self, estimand_type, treatment_name, outcome_name, mediators_names): # TODO: support multivariate treatments better. expr = None if estimand_type in (CausalIdentifier.NONPARAMETRIC_NDE, CausalIdentifier.NONPARAMETRIC_NIE): outcome_name = outcome_name[0] sym_outcome = spstats.Normal(outcome_name, 0, 1) sym_treatment_symbols = [ spstats.Normal(t, 0, 1) for t in treatment_name ] sym_treatment = sp.Array(sym_treatment_symbols) sym_mediators_symbols = [ sp.Symbol(inst) for inst in mediators_names ] sym_mediators = sp.Array(sym_mediators_symbols) sym_outcome_derivative = sp.Derivative(sym_outcome, sym_mediators) sym_treatment_derivative = sp.Derivative(sym_mediators, sym_treatment) # For direct effect num_expr_str = outcome_name if len(mediators_names) > 0: num_expr_str += "|" + ",".join(mediators_names) sym_mu = sp.Symbol("mu") sym_sigma = sp.Symbol("sigma", positive=True) sym_conditional_outcome = spstats.Normal(num_expr_str, sym_mu, sym_sigma) sym_directeffect_derivative = sp.Derivative( sym_conditional_outcome, sym_treatment) if estimand_type == CausalIdentifier.NONPARAMETRIC_NIE: sym_effect = spstats.Expectation(sym_treatment_derivative * sym_outcome_derivative) elif estimand_type == CausalIdentifier.NONPARAMETRIC_NDE: sym_effect = spstats.Expectation(sym_directeffect_derivative) sym_assumptions = { "Mediation": ("{2} intercepts (blocks) all directed paths from {0} to {1} except the path {{{0}}}\N{RIGHTWARDS ARROW}{{{1}}}." ).format(",".join(treatment_name), ",".join(outcome_name), ",".join(mediators_names)), "First-stage-unconfoundedness": (u"If U\N{RIGHTWARDS ARROW}{{{0}}} and U\N{RIGHTWARDS ARROW}{{{1}}}" " then P({1}|{0},U) = P({1}|{0})").format( ",".join(treatment_name), ",".join(mediators_names)), "Second-stage-unconfoundedness": (u"If U\N{RIGHTWARDS ARROW}{{{2}}} and U\N{RIGHTWARDS ARROW}{1}" " then P({1}|{2}, {0}, U) = P({1}|{2}, {0})").format( ",".join(treatment_name), outcome_name, ",".join(mediators_names)) } else: raise ValueError( "Estimand type not supported. Supported estimand types are {0} or {1}'." .format(CausalIdentifier.NONPARAMETRIC_NDE, CausalIdentifier.NONPARAMETRIC_NIE)) estimand = {'estimand': sym_effect, 'assumptions': sym_assumptions} return estimand
def construct_symbolic_estimator(self, estimand): sym_outcome = (spstats.Normal(",".join(estimand.outcome_variable), 0, 1)) sym_treatment = (spstats.Normal(",".join(estimand.treatment_variable), 0, 1)) sym_instrument = sp.Symbol(estimand.instrumental_variables[0]) sym_outcome_derivative = sp.Derivative(sym_outcome, sym_instrument) sym_treatment_derivative = sp.Derivative(sym_treatment, sym_instrument) sym_effect = (spstats.Expectation(sym_outcome_derivative) / sp.stats.Expectation(sym_treatment_derivative)) estimator_assumptions = { "treatment_effect_homogeneity": ("Each unit's treatment {0} is".format(self._treatment_name) + "affected in the same way by common causes of " "{0} and {1}".format(self._treatment_name, self._outcome_name)), "outcome_effect_homogeneity": ("Each unit's outcome {0} is".format(self._outcome_name) + "affected in the same way by common causes of " "{0} and {1}".format(self._treatment_name, self._outcome_name)), } sym_assumptions = { **estimand.estimands["iv"]["assumptions"], **estimator_assumptions } symbolic_estimand = RealizedEstimand(estimand, estimator_name="Wald Estimator") symbolic_estimand.update_assumptions(sym_assumptions) symbolic_estimand.update_estimand_expression(sym_effect) return symbolic_estimand
def construct_backdoor_estimand(self, estimand_type, treatment_name, outcome_name, common_causes): # TODO: outputs string for now, but ideally should do symbolic # expressions Mon 19 Feb 2018 04:54:17 PM DST expr = None if estimand_type == "ate": num_expr_str = outcome_name + "|" num_expr_str += ",".join(common_causes) expr = "d(" + num_expr_str + ")/d" + treatment_name sym_mu = sp.Symbol("mu") sym_sigma = sp.Symbol("sigma", positive=True) sym_outcome = spstats.Normal(num_expr_str, sym_mu, sym_sigma) # sym_common_causes = [sp.stats.Normal(common_cause, sym_mu, sym_sigma) for common_cause in common_causes] sym_treatment = sp.Symbol(treatment_name) sym_conditional_outcome = spstats.Expectation(sym_outcome) sym_effect = sp.Derivative(sym_conditional_outcome, sym_treatment) sym_assumptions = { 'Unconfoundedness': ( u"If U\N{RIGHTWARDS ARROW}{0} and U\N{RIGHTWARDS ARROW}{1}" " then P({1}|{0},{2},U) = P({1}|{0},{2})" ).format(treatment_name, outcome_name, ",".join(common_causes)) } estimand = { 'estimand': sym_effect, 'assumptions': sym_assumptions } return estimand
def construct_iv_estimand(self, estimand_type, treatment_name, outcome_name, instrument_names): expr = None if estimand_type == "ate": sym_outcome = spstats.Normal(outcome_name, 0, 1) sym_treatment = spstats.Normal(treatment_name, 0, 1) sym_instrument = sp.Symbol(instrument_names[0]) # ",".join(instrument_names)) sym_outcome_derivative = sp.Derivative(sym_outcome, sym_instrument) sym_treatment_derivative = sp.Derivative(sym_treatment, sym_instrument) sym_effect = spstats.Expectation(sym_outcome_derivative / sym_treatment_derivative) sym_assumptions = { "As-if-random": ( "If U\N{RIGHTWARDS ARROW}\N{RIGHTWARDS ARROW}{0} then " "\N{NOT SIGN}(U \N{RIGHTWARDS ARROW}\N{RIGHTWARDS ARROW}{1})" ).format(outcome_name, ",".join(instrument_names)), "Exclusion": ( u"If we remove {{{0}}}\N{RIGHTWARDS ARROW}{1}, then " u"\N{NOT SIGN}({0}\N{RIGHTWARDS ARROW}{2})" ).format(",".join(instrument_names), treatment_name, outcome_name) } estimand = { 'estimand': sym_effect, 'assumptions': sym_assumptions } return estimand
def construct_iv_estimand(self, estimand_type, treatment_name, outcome_name, instrument_names): # TODO: support multivariate treatments better. expr = None outcome_name = outcome_name[0] sym_outcome = spstats.Normal(outcome_name, 0, 1) sym_treatment_symbols = [ spstats.Normal(t, 0, 1) for t in treatment_name ] sym_treatment = sp.Array(sym_treatment_symbols) sym_instrument_symbols = [sp.Symbol(inst) for inst in instrument_names] sym_instrument = sp.Array( sym_instrument_symbols) # ",".join(instrument_names)) sym_outcome_derivative = sp.Derivative(sym_outcome, sym_instrument) sym_treatment_derivative = sp.Derivative(sym_treatment, sym_instrument) sym_effect = spstats.Expectation(sym_outcome_derivative / sym_treatment_derivative) sym_assumptions = { "As-if-random": ("If U\N{RIGHTWARDS ARROW}\N{RIGHTWARDS ARROW}{0} then " "\N{NOT SIGN}(U \N{RIGHTWARDS ARROW}\N{RIGHTWARDS ARROW}{{{1}}})" ).format(outcome_name, ",".join(instrument_names)), "Exclusion": (u"If we remove {{{0}}}\N{RIGHTWARDS ARROW}{{{1}}}, then " u"\N{NOT SIGN}({{{0}}}\N{RIGHTWARDS ARROW}{2})").format( ",".join(instrument_names), ",".join(treatment_name), outcome_name) } estimand = {'estimand': sym_effect, 'assumptions': sym_assumptions} return estimand
def construct_backdoor_estimand(self, estimand_type, treatment_name, outcome_name, common_causes): # TODO: outputs string for now, but ideally should do symbolic # expressions Mon 19 Feb 2018 04:54:17 PM DST # TODO Better support for multivariate treatments expr = None outcome_name = outcome_name[0] num_expr_str = outcome_name if len(common_causes) > 0: num_expr_str += "|" + ",".join(common_causes) expr = "d(" + num_expr_str + ")/d" + ",".join(treatment_name) sym_mu = sp.Symbol("mu") sym_sigma = sp.Symbol("sigma", positive=True) sym_outcome = spstats.Normal(num_expr_str, sym_mu, sym_sigma) sym_treatment_symbols = [sp.Symbol(t) for t in treatment_name] sym_treatment = sp.Array(sym_treatment_symbols) sym_conditional_outcome = spstats.Expectation(sym_outcome) sym_effect = sp.Derivative(sym_conditional_outcome, sym_treatment) sym_assumptions = { 'Unconfoundedness': (u"If U\N{RIGHTWARDS ARROW}{{{0}}} and U\N{RIGHTWARDS ARROW}{1}" " then P({1}|{0},{2},U) = P({1}|{0},{2})").format( ",".join(treatment_name), outcome_name, ",".join(common_causes)) } estimand = {'estimand': sym_effect, 'assumptions': sym_assumptions} return estimand
def construct_frontdoor_estimand(self, estimand_type, treatment_name, outcome_name, frontdoor_variables_names): # TODO: support multivariate treatments better. expr = None outcome_name = outcome_name[0] sym_outcome = spstats.Normal(outcome_name, 0, 1) sym_treatment_symbols = [spstats.Normal(t, 0, 1) for t in treatment_name] sym_treatment = sp.Array(sym_treatment_symbols) sym_frontdoor_symbols = [sp.Symbol(inst) for inst in frontdoor_variables_names] sym_frontdoor = sp.Array(sym_frontdoor_symbols) # ",".join(instrument_names)) sym_outcome_derivative = sp.Derivative(sym_outcome, sym_frontdoor) sym_treatment_derivative = sp.Derivative(sym_frontdoor, sym_treatment) sym_effect = spstats.Expectation(sym_treatment_derivative * sym_outcome_derivative) sym_assumptions = { "Full-mediation": ( "{2} intercepts (blocks) all directed paths from {0} to {1}." ).format(",".join(treatment_name), ",".join(outcome_name), ",".join(frontdoor_variables_names)), "First-stage-unconfoundedness": ( u"If U\N{RIGHTWARDS ARROW}{{{0}}} and U\N{RIGHTWARDS ARROW}{{{1}}}" " then P({1}|{0},U) = P({1}|{0})" ).format(",".join(treatment_name), ",".join(frontdoor_variables_names)), "Second-stage-unconfoundedness": ( u"If U\N{RIGHTWARDS ARROW}{{{2}}} and U\N{RIGHTWARDS ARROW}{1}" " then P({1}|{2}, {0}, U) = P({1}|{2}, {0})" ).format(",".join(treatment_name), outcome_name, ",".join(frontdoor_variables_names)) } estimand = { 'estimand': sym_effect, 'assumptions': sym_assumptions } return estimand