causalis.scenarios.gate.model

Utilities for group-level IRM estimands such as GATE and GATET.

The public entry points in this module estimate subgroup effects from a fitted binary-treatment IRM using pre-defined groups aligned to the fit-time sample.

Module Contents

Functions

estimate_gate_from_irm

Estimate Group Average Treatment Effects (GATEs) from a fitted IRM.

estimate_gatet_from_irm

Estimate Group Average Treatment Effects on the Treated (GATETs) from a fitted IRM.

API

causalis.scenarios.gate.model.estimate_gate_from_irm(irm_model: Any, groups: Optional[pandas.DataFrame | pandas.Series | numpy.ndarray], alpha: float = 0.05, cov_type: str = 'HC3', cov_kwds: Optional[Dict[str, Any]] = None) causalis.data_contracts.gate_estimate.GateEstimate

Estimate Group Average Treatment Effects (GATEs) from a fitted IRM.

Parameters

irm_model : fitted IRM Interactive Regression Model with stored cross-fitted nuisance predictions :math:\hat g_0(X), :math:\hat g_1(X), and :math:\hat m(X). The model must already be fitted. groups : Optional[pd.DataFrame, pd.Series, or np.ndarray] Pre-specified subgroup definition.

Accepted forms are:
- A single Series / one-column DataFrame of group labels.
- A multi-column dummy basis with binary indicators.
- A numpy array of group labels (assumed aligned with fit-time rows).

For strict GATE, groups must define a mutually exclusive and exhaustive
partition of the fitted sample. Each group must contain at least one
treated and one control observation.

GATE requires the fitted ``CausalData`` to define ``user_id``.
Group rows are aligned to the IRM fit-time sample using those fit-time
observation ids. In practice, ``groups.index`` must either exactly match
those ids or be a permutation of them. As a convenience, when the model
was fitted with ``user_id``, GATE also accepts groups indexed by the
original fit-time row index if the current row-to-``user_id`` mapping is
still unchanged.

If None, falls back to ``irm_model.data.gate_groups`` when present.

alpha : float, default 0.05 Significance level for two-sided confidence intervals. cov_type : {“HC0”, “HC1”, “HC2”, “HC3”}, default “HC3” Heteroskedasticity-robust covariance estimator used for subgroup inference. The implementation uses a closed-form saturated dummy-model covariance, equivalent to a no-intercept OLS regression on the GATE basis. cov_kwds : Optional[Dict[str, Any]], default None Additional covariance options requested by the caller. These are currently ignored because GATE uses the closed-form HCx covariance formulas implemented in this module.

Returns

GateEstimate Result contract containing subgroup effects, standard errors, Wald statistics, confidence intervals, covariance matrix, and group-level diagnostics. The returned object also supports contrast(...) and pairwise_summary(...) for formal post-estimation group comparisons.

Examples

import pandas as pd 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, … ) groups = pd.Series( … (data.df[“x1”] >= 0).map({True: “high_x1”, False: “low_x1”}), … index=data.df[“user_id”], … name=”segment”, … ) irm = IRM( … data=data, … ml_g=RandomForestRegressor(n_estimators=200, max_depth=6, random_state=3141), … ml_m=RandomForestClassifier(n_estimators=200, max_depth=6, random_state=3141), … n_folds=3, … random_state=3141, … ) irm.fit() gate = estimate_gate_from_irm(irm, groups=groups) gate.summary() # doctest: +SKIP

Notes

This implementation targets strict subgroup effects under the same unconfoundedness and overlap assumptions used by IRM, with an additional requirement that subgroup membership is pre-treatment.

Let :math:G denote a finite partition of the sample space and let :math:\phi(W; \hat\eta) be the cross-fitted doubly robust signal

.. math::

\phi =
\hat g_1(X) - \hat g_0(X)
+ (Y - \hat g_1(X)) \frac{D}{\hat m(X)}
- (Y - \hat g_0(X)) \frac{1-D}{1-\hat m(X)}.

For a subgroup :math:g, the target estimand is

.. math::

\theta_g = \mathbb{E}[\phi(W; \eta_0) \mid G=g].

With a mutually exclusive and exhaustive dummy basis, the estimator reduces to the groupwise sample mean of the orthogonal score:

.. math::

\hat\theta_g = \frac{1}{n_g} \sum_{i : G_i = g} \hat\phi_i.

Inference is computed from the equivalent saturated no-intercept linear regression of :math:\hat\phi on the subgroup dummies. Because the design is block-diagonal, the covariance matrix is diagonal and each subgroup variance is available in closed form under HC0/HC1/HC2/HC3.

normalize_ipw=True on the IRM is intentionally ignored for GATE so the estimator uses the canonical unnormalized orthogonal signal above.

causalis.scenarios.gate.model.estimate_gatet_from_irm(irm_model: Any, groups: Optional[pandas.DataFrame | pandas.Series | numpy.ndarray], alpha: float = 0.05, cov_type: str = 'HC3', cov_kwds: Optional[Dict[str, Any]] = None) causalis.data_contracts.gate_estimate.GateEstimate

Estimate Group Average Treatment Effects on the Treated (GATETs) from a fitted IRM.

Parameters

irm_model : fitted IRM Interactive Regression Model with stored cross-fitted nuisance predictions :math:\hat g_0(X), :math:\hat g_1(X), and :math:\hat m(X). The model must already be fitted. groups : Optional[pd.DataFrame, pd.Series, or np.ndarray] Pre-specified subgroup definition.

Accepted forms are:
- A single Series / one-column DataFrame of group labels.
- A multi-column dummy basis with binary indicators.
- A numpy array of group labels (assumed aligned with fit-time rows).

For strict GATET, groups must define a mutually exclusive and exhaustive
partition of the fitted sample. Each group must contain at least one
treated observation. Groups with no controls are still accepted for
GATET, but a warning is emitted because within-group overlap is
degenerate and identification relies on :math:`\hat g_0(X)` and
:math:`\hat m(X)` learned outside the subgroup.

GATET requires the fitted ``CausalData`` to define ``user_id``.
Group rows are aligned to the IRM fit-time sample using those fit-time
observation ids. In practice, ``groups.index`` must either exactly match
those ids or be a permutation of them. As a convenience, when the model
was fitted with ``user_id``, GATET also accepts groups indexed by the
original fit-time row index if the current row-to-``user_id`` mapping is
still unchanged.

If None, falls back to ``irm_model.data.gate_groups`` when present.

alpha : float, default 0.05 Significance level for two-sided confidence intervals. cov_type : {“HC0”, “HC1”, “HC2”, “HC3”}, default “HC3” Heteroskedasticity-robust covariance estimator used for subgroup inference. The implementation uses a closed-form subgroupwise sandwich covariance equivalent to a no-intercept regression on subgroup dummies scaled by within-group treated shares. cov_kwds : Optional[Dict[str, Any]], default None Additional covariance options requested by the caller. These are currently ignored because GATET uses the closed-form HCx covariance formulas implemented in this module.

Returns

GateEstimate Result contract containing subgroup-treated effects, standard errors, confidence intervals, covariance matrix, and group-level diagnostics. The returned object also supports contrast(...) and pairwise_summary(...) for formal post-estimation group comparisons.

Examples

import pandas as pd 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, … ) groups = pd.Series( … (data.df[“x1”] >= 0).map({True: “high_x1”, False: “low_x1”}), … index=data.df[“user_id”], … name=”segment”, … ) irm = IRM( … data=data, … ml_g=RandomForestRegressor(n_estimators=200, max_depth=6, random_state=3141), … ml_m=RandomForestClassifier(n_estimators=200, max_depth=6, random_state=3141), … n_folds=3, … random_state=3141, … ) irm.fit() gatet = estimate_gatet_from_irm(irm, groups=groups) gatet.summary() # doctest: +SKIP

Notes

This implementation targets subgroup ATT under the same unconfoundedness and overlap assumptions used by IRM, with the additional requirement that subgroup membership is pre-treatment.

Let :math:G denote a finite partition of the sample space. For subgroup :math:g, the target estimand is

.. math::

\theta_g^{\mathrm{GATET}}
= \mathbb{E}[Y(1)-Y(0)\mid G=g, D=1].

Equivalently, for a strict subgroup partition,

.. math::

\theta_g^{\mathrm{GATET}}
= \mathbb{E}\left[
    \frac{D \mathbf{1}\{G=g\}}{\mathbb{P}(D=1, G=g)}
    (Y(1)-Y(0))
  \right].

Using the fitted nuisances :math:\hat g_0(X) and :math:\hat m(X), define the ATT-style orthogonal signal

.. math::

z =
D \cdot (Y - \hat g_0(X))
- \hat m(X)(1-D)\frac{Y-\hat g_0(X)}{1-\hat m(X)}.

Then the subgroup ATT can be written as

.. math::

\hat\theta_g^{\mathrm{GATET}}
= \frac{1}{n_{\mathrm{treated}, g}}
  \sum_{i:G_i=g} z_i,

where :math:n_{\mathrm{treated}, g} is the number of treated observations in subgroup :math:g. In particular, the implementation changes the score itself to the subgroup-ATT signal above; it is not computed by first building ordinary GATE scores and then averaging them only over treated units in subgroup :math:g.

Inference is based on the subgroup moment residual

.. math::

u_{ig} = \mathbf{1}\{G_i=g\}\,(z_i - D_i \hat\theta_g),

with diagonal HC0/HC1/HC2/HC3 covariance formulas computed in closed form.

As a sanity check, when the groups form an exhaustive partition,

.. math::

\mathrm{ATTE}
= \sum_g \mathbb{P}(G=g \mid D=1)\,
  \theta_g^{\mathrm{GATET}}.

normalize_ipw=True on the IRM is intentionally ignored for GATET so the estimator uses the canonical unnormalized subgroup-ATT signal above.