metacausal.CausalEnsemble¶
- class metacausal.CausalEnsemble(methods=None, aggregation='median', outcome_type='auto')[source]¶
Bases:
objectEnsemble of causal estimators with robust aggregation.
Orchestrates multiple causal ML methods and aggregates their treatment effect estimates. The median is the default aggregation rule, providing a 50% breakdown point against catastrophic failure of individual methods.
- Parameters:
methods (list | None) –
List of causal estimators. Accepts:
Objects implementing
CausalEstimatorprotocolEconML estimator instances (auto-detected and wrapped)
DoubleMLAdapterinstances (explicit)GenericATEAdapterorGenericCATEAdapterinstancesLists/tuples of 2 or 3 callables, auto-wrapped as
GenericCATEAdapter
If
None, usesdefault_methods()— and which pool it returns depends on the outcome type detected at fit time.aggregation (str | AggregationStrategy) – Aggregation strategy. Either a string alias (
"median","mean","trimmed_mean","cba") or a strategy object implementingAggregationStrategy.outcome_type (str) – How to interpret Y.
"auto"(default) detects from the value set of Y at fit time (metacausal.infer_outcome_type()): Y ⊆ {0, 1} or boolean is binary, otherwise continuous."continuous"and"binary"force the corresponding interpretation."binary"with non-binary Y raises;"continuous"with binary Y is silently accepted (a “give me the linear- probability ATE” request).
Outcome-type lifecycle: at fit time the ensemble resolves the outcome type from Y, materialises the right default pool if no methods were supplied, drops any component whose
supported_outcome_typesdoes not include the detected type (with one summary warning naming the drops), and validates each survivor’s nuisance configuration. For binary outcomes the estimand is the risk difference.Examples
Fit two components and compute the ensemble ATE with a full-pipeline bootstrap.
estimate()(not shown) offers a one-call fit + ate/bootstrap convenience wrapper.>>> from metacausal import CausalEnsemble >>> from metacausal.datasets import load_lalonde >>> from metacausal.adapters import DoubleMLAdapter >>> from econml.dml import CausalForestDML >>> from doubleml import DoubleMLIRM >>> from sklearn.ensemble import ( ... HistGradientBoostingClassifier, HistGradientBoostingRegressor, ... ) >>> X, T, Y = load_lalonde() >>> ens = CausalEnsemble([ ... CausalForestDML( ... model_y=HistGradientBoostingRegressor(max_iter=50), ... model_t=HistGradientBoostingClassifier(max_iter=50), ... discrete_treatment=True, ... n_estimators=50, ... n_jobs=1, ... ), ... DoubleMLAdapter( ... DoubleMLIRM, ... ml_g=HistGradientBoostingRegressor(max_iter=50), ... ml_m=HistGradientBoostingClassifier(max_iter=50), ... ), ... ]) >>> _ = ens.fit(X, T, Y, random_state=42) >>> result = ens.ate() >>> boot = ens.bootstrap(n_boot=5, random_state=42) # small n_boot for a fast example
Methods
__init__Compute the ensemble ATE from fitted component models.
Bootstrap inference for ensemble ATE and CATE.
Compute ensemble CATE from fitted component models.
Pairwise disagreement heatmap between component CATEs on
X.Fit and compute ATE (and optionally bootstrap CIs) in one call.
Fit all component methods on the given data.
Return a formatted, multi-line summary of ensemble state.
Bar chart of the fitted aggregation strategy's weights.
Attributes
Names of component methods that support CATE.
Names of all component methods.
Details
- ate(X=None, aggregation=None)[source]¶
Compute the ensemble ATE from fitted component models.
- Parameters:
X (ndarray | None) – Covariate matrix for ATE evaluation. If
None, uses the training covariates fromfit().aggregation (str | AggregationStrategy | None) – Optional override for the aggregation strategy. If
None, uses the strategy fromfit(). Seecate()for full override semantics.
- Returns:
AteEstimate with aggregated ATE and component estimates. For bootstrap CIs, use
bootstrap().- Return type:
- bootstrap(X=None, n_boot=200, alpha=0.05, random_state=None, n_jobs=1, method='nonparametric', subsample_size=0.5)[source]¶
Bootstrap inference for ensemble ATE and CATE.
Resamples training data, refits the entire pipeline (component models and aggregation weights) per replicate, and computes percentile CIs for both ATE and CATE.
For SupervisedStrategy, each replicate re-runs the full cross-fitting and nuisance estimation on the resampled data — this is the honest bootstrap but is expensive: B × Q × K adapter fits plus B nuisance fits. Use lightweight nuisance models (set propensity_model and outcome_model on the strategy) when running many replicates.
- Parameters:
X (ndarray | None) – Covariates for prediction. If
None, uses X_train.n_boot (int) – Number of bootstrap replicates.
alpha (float) – Significance level (default 0.05 → 95% CIs).
random_state (int | None) – Seed for bootstrap resampling. If
Noneandfit()was called with a seed, bootstrap seeds are derived deterministically from the fit seed.n_jobs (int) – Parallel workers for bootstrap replicates. 1 = sequential, -1 = all cores. Requires joblib. Each replicate’s inner cross-fitting and nuisance estimation run sequentially — bootstrap is treated as the outermost parallel level to avoid oversubscription.
method (str) – Resampling scheme.
"nonparametric"(default): n-out-of-n with replacement, the standard Efron bootstrap. Replicates contain duplicates of the original units, which can leak across cross-fit folds inside DML-style components and supervised aggregation wrappers, weakening the orthogonality argument those components rely on."subsample": m-out-of-n without replacement, T-stratified. Eliminates duplicates so any downstream cross-fit (the supervised pipeline’s outer loop, every adapter’s internal cross-fit, supervised aggregation CV) stays honest. CIs use the Politis–Romano (1994) scaled-percentile correction:θ̂ + sqrt(m/n) * percentile(T_m - θ̂).subsample_size (float | int) – Subsample size when
method="subsample". Float in (0, 1) is interpreted as a fraction of n; int is interpreted as m directly. Must satisfy 1 ≤ m < n. The default 0.5 (m = n/2) is a practical compromise between preserving sample size for the components’ nuisance fits and giving the subsampling correction enough room to work; smaller m is justified theoretically but increases replicate failure rates and finite-sample bias of the components. Ignored whenmethod="nonparametric".
- Returns:
BootstrapResult with ATE and CATE point estimates, CIs, and bootstrap distributions.
- Return type:
- cate(X=None, aggregation=None)[source]¶
Compute ensemble CATE from fitted component models.
Only methods with
supports_cate=Trueparticipate. The aggregation strategy determines how component CATE vectors are combined (pointwise rule, agreement-based weights, or outcome-supervised weights).- Parameters:
X (ndarray | None) – Covariate matrix for CATE prediction, shape (n, p). If
None, uses the training covariates fromfit().aggregation (str | AggregationStrategy | None) – Optional override for the aggregation strategy. If
None, uses the strategy fromfit(). Accepts a string alias, a PointwiseStrategy (stateless), an AgreementStrategy (weights recomputed from cached training CATE predictions), or a SupervisedStrategy (weights recomputed from cached OOF predictions — requires a prior supervised fit). The override object is mutated (weights populated). Pass a fresh instance per call if you need independent weight objects.
- Returns:
CateEstimate with aggregated CATE and component estimates. For bootstrap CIs, use
bootstrap().- Return type:
- disagreement(X, *, ax=None, metric='spearman', cluster=False, annotate=True)[source]¶
Pairwise disagreement heatmap between component CATEs on
X.Thin wrapper around
metacausal.plots.disagreement(); see there for the full parameter reference. Requires theplotsextra (pip install 'metacausal[plots]').
- estimate(X, T, Y, n_boot=0, alpha=0.05, random_state=None, n_jobs=1, method='nonparametric', subsample_size=0.5, **kwargs)[source]¶
Fit and compute ATE (and optionally bootstrap CIs) in one call.
- Parameters:
X (ndarray) – Covariate matrix, shape (n, p).
T (ndarray) – Treatment vector, shape (n,).
Y (ndarray) – Outcome vector, shape (n,).
n_boot (int) – Number of bootstrap replicates. 0 = point estimate only.
alpha (float) – Significance level for bootstrap CIs (default 0.05 → 95%).
random_state (int | None) – Seed for reproducibility.
n_jobs (int) – Parallel workers. Routed to the outermost active level: when
n_boot > 0, bootstrap is parallelized and the innerfit()runs sequentially; otherwise the supervised cross-fitting / component-fit loop is parallelized.method (str) – Bootstrap resampling scheme; see
bootstrap().subsample_size (float | int) – Subsample size when
method="subsample"; seebootstrap().**kwargs – Forwarded to each method’s
fit()call.
- Returns:
AteEstimatewhenn_boot=0,BootstrapResultwhenn_boot > 0.- Return type:
- fit(X, T, Y, random_state=None, n_jobs=1, **kwargs)[source]¶
Fit all component methods on the given data.
- Parameters:
X (ndarray) – Covariate matrix, shape (n, p).
T (ndarray) – Treatment vector, shape (n,).
Y (ndarray) – Outcome vector, shape (n,).
random_state (int | None) – Seed for reproducibility. Controls component estimator randomness.
n_jobs (int) – Parallel workers for component fits. For a
SupervisedStrategy, this also parallelizes the Q×K cross-fitting loop. Inner levels always run sequentially; if called inside an already-parallel context, this value is ignored.**kwargs – Forwarded to each adapter’s
fit()call.
- Returns:
self (for chaining –
ens.fit(X, T, Y).ate()).- Return type:
- weights(*, ax=None, on_uniform='warn', sort=True)[source]¶
Bar chart of the fitted aggregation strategy’s weights.
Thin wrapper around
metacausal.plots.weights(); see there for the full parameter reference. Requires theplotsextra (pip install 'metacausal[plots]'). RaisesValueErrorif the fitted aggregation strategy is pointwise (no weights to plot).- Returns:
matplotlib Axes the plot was drawn on.
- Parameters:
ax (Axes | None)
on_uniform (Literal['warn', 'error', 'ignore'])
sort (bool)
- Return type:
Axes