causalis.scenarios.unconfoundedness.refutation.unconfoundedness.sensitivity¶
Sensitivity analysis and benchmarking for unconfoundedness.
This module provides tools to assess the robustness of causal estimates to potential unobserved confounding. It implements the sensitivity framework based on partial $R^2$ as described in Cinelli & Hazlett (2020), adapted for non-linear and semi-parametric models via influence functions.
The framework assumes the existence of an unobserved confounder $U$ and quantifies the bias as a function of:
$R^2_{Y \sim U | D, X}$: How much of the outcome variance $U$ explains.
$R^2_{D \sim U | X}$: How much of the treatment variance $U$ explains.
The bias-aware estimate is then:
.. math::
\hat{\tau}_{adj} = \hat{\tau} \pm \text{bias}(R^2_{Y \sim U}, R^2_{D \sim U})
Module Contents¶
Functions¶
Compute DoubleML-style sensitivity elements for binary-treatment IRM. |
|
Compute bias-aware confidence intervals. |
|
Render a single, unified bias-aware summary string. |
|
Render a single, unified bias-aware summary string. |
|
Benchmark confounders one by one by refitting a short IRM that excludes each
requested confounder from the supplied |
|
Compute bias-aware components and cache them. |
|
Run sensitivity analysis and return a structured interpretation. |
Data¶
API¶
- causalis.scenarios.unconfoundedness.refutation.unconfoundedness.sensitivity.__all__¶
[‘sensitivity_analysis’, ‘sensitivity_benchmark’, ‘get_sensitivity_summary’, ‘interpret_sensitivity_…
- causalis.scenarios.unconfoundedness.refutation.unconfoundedness.sensitivity.compute_irm_sensitivity_elements(*, model: Any, y: numpy.ndarray, d: numpy.ndarray, g0: numpy.ndarray, g1: numpy.ndarray, m_hat: numpy.ndarray, w: Optional[numpy.ndarray] = None, w_bar: Optional[numpy.ndarray] = None, psi: Optional[numpy.ndarray] = None, inv_m: Optional[numpy.ndarray] = None, inv_1m: Optional[numpy.ndarray] = None, score: Any = 'ATE') dict[str, Any]¶
Compute DoubleML-style sensitivity elements for binary-treatment IRM.
- causalis.scenarios.unconfoundedness.refutation.unconfoundedness.sensitivity.compute_bias_aware_ci(effect_estimation: Dict[str, Any] | Any, *, r2_y: float, r2_d: float, rho: float = 1.0, H0: float = 0.0, alpha: float = 0.05, use_signed_rr: bool = False) Dict[str, Any]¶
Compute bias-aware confidence intervals.
Returns a dict with:
theta, se, alpha, z
sampling_ci
theta_bounds_cofounding = [theta_lower, theta_upper] = theta ± bound_width
bias_aware_ci = [theta - (bound_width + zse), theta + (bound_width + zse)]
max_bias_base, max_bias, bound_width and components (sigma2, nu2)
Parameters
effect_estimation : Dict[str, Any] or Any The effect estimation object. r2_y : float Sensitivity parameter for the outcome (R^2 form, R_Y^2). r2_d : float Sensitivity parameter for the treatment (R^2 form, R_D^2). rho : float, default 1.0 Correlation parameter. H0 : float, default 0.0 Null hypothesis for robustness values. alpha : float, default 0.05 Significance level. use_signed_rr : bool, default False Whether to use signed rr in the quadratic combination of sensitivity components. If True and m_alpha/rr are available, the bias bound is computed via the per-unit quadratic form and RV/RVa are not reported.
Returns
dict Dictionary with bias-aware results.
- causalis.scenarios.unconfoundedness.refutation.unconfoundedness.sensitivity.format_bias_aware_summary(res: Dict[str, Any], label: str | None = None) str¶
Render a single, unified bias-aware summary string.
Parameters
res : Dict[str, Any] The result dictionary from compute_bias_aware_ci. label : str, optional, default None The label for the estimand.
Returns
str Formatted summary string.
- causalis.scenarios.unconfoundedness.refutation.unconfoundedness.sensitivity.get_sensitivity_summary(effect_estimation: Dict[str, Any] | Any, *, label: Optional[str] = None) Optional[str]¶
Render a single, unified bias-aware summary string.
If bias-aware components are missing, shows a sampling-only variant with max_bias=0 and then formats via
format_bias_aware_summaryfor consistency.Parameters
effect_estimation : Dict[str, Any] or Any The effect estimation object. label : str, optional, default None The label for the estimand.
Returns
Optional[str] Formatted summary string or None if extraction fails.
Notes
The summary reports the point estimate, standard error, and confidence intervals under both the null (no unobserved confounding) and the assumed level of confounding ($R^2_Y, R^2_D$).
It also includes the Robustness Value (RV), which is the minimum strength of confounding ($R^2_Y = R^2_D$) required to change the conclusion (e.g., make the estimate non-significant or zero).
Examples
from causalis.scenarios.unconfoundedness.refutation.unconfoundedness import sensitivity_analysis, get_sensitivity_summary
Assuming ‘estimate’ is a fitted CausalEstimate from IRM
res = sensitivity_analysis(estimate, r2_y=0.05, r2_d=0.05) # doctest: +SKIP summary = get_sensitivity_summary(estimate) # doctest: +SKIP print(summary) # doctest: +SKIP
- causalis.scenarios.unconfoundedness.refutation.unconfoundedness.sensitivity.sensitivity_benchmark(effect_estimation: Dict[str, Any] | Any, data: causalis.dgp.causaldata.CausalData, benchmarking_set: List[str] | Literal[all], fit_args: Optional[Dict[str, Any]] = None) pandas.DataFrame¶
Benchmark confounders one by one by refitting a short IRM that excludes each requested confounder from the supplied
CausalData.This function intentionally performs a genuine short-model refit for every benchmarked confounder because
theta_shortanddeltaare defined by that re-estimation step. The residual-based strengths alone are not enough to recover those values.Parameters
effect_estimation : dict or Any Estimate/model container exposing a fitted IRM-like model. data : CausalData The causal dataset used for benchmarking. It must match the fitted long model on treatment, outcome, confounders, and row order. benchmarking_set : list[str] or “all” Confounders to benchmark one by one. Passing
"all"benchmarks every confounder indata.confoundersin that order. fit_args : dict, optional Additional keyword arguments passed toIRM.estimate(...)on each short model. Ifscoreis omitted, ATE/ATTE is inferred from the supplied estimate/model, and defaults to ATE. Ifstore_diagnosticsor legacydiagnostic_datais omitted, the short benchmark refits usestore_diagnostics=Falseby default.Returns
pandas.DataFrame A long-form DataFrame with one row per benchmarked confounder and columns
benchmark_confounder,r2_y,r2_d,rho,theta_long,theta_short, anddelta.Notes
Benchmarking allows the user to judge the plausibility of unobserved confounding by comparing it to the strength of observed confounders. For each confounder $X_k$, we calculate:
$R^2_{Y \sim X_k | D, X_{-k}}$: The partial $R^2$ of the outcome on $X_k$.
$R^2_{D \sim X_k | X_{-k}}$: The partial $R^2$ of the treatment on $X_k$.
These values can then be used as $R^2_Y$ and $R^2_D$ in
sensitivity_analysis.Examples
from causalis.dgp import obs_linear_26_dataset from causalis.scenarios.unconfoundedness.model import IRM from causalis.scenarios.unconfoundedness.refutation import sensitivity_benchmark
1. Fit a model
data = obs_linear_26_dataset(n=1000, seed=42, return_causal_data=True) irm = IRM(data=data).fit() estimate = irm.estimate()
2. Benchmark specific confounders
benchmarks = sensitivity_benchmark(estimate, data, benchmarking_set=[‘x1’, ‘x2’]) # doctest: +SKIP print(benchmarks[[‘benchmark_confounder’, ‘r2_y’, ‘r2_d’]]) # doctest: +SKIP
- causalis.scenarios.unconfoundedness.refutation.unconfoundedness.sensitivity.sensitivity_analysis(effect_estimation: Dict[str, Any] | Any, *, r2_y: float, r2_d: float, rho: float = 1.0, H0: float = 0.0, alpha: float = 0.05, use_signed_rr: bool = False) Dict[str, Any]¶
Compute bias-aware components and cache them.
This function turns a fitted estimate into a simple hidden-confounding stress test. In the default mode, the bound width is
.. math::
|\rho| \cdot \sqrt{\sigma^2 \nu^2} \cdot \sqrt{\frac{r2_y}{1-r2_y}\frac{r2_d}{1-r2_d}},so the reported confounding interval is
.. math::
[\theta - \text{bound\_width}, \theta + \text{bound\_width}].Here :math:
r2_ycontrols how much residual outcome variation an omitted confounder could explain, :math:r2_ddoes the same for treatment assignment, and :math:\rhosets the sign and strength alignment between the two channels.Parameters
effect_estimation : Dict[str, Any] or Any The effect estimation object. r2_y : float Sensitivity parameter for the outcome (R^2 form, R_Y^2; converted to odds form internally). r2_d : float Sensitivity parameter for the treatment (R^2 form, R_D^2). rho : float, default 1.0 Correlation parameter. H0 : float, default 0.0 Null hypothesis for robustness values. alpha : float, default 0.05 Significance level. use_signed_rr : bool, default False Whether to use signed rr in the quadratic combination of sensitivity components. If True and m_alpha/rr are available, the bias bound is computed via the per-unit quadratic form and RV/RVa are not reported.
Returns
dict Dictionary with bias-aware results: - theta, se, alpha, z - sampling_ci - theta_bounds_cofounding = (theta - bound_width, theta + bound_width) - bias_aware_ci = faithful CI for the bounds - max_bias and components (sigma2, nu2) - params (r2_y, r2_d, rho, use_signed_rr)
Examples
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor from causalis.dgp import obs_linear_26_dataset from causalis.scenarios.unconfoundedness.model import IRM data = obs_linear_26_dataset( … n=1000, … seed=3141, … include_oracle=False, … return_causal_data=True, … ) irm = IRM( … data=data, … ml_g=RandomForestRegressor( … n_estimators=200, … max_depth=6, … min_samples_leaf=5, … random_state=3141, … ), … ml_m=RandomForestClassifier( … n_estimators=200, … max_depth=6, … min_samples_leaf=5, … random_state=3141, … ), … n_folds=3, … random_state=3141, … ) estimate = irm.fit().estimate(score=”ATE”) out = sensitivity_analysis(estimate, r2_y=0.02, r2_d=0.02, rho=1.0) out[“theta_bounds_cofounding”] # doctest: +SKIP out[“bias_aware_ci”] # doctest: +SKIP
- causalis.scenarios.unconfoundedness.refutation.unconfoundedness.sensitivity.interpret_sensitivity_analysis(effect_estimation: Dict[str, Any] | Any, *, r2_y: float, r2_d: float, rho: float = 1.0, H0: float = 0.0, alpha: float = 0.05, use_signed_rr: bool = False) Dict[str, Any]¶
Run sensitivity analysis and return a structured interpretation.
Parameters
effect_estimation : Dict[str, Any] or Any The effect estimation object. r2_y : float Sensitivity parameter for outcome residual confounding strength. r2_d : float Sensitivity parameter for treatment residual confounding strength. rho : float, default 1.0 Correlation parameter for unobserved confounding. H0 : float, default 0.0 Null hypothesis used for significance checks. alpha : float, default 0.05 Significance level. use_signed_rr : bool, default False Whether to use signed rr in the quadratic sensitivity combination.
Returns
Dict[str, Any] Dictionary with: - raw: the output of
sensitivity_analysis(...)- interpretation: machine-readable interpretation fields - summary: compact human-readable interpretationNotes
This function wraps
sensitivity_analysisand provides a textual interpretation of the results, including whether the estimate remains significant under the assumed confounding.Examples
from causalis.scenarios.unconfoundedness.refutation.unconfoundedness import interpret_sensitivity_analysis
Assuming ‘estimate’ is a fitted CausalEstimate
interpretation = interpret_sensitivity_analysis(estimate, r2_y=0.01, r2_d=0.01) # doctest: +SKIP print(interpretation[“summary”]) # doctest: +SKIP