Skip to content

Refutation

Refutation tests falsify the causal claim. If assumptions hold, these should not reject the null.

Available Methods

Method Description Null Hypothesis
placebo_treatment Replace treatment with random noise Effect = 0
placebo_outcome Replace outcome with random noise Effect = 0
random_common_cause Add random covariate to graph Effect unchanged
data_subset Re-estimate on random subset Effect unchanged
simulated_confounder Add synthetic unobserved confounder Effect robust
add_unobserved_confounder Rosenbaum bounds grid search Find critical Γ

Running Refutations

from causal_toolkit.wrappers import DoWhyWrapper
from causal_toolkit.core.base import RefutationMethod

dowhy = DoWhyWrapper(model)
dowhy.identify(IdentificationStrategy.BACKDOOR)
dowhy.estimate(EstimatorType.DOUBLY_ROBUST)

# All default tests
refutations = dowhy.refute()

# Custom selection
refutations = dowhy.refute([
    RefutationMethod.PLACEBO_TREATMENT,
    RefutationMethod.RANDOM_COMMON_CAUSE,
    RefutationMethod.DATA_SUBSET,
])

for r in refutations:
    status = "✗ REJECTED" if r.rejected else "✓ NOT REJECTED"
    print(f"{r.method.value}: {status} (p={r.p_value:.4f})")

Interpretation

Result Meaning
NOT REJECTED (p > 0.05) Good - robust to this challenge
REJECTED (p < 0.05) Warning - assumption may be violated

Specific Warnings

Rejected Test Possible Issue
placebo_treatment Bug in estimation, or treatment not well-defined
placebo_outcome Outcome definition issue
random_common_cause Sensitive to covariate inclusion
data_subset Outliers or influential observations
simulated_confounder Moderate unobserved confounding could explain effect

Sensitivity as Refutation

from causal_toolkit.analysis import SensitivityAnalyzer

analyzer = SensitivityAnalyzer(model)
result = analyzer.cinelli_hazlett(estimate, benchmark_covariate="income")

if result.conclusion_reversed:
    print("⚠ Confounding as strong as 'income' would reverse conclusion")
    print(f"Robustness Value = {result.robustness_value:.4f}")

Custom Parameters

refutations = dowhy.refute(
    methods=[RefutationMethod.DATA_SUBSET],
    subset_fraction=0.8,      # Keep 80% of data
    num_simulations=50
)

refutations = dowhy.refute(
    methods=[RefutationMethod.SIMULATED_CONFOUNDER],
    confounder_strength=0.3,  # Correlation with T and Y
    num_simulations=20
)

RefutationResult Structure

@dataclass
class RefutationResult:
    method: RefutationMethod          # Which test
    null_hypothesis: str              # What was tested
    test_statistic: float             # Test stat value
    p_value: float                    # p-value
    rejected: bool                    # p < 0.05
    details: Dict[str, Any]           # Full test output