Skip to content

Identification

How causal effects are identified from observed data.

Strategies

Strategy Assumptions Use Case
Backdoor No unobserved confounders Most common
Frontdoor Mediator blocks all paths, no confounder of M→Y When backdoor blocked
Instrumental Variable Z affects T, Z⊥Y given T,X Endogeneity / RCT non-compliance
Mediation No mediator-outcome confounders Mechanism analysis
Regression Discontinuity Cutoff assignment Sharp/fuzzy RD

Backdoor Criterion

A set of variables Z satisfies the backdoor criterion relative to (T, Y) if: 1. No node in Z is a descendant of T 2. Z blocks all backdoor paths between T and Y

from causal_toolkit.visualization import CausalGraphVisualizer

viz = CausalGraphVisualizer().from_graph_spec(
    edges=[("X", "T"), ("X", "Y"), ("T", "Y"), ("U", "T"), ("U", "Y")],
    treatment="T",
    outcome="Y",
    common_causes=["X"]
)

# Find valid adjustment sets
adjustment_sets = viz.find_adjustment_sets()
print(adjustment_sets)
# [['X']]  # X blocks backdoor path T <- U -> Y? No - U unobserved!
# Actually: backdoor path is T <- X -> Y (blocked by X)
# AND T <- U -> Y (unblocked - needs U observed)

If unobserved confounders exist → backdoor fails → need IV, frontdoor, or sensitivity analysis.

Frontdoor Criterion

When direct T→Y path is confounded but there's mediator M: - T causes M - M causes Y - All T→Y paths go through M - No confounder of M→Y (or observed)

pipeline:
  identification:
    strategy: frontdoor
    mediators: ["compliance"]
    treatment: "assignment"
    outcome: "outcome"

Instrumental Variable

Z → T → Y, with Z ⊥ Y | T, X - Relevance: Z correlates with T - Exclusion: Z affects Y only through T - Exchangeability: Z independent of confounders

# Two-stage least squares
from causal_toolkit.core.base import EstimatorType
estimate = dowhy.estimate(EstimatorType.TWO_STAGE_LS)

Checking Identification

# From CausalModel
model = CausalModel(...)
dowhy = DoWhyWrapper(model)
estimand = dowhy.identify(IdentificationStrategy.BACKDOOR)

# Check assumptions
print(estimand.assumptions.validate())
# [] = no violations, or list of warnings

# Graphical check
from causal_toolkit.visualization.graphs import CausalGraphVisualizer
viz = CausalGraphVisualizer().from_graph_spec(...)
print(viz.compute_do_calculus_steps())
# Shows step-by-step do-calculus derivation