modeling.PathModel
A compiled Bayesian path model.
Usage
modeling.PathModel(
spec,
graph_info,
data,
families=None,
panel_info=None,
pooling=None,
latent=None,
priors=None,
scaling_factors=None,
*,
initial_internal=None,
initial_explicit=False
)Created by pathmc.model(). Holds the parsed specification, graph structure, design matrices, and the compiled PyMC model.
When data is None, the model operates in data-free mode: introspection (graph(), equations(), priors()) and identification helpers work, but methods requiring data or a compiled PyMC model raise RuntimeError.
Parameters
spec: Spec-
Parsed model specification.
graph_info: GraphInfo-
DAG with topological order and node classification.
data: nw.DataFrame | None-
Observed data used to build design matrices.
Nonefor data-free DAG exploration. families: dict[str, str] | None = None-
Per-variable distribution families.
panel_info: PanelInfo | None = None-
Panel metadata (unit/time structure).
pooling: str | dict | None = None-
Pooling specification for hierarchical panel models.
latent: set[str] | None = None-
Endogenous variables with no observed data column.
priors: dict | None = None-
Custom prior configuration mapping parameter names to
Priorobjects frompymc_extras. Overrides are merged with defaults. scaling_factors: ScalingFactors | None = None-
Fitted scale factors applied to the data before compilation (see
pathmc.Scaling). Exposed via fitted_scaling.
Attributes
| Name | Description |
|---|---|
| fitted_scaling |
Fitted scale factors when the model was built with scaling=, else None.
|
| initial_baseline | Pre-sample lag states in model units, or None without a lag scan. |
| pymc_model | The compiled PyMC model. |
fitted_scaling
Fitted scale factors when the model was built with scaling=, else None.
fitted_scaling: ScalingFactors | None
Pass this object back as scaling= to pathmc.simulate() to reuse the exact estimation-time scales.
initial_baseline
Pre-sample lag states in model units, or None without a lag scan.
initial_baseline: InitialBaseline | None
The first fitted time uses these values as the lag() input. They are not an extra period in do() or predict() summaries. explicit is False when the values were inferred. An endogenous base then uses that unit’s first observed value. An exogenous base uses a matching <variable>_lag1 column when present, otherwise the first observed row, otherwise zeros. A do(initial=) override does not change this record. The returned arrays are a snapshot; editing them does not alter model execution.
pymc_model
The compiled PyMC model.
pymc_model: pm.Model
Raises
RuntimeError- If the model was created without data.
Methods
| Name | Description |
|---|---|
| adjustment_model() | Build a backdoor-adjusted single-equation outcome model. |
| adjustment_sets() | Find valid backdoor adjustment sets for the causal effect |
| ate() | Compute the average treatment effect of treatment on outcome. |
| att() | Compute the average treatment effect on the treated (ATT). |
| atu() | Compute the average treatment effect on the untreated (ATU). |
| cate() | Compute the conditional average treatment effect. |
| collider_warnings() | Warn when a proposed adjustment variable is a collider on a |
| comparisons() | Interventional contrasts between two values of a variable. |
| counterfactual() | Compute unit-level counterfactual outcomes. |
| datagrid() | Build a business-unit covariate grid from the fitted data frame. |
| design() | Return the design matrix for an endogenous variable’s equation. |
| do() | Simulate an intervention using the do-operator. |
| effect() | Compute the effect along a causal path in the DAG. |
| effects_summary() | Return a posterior summary of labeled coefficients and defined parameters. |
| equations() | Return model equations and/or prior specifications. |
| falsify() | Falsify the whole DAG against the data via a permutation test. |
| fit() | Run MCMC sampling and store the resulting posterior DataTree. |
| frontdoor_identifiable() | Check whether the front-door criterion identifies the causal |
| graph() | Return a graphviz DAG of the structural model. |
| implied_independences() | List all conditional independences implied by the DAG. |
| is_identifiable() | Check if the causal effect of treatment on outcome is |
| latent_trajectory() | Return the posterior latent state of a panel variable over time. |
| predict() | Run posterior predictive sampling. |
| predictions() | Interventional or associational response-mean predictions. |
| priors() | Return a summary of prior distributions for all parameters. |
| prob() | Compute the probability of an expression under an intervention. |
| refute_placebo() | Refute the estimated effect with a Bayesian placebo treatment. |
| sample_prior_predictive() | Draw samples from the prior predictive distribution. |
| sensitivity() | Assess robustness of a causal effect to unmeasured confounding. |
| set_priors() | Update prior distributions and recompile the model. |
| slopes() | Finite-difference interventional slopes. |
| standardized() | Return stdyx-standardized coefficients for labeled effects. |
| summary() | Return a posterior summary table. |
| test_implications() | Test all DAG-implied conditional independences against the data. |
| to_graphviz() | Render the underlying PyMC model as a graphviz plate diagram. |
adjustment_model()
Build a backdoor-adjusted single-equation outcome model.
Usage
adjustment_model(
query=None,
*,
treatment=None,
outcome=None,
adjustment_set=None,
formula=None,
data=None,
families=None,
priors=None
)Returns an ~pathmc.adjustment.AdjustmentModel facade around a reduced Y ~ treatment + Z regression for the designated treatment–outcome query on this structural DAG.
Parameters
query: str | None = None-
Treatment–outcome query, e.g.
"X -> Y". Exactly one of query or bothtreatment=andoutcome=must be passed. treatment: str | None = None-
Treatment variable name.
outcome: str | None = None-
Outcome variable name.
adjustment_set: set[str] | None = None-
Explicit backdoor adjustment set. Required when several minimal sets exist; otherwise the unique minimal set is auto-selected.
formula: str | None = None-
Custom reduced regression formula. Must include the treatment and every member of the adjustment set.
data: IntoFrame | None = None-
Observed data. Required when this model is data-free; overrides the parent’s data when both are present.
families: dict[str, str] | None = None-
Per-variable families for the reduced model only.
priors: dict | None = None-
Prior overrides for the reduced equation. A shared key is inherited when its shape does not follow the predictor set or treatment coding: outcome dispersion priors, transform-parameter priors used by the reduced formula, and scalar hierarchical categorical hyperpriors (
mu_beta_*,sigma_beta_*). Otherwise a custom parent value must be passed here or construction raises. That includes coefficient priors and non-scalar hyperpriors. Unknown shared keys fail closed the same way. An explicit value here still wins.
Returns
Raises
ValueError- If this model has a custom prior on a shared key whose shape follows the predictor set or treatment coding, or on an unknown shared key, and priors does not override that key.
See Also
- AdjustmentModel
-
The returned facade.
- datagrid()
-
Build a covariate grid for interpret queries.
adjustment_sets()
Find valid backdoor adjustment sets for the causal effect
Usage
adjustment_sets(treatment, outcome)of treatment on outcome.
This function reasons about the DAG structure declared in the model specification. It cannot detect omitted variables, missing edges, or other forms of misspecification. Use test_implications() to check whether the DAG’s structural assumptions are consistent with observed data.
A ~~ residual covariance block is a declaration of an unobserved common cause of its members, so it is expanded into a latent confounder node before the criterion is applied: no adjustment set can block it.
Parameters
treatment: str-
Treatment variable name.
outcome: str- Outcome variable name.
Returns
list[set[str]]- All valid minimal adjustment sets, sorted by size.
ate()
Compute the average treatment effect of treatment on outcome.
Usage
ate(outcome, treatment, values=(0.0, 1.0), **do_kwargs)Shorthand for do(set={treatment: hi}) - do(set={treatment: lo}).
Parameters
outcome: str-
Outcome variable name; the default target of the result’s accessors (
.mean(),.hdi(),.prob()). treatment: str-
Treatment variable to intervene on.
values: tuple[float, float] = (0.0, 1.0)-
(lo, hi)intervention values. Default(0.0, 1.0). **do_kwargs: Any-
Passed to do() (e.g.
kind,simulate_over).
Returns
EstimandResult-
Contrast
do(treatment=hi) - do(treatment=lo), focused on outcome.
att()
Compute the average treatment effect on the treated (ATT).
Usage
att(outcome, treatment, values=(0.0, 1.0), treated_value=1.0, kind="mean")Estimates E[Y(hi) - Y(lo) | T = treated_value] using subgroup-aware empirical integration over the covariate distribution of the treated subgroup.
Unlike ate(), which averages over the full covariate distribution, att() restricts to rows where the treatment variable equals treated_value. In linear models without interactions, ATT equals ATE; they diverge with effect modification or nonlinear link functions.
Parameters
outcome: str-
Outcome variable name (used for documentation; the contrast is computed over all endogenous variables).
treatment: str-
Treatment variable to intervene on.
values: tuple[float, float] = (0.0, 1.0)-
(lo, hi)intervention values. Default(0.0, 1.0). treated_value: float = 1.0-
Value of the treatment variable identifying the treated subgroup (default
1.0). kind: str = "mean"-
"mean"for deterministic propagation,"predictive"to include residual noise.
Returns
EstimandResult-
Contrast
do(treatment=hi) - do(treatment=lo)integrated over the treated subgroup’s covariate distribution, focused on outcome.
Raises
RuntimeError-
If called before
.fit(). NotImplementedError-
If the model is a panel model (not yet supported).
ValueError- If no observations match treated_value.
Examples
>>> model = pathmc.model("Y ~ T + X", data=df)
>>> model.fit(draws=1000, tune=1000)
>>> att = model.att("Y", "T")
>>> att.mean() # E[Y(1) - Y(0) | T=1]atu()
Compute the average treatment effect on the untreated (ATU).
Usage
atu(outcome, treatment, values=(0.0, 1.0), untreated_value=0.0, kind="mean")Estimates E[Y(hi) - Y(lo) | T = untreated_value] using subgroup-aware empirical integration over the covariate distribution of the untreated subgroup.
Unlike ate(), which averages over the full covariate distribution, atu() restricts to rows where the treatment variable equals untreated_value. In linear models without interactions, ATU equals ATE; they diverge with effect modification or nonlinear link functions.
Parameters
outcome: str-
Outcome variable name (used for documentation; the contrast is computed over all endogenous variables).
treatment: str-
Treatment variable to intervene on.
values: tuple[float, float] = (0.0, 1.0)-
(lo, hi)intervention values. Default(0.0, 1.0). untreated_value: float = 0.0-
Value of the treatment variable identifying the untreated subgroup (default
0.0). kind: str = "mean"-
"mean"for deterministic propagation,"predictive"to include residual noise.
Returns
EstimandResult-
Contrast
do(treatment=hi) - do(treatment=lo)integrated over the untreated subgroup’s covariate distribution, focused on outcome.
Raises
RuntimeError-
If called before
.fit(). NotImplementedError-
If the model is a panel model (not yet supported).
ValueError- If no observations match untreated_value.
Examples
>>> model = pathmc.model("Y ~ T + X", data=df)
>>> model.fit(draws=1000, tune=1000)
>>> atu = model.atu("Y", "T")
>>> atu.mean() # E[Y(1) - Y(0) | T=0]cate()
Compute the conditional average treatment effect.
Usage
cate(outcome, treatment, values=(0.0, 1.0), condition=None, **do_kwargs)Like ate() but with additional variables fixed in both scenarios, enabling effect modification analysis.
Parameters
outcome: str-
Outcome variable name; the default target of the result’s accessors.
treatment: str-
Treatment variable to intervene on.
values: tuple[float, float] = (0.0, 1.0)-
(lo, hi)intervention values. condition: dict[str, float] | None = None-
Variables to fix at specific values in both scenarios.
**do_kwargs: Any- Passed to do().
Returns
EstimandResult- Contrast with conditioning variables held fixed, focused on outcome.
collider_warnings()
Warn when a proposed adjustment variable is a collider on a
Usage
collider_warnings(adjustment_vars, treatment, outcome)path between treatment and outcome.
This is a structural heuristic. It flags the collider even when the rest of the set keeps that path blocked. is_valid_adjustment_set() decides whether the set satisfies the backdoor criterion.
This function reasons about the DAG structure declared in the model specification. It cannot detect omitted variables, missing edges, or other forms of misspecification. Use test_implications() to check whether the DAG’s structural assumptions are consistent with observed data.
A ~~ residual covariance block is a declaration of an unobserved common cause of its members, so it is expanded into a latent confounder node before colliders are searched for: a variable with only one declared parent can still become a collider once that synthetic confounder is added.
Parameters
adjustment_vars: set[str]-
Proposed adjustment set.
treatment: str-
Treatment variable name.
outcome: str- Outcome variable name.
Returns
list[str]- Warning strings for problematic variables.
comparisons()
Interventional contrasts between two values of a variable.
Usage
comparisons(
outcome,
variable,
*,
contrast=(0.0, 1.0),
comparison="diff",
conditional=None,
average_by="all"
)With comparison="diff" and average_by="all", matches ate() on the same contrast.
Parameters
outcome: str-
Outcome variable name.
variable: str-
Variable to vary under do().
contrast: tuple[float, float] = (0.0, 1.0)-
(lo, hi)intervention values. comparison: str = "diff"-
"diff","ratio", or"lift". conditional: dict[str, float | str] or None = None-
Additional variables held fixed at scalar values. A categorical predictor takes a fitted level label.
average_by: str or None = "all"-
"all"collapses to EstimandResult;Nonekeeps unit-level InterpretResult draws.
Returns
EstimandResult or InterpretResult
counterfactual()
Compute unit-level counterfactual outcomes.
Usage
counterfactual(evidence, do, allow_partial_evidence=False)Implements Pearl’s three-step procedure (abduction → action → prediction) using posterior draws. Unlike do(), which answers population-level questions with exogenous terms at their means, counterfactuals infer individual-specific exogenous values from evidence before simulating the intervention in do.
Parameters
evidence: dict[str, Any]-
Observed values for a specific individual. Used in the abduction step to recover their exogenous (U) terms. A categorical predictor is given as one of its fitted level labels and must always be supplied, since a label has no population mean to fall back on.
do: dict[str, Any]-
Intervention values (same format as
do(set=...)); a categorical predictor takes a level label. allow_partial_evidence: bool = False-
If
True, missing numeric evidence is assigned its population mean (U = 0) with a warning. By default, all model variables must be supplied so a counterfactual cannot silently mix individual and population information.
Returns
DoResult-
Posterior over counterfactual outcomes with
.mean(var),.hdi(var), and contrast arithmetic.
Raises
RuntimeError-
If called before
.fit(). ValueError-
If evidence or do keys are invalid, an intervention on an
hsgp()input falls outside the basis boundary frozen from the fitted data, or the model uses non-Gaussian families. NotImplementedError- If the model is a panel model.
datagrid()
Build a business-unit covariate grid from the fitted data frame.
Usage
datagrid(**cols)On a model fitted with scaling=, user-supplied values are already business units. Columns held at their fitted mean or mode are converted from the model’s internal scale before the grid is returned. See pathmc.datagrid() for details.
Parameters
**cols: list[float] | list[int]- Column names mapped to lists of values to cross.
Returns
pd.DataFrame- Cartesian product grid with unspecified columns held constant.
design()
Return the design matrix for an endogenous variable’s equation.
Usage
design(var)Parameters
var: str- Name of the endogenous (LHS) variable.
Returns
IntoFrame- Design matrix with named columns, backed by the same DataFrame library as the data passed to model().
Raises
RuntimeError-
If the model was created without data.
KeyError- If var is not an endogenous variable in the model.
do()
Simulate an intervention using the do-operator.
Usage
do(self, set: dict[str, Any] | None = None, shift: dict[str, float] | None = None, kind: str = 'mean', simulate_over: str | None = None, random_seed: int | None = None, average_units: bool = True, initial: Mapping[str, Any] | None = None, policy: None = None, horizon: None = None, n_simulations: None = None, keep_trajectories: None = None) -> DoResult
do(self, set: dict[str, Any] | None = None, shift: dict[str, float] | None = None, kind: str = 'mean', simulate_over: str | None = None, random_seed: int | None = None, average_units: bool = True, initial: Mapping[str, Any] | None = None, policy: TreatmentPolicy, horizon: int | None = None, n_simulations: int | None = None, keep_trajectories: bool | None = None) -> PolicyRunUses PyMC-native graph surgery: pm.do() on the generative model for interventions, then pm.sample_posterior_predictive() (kind=“predictive”) or compute_deterministics (kind=“mean”) for propagation.
Parameters
set: dict[str, float | np.ndarray] | None = None-
Variables to fix at specific values (hard intervention). For panel models with
simulate_over="time", values can be arrays of shape(n_times,)for time-varying interventions (e.g., a temporary spend increase). A NaN in that array releases the step of an endogenous variable: the scan runs the variable’s own equation there, and the reported path keeps the finite held values elsewhere. NaN does not release an exogenous input, which has no equation to fall back on. On a model compiled withscaling=, values are in business units: pathmc divides by the fitted factor before graph surgery sodo(set={"tv": 500})means 500 of the original column, not 500 scaled units. shift: dict[str, float] | None = None-
Not implemented; raises
NotImplementedErrorif given. kind: str = "mean"-
"mean"for deterministic propagation via mu Deterministics,"predictive"to include residual noise. Residual-covariance blocks are sampled from a generative sibling so that noise reaches descendants; the estimation likelihood is unchanged. simulate_over: str | None = None-
"time"to activate time-forward panel simulation. Required for models withlag()oradstock(), which also require the model to have been fitted withpanel=. average_units: bool = True-
When
True(default),kind="mean"averages response means over observation rows. prob sets this toFalsewhen the expression names both an endogenous variable and a per-unit intervention, so the comparison uses unit-level means. random_seed: int | None = None-
Seed for reproducible predictive process noise. Does not change stored posterior draws. Ignored for deterministic mean queries.
initial: Mapping[str, Any] | None = None-
Pre-sample state for every observed
lag()base, in business units. A scalar broadcasts to every fitted unit; a mapping must contain exactly the fitted unit labels.Nonereuses initial_baseline. An override is local to this call: it governs both mean and predictive recursion and does not refit the model or change the stored baseline. The values are the state before the first fitted time, not an extra outcome period. policy: TreatmentPolicy | None = None-
History-dependent treatment assigned inside the panel scan. Requires an explicit pre-sample baseline,
simulate_over='time', and a Gaussian lag model.kind='predictive'is rejected: the policy path always integrates process noise. horizon: int | None = None-
Required. Must equal the fitted time count. Do not omit it.
n_simulations: int | None = None-
Stochastic histories per posterior draw. Default 256. Minimum 2.
keep_trajectories: bool | None = None- Retain unit-level paths. Default False for a policy query.
Returns
Raises
RuntimeError-
If called before
.fit(). ValueError-
If
kindis not"mean"or"predictive". Also ifsimulate_over="time"without panel, or if an intervention on anhsgp()input falls outside the basis boundary[mid - L, mid + L]frozen from the fitted data (beyond it the basis aliases instead of extrapolating), or if a temporal scan model is simulated withoutsimulate_over="time".
effect()
Compute the effect along a causal path in the DAG.
Usage
effect(path)Uses the product-of-coefficients method, which assumes linear structural equations (identity link). For multi-edge paths that cross non-Gaussian link scales, use simulate() with do() interventions instead.
Parameters
path: str-
A path string like
"X -> M -> Y"specifying the causal pathway. Each edge must correspond to a regression term.
Returns
EffectResult- Posterior draws for the path-specific effect.
Raises
RuntimeError-
If the model was created without data, or called before
.fit(). ValueError-
If a node is not endogenous, an edge does not exist, or a drawn edge exists only as an interaction constituent. In that last case the error names the interaction term (for example
X:Z) instead of claiming the edge is absent. NotImplementedError- If a multi-edge path includes a non-Gaussian edge target whose coefficient would be multiplied across link scales.
effects_summary()
Return a posterior summary of labeled coefficients and defined parameters.
Usage
effects_summary()Returns
pd.DataFrame-
Summary with mean, sd, and HDI for each labeled coefficient and
:=defined parameter. On a scaled model, labeled coefficients are mapped to business units according to the term (linear/adstock:f_out / f_pred; saturation/HSGP:f_out; interaction:f_out / prod(f_pred_i)), and defined parameters are evaluated from those rescaled draws so a product such asindirect := a*bagrees with the rows above it. Per-unit factors are reduced with a data-weighted mean — a ratio of means, not the mean of ratios.
Raises
RuntimeError-
If the model was created without data, or called before
.fit(). DuplicateEquationError- If a defined parameter uses a coefficient label’s name. The summary reports each labeled coefficient and each defined parameter, so the defined parameter must not occupy the label.
equations()
Return model equations and/or prior specifications.
Usage
equations(show="all")Works before sampling. By default renders both structural equations and priors as combined LaTeX in Jupyter / Quarto.
Parameters
show: ("all", "structural", "priors") = "all"-
What to display:
"all"— structural equations and priors (default)."structural"— only the structural (regression) equations."priors"— only the prior distributions.
Returns
ModelEquations | EquationList | PriorTable-
ModelEquationswhen show=“all”,EquationListwhen show=“structural”,PriorTablewhen show=“priors”.
falsify()
Falsify the whole DAG against the data via a permutation test.
Usage
falsify(
n_permutations=None,
significance_level=0.05,
significance_ci=0.05,
include_unconditional=True,
random_seed=None
)Grades the entire DAG at once, rather than one missing edge at a time as test_implications() does. It counts how many Local Markov Conditions (implied parental conditional independences) the data violate, then compares that count to a baseline of randomly relabeled competitor graphs.
The DAG is reported as informative (falsifiable) when few node permutations share its Markov equivalence class. An informative DAG that violates fewer conditions than the permuted baseline is the positive case: not contradicted and testable. A non-informative DAG is also not rejected, but that verdict is vacuous — interpret it as not falsifiable rather than as evidence for the graph. This ports dowhy’s gcm.falsify_graph (Eulig et al., 2023) using the same partial-correlation conditional independence methodology as test_implications(). Because that test is linear, purely nonlinear dependencies are not detected, so a “not rejected” verdict is only as strong as the linear-Gaussian assumption. local_violations includes requested_k, effective_k, rank_collapsed, and df for each test on this DAG. A rank-deficient parent set (a constant, duplicate, or collinear parent) sets rank_collapsed and warns. That row stays in the frame, but given_lmc_violations, n_lmc_tests, violations, and the permutation fraction all exclude it. A permuted DAG that produced no written parental test is omitted from that fraction, not scored as zero violations. If every drawn permutation is omitted, falsified is unset and the summary, compact repr, and HTML say the LMC comparison was not run. The summary names the collapsed count separately. If every requested test collapses, the DAG cannot be evaluated.
Uses the observed data, not the posterior, and works before sampling. Models with residual covariances (~~) are not supported and raise ValueError — falsify the directed structure without the ~~ terms instead.
Parameters
n_permutations: int | None = None-
Number of permuted DAGs in the baseline. Defaults to
round(1 / significance_level). For small graphs (at most 7 nodes), if it meets or exceeds the number of distinct node relabelings (n!), all are enumerated exactly; otherwise random relabelings are sampled. significance_level: float = 0.05-
Significance level for the permutation-based verdict (default 0.05).
significance_ci: float = 0.05-
Significance level for each conditional independence test (default 0.05).
include_unconditional: bool = True-
Whether to also test unconditional independences implied by root nodes (default
True). random_seed: int | None = None- Seed for the permutation sampler, for reproducible results.
Returns
FalsificationResult-
Verdict (
.falsified,.falsifiable), permutation p-values, per-test local violations, and a.plot()helper.
fit()
Run MCMC sampling and store the resulting posterior DataTree.
Usage
fit(compute_log_likelihood=True, **kwargs)All keyword arguments are forwarded to pm.sample().
Parameters
compute_log_likelihood: bool = True-
Whether to compute the pointwise log-likelihood after sampling (default
True). SetFalseto skip the extra pass when you do not needaz.loo/az.waic— e.g. internal re-fits during refutation. **kwargs: Any-
Keyword arguments forwarded to
pm.sample(). Common options include draws,tune,chains,random_seed,target_accept, andnuts_sampler.
Returns
xarray.DataTree- Posterior samples.
frontdoor_identifiable()
Check whether the front-door criterion identifies the causal
Usage
frontdoor_identifiable(treatment, mediator, outcome)effect of treatment on outcome through mediator.
This function reasons about the DAG structure declared in the model specification. It cannot detect omitted variables, missing edges, or other forms of misspecification. Use test_implications() to check whether the DAG’s structural assumptions are consistent with observed data. If the spec includes adjustment variables that add edges absent from the true causal DAG, the check may report false negatives. Build a separate GraphInfo from the causal structure for an accurate check in that case.
Parameters
treatment: str-
Treatment variable name.
mediator: str-
Mediator variable name.
outcome: str- Outcome variable name.
Returns
tuple[bool, str]-
(identifiable, message)where message explains the result or describes which condition fails.
graph()
Return a graphviz DAG of the structural model.
Usage
graph()Works before sampling. Exogenous nodes are drawn as boxes, endogenous nodes as ellipses. Latent nodes get dashed borders (bold for stochastic latent). Labeled coefficients appear on edges.
implied_independences()
List all conditional independences implied by the DAG.
Usage
implied_independences()For each pair of non-adjacent nodes, returns the independence statement with the conditioning set derived from the basis set method (Shipley, 2000). Works before sampling — only the graph structure is needed.
A ~~ residual covariance block declares an unobserved common cause of its members, so d-separation is checked with that confounder made explicit: two variables joined only by a declared ~~ edge are never reported as independent.
Returns
list[ConditionalIndependence]- Implied independence statements, sorted alphabetically.
is_identifiable()
Check if the causal effect of treatment on outcome is
Usage
is_identifiable(treatment, outcome)identifiable via the backdoor criterion.
This function reasons about the DAG structure declared in the model specification. It cannot detect omitted variables, missing edges, or other forms of misspecification. Use test_implications() to check whether the DAG’s structural assumptions are consistent with observed data.
A ~~ residual covariance block is a declaration of an unobserved common cause of its members, so it is expanded into a latent confounder node before the criterion is applied: no adjustment set can block it.
Parameters
treatment: str-
Treatment variable name.
outcome: str- Outcome variable name.
Returns
bool- True if at least one valid adjustment set exists.
latent_trajectory()
Return the posterior latent state of a panel variable over time.
Usage
latent_trajectory(var)For scan-compiled panel models with latent variables (latent=[...]), extracts the inferred latent trajectory from the posterior.
Shape Semantics
The returned DataArray has dimensions (chain, draw, time, unit):
chain/draw: posterior sampling dimensions.time: sorted unique time values from the model’s panel structure (thetimecolumn values).unit: unit labels (theunitcolumn values).
The underlying posterior variable is the Deterministic registered at compile time: for stochastic latents (latent_normal family) the scan emits the realized state under the variable’s own name (var); for deterministic latents it is stored as mu_{var}. Each posterior slice is an (n_times, n_units) matrix in time-major sorted order; this method attaches the matching time/unit coordinates.
The default init_{var} prior is Normal(0, 1) and is not scale-free: override it when the latent is not O(1), otherwise t=0 is pinned near zero and early-period trajectories are biased.
Parameters
var: str- Name of a latent endogenous variable.
Returns
xarray.DataArray-
Posterior latent states with dims
(chain, draw, time, unit)andtime/unitcoordinates.
Raises
RuntimeError-
If the model has no data or has not been fitted yet.
ValueError- If var is not a latent variable, or the model is not a scan-compiled panel model (no temporal terms).
Examples
>>> traj = m.latent_trajectory("awareness") # doctest: +SKIP
>>> traj.mean(dim=("chain", "draw")) # doctest: +SKIPpredict()
Run posterior predictive sampling.
Usage
predict(data=None, *, one_step_ahead=True, **kwargs)Wraps pm.sample_posterior_predictive() and extends the stored DataTree with a posterior_predictive group.
Parameters
data: IntoFrame | None = None-
Optional predictor data for out-of-sample prediction. It must have the same number of rows as the fitted cross-sectional model. Categorical columns are encoded with the fitted level order and reference; unseen levels raise a clear error.
one_step_ahead: bool = True-
Only meaningful for panel models with a lagged endogenous term such as
y ~ lag(y). WhenTrue(default) each time step conditions on the observed previous value, so the predictions are one-step-ahead forecasts — the posterior predictive distribution that matches the likelihood the model was fitted with, and the right choice for a posterior predictive check. WhenFalsethe recursion is free-running: step t conditions on the model’s own simulated value at t-1, giving a multi-step trajectory whose uncertainty compounds over time. Ignored for models without a lagged endogenous term, where the two are identical. Scan residual blocks jointly sample their original outcome names. One-step means use observed current parents and lag history; free trajectories propagate the same realized correlated states to descendants and later lags. Scan +~~requires complete, observed Gaussian equations throughout the model. **kwargs: Any-
Passed directly to
pm.sample_posterior_predictive(). Passextend_inferencedata=Falseto leave the stored DataTree untouched and get the standalone posterior predictive samples back instead.
Returns
xarray.DataTree-
The stored DataTree with a
posterior_predictivegroup added, or the standalone posterior predictive samples whenextend_inferencedata=Falseis passed.
Raises
RuntimeError-
If the model was created without data, or called before
.fit(). ValueError-
For panel models with a temporal dependency (
lag()oradstock()), ifpm.set_data()was used to swap in a differently-shaped panel first.n_units/n_timesare baked into the scan graph at model-compile time, so out-of-sample prediction on a new panel shape is not supported here: build a new model withpathmc.model(..., data=new_data, panel=...)on the new data instead.
predictions()
Interventional or associational response-mean predictions.
Usage
predictions(outcome, *, set=None, newdata=None)Parameters
outcome: str-
Outcome variable name.
set: dict[str, float | str] or None = None-
Intervention values. A categorical predictor takes a fitted level label. When omitted, predictions are associational.
newdata: IntoFrame or None = None-
Covariate grid or frame to predict on, in business units. Defaults to the fitted data. On a scaled model the grid is divided by the fitted factors before compilation, matching
do(set=).
Returns
InterpretResult- Unit-level posterior draws for the outcome.
priors()
Return a summary of prior distributions for all parameters.
Usage
priors()Works before sampling. The table reflects any custom priors set via set_priors() or the priors argument to pathmc.model().
Use equations() for a unified view of both structural equations and priors.
prob()
Compute the probability of an expression under an intervention.
Usage
prob(expr, set=None, kind="predictive", **do_kwargs)Evaluates P(expr | do(set)) using posterior predictive draws.
Parameters
expr: str-
Boolean expression over variable names, e.g.
"Y > 0". set: dict[str, float] | None = None-
Intervention values for the do-operator.
kind: str = "predictive"-
Propagation kind (default
"predictive"to include residual noise, which is needed for meaningful probabilities). **do_kwargs: Any- Passed to do().
Returns
float-
Fraction of aligned comparisons that satisfy expr. For
kind="mean", a per-unit intervention is compared to unit-level means, not to the outcome averaged over units.
refute_placebo()
Refute the estimated effect with a Bayesian placebo treatment.
Usage
refute_placebo(
outcome,
treatment,
*,
values=(0.0, 1.0),
n_permutations=4,
significance_level=0.05,
sample_kwargs=None,
random_seed=None
)Permutes the treatment column n_permutations times, re-fitting and re-estimating the ATE each time. A sound pipeline reports no effect for a permuted treatment. The per-permutation posterior summaries are pooled through a hierarchical normal-normal random-effects model that separates the systematic placebo bias mu_null (which should straddle zero) from the structural volatility tau_het; the real effect is then calibrated against the resulting null predictive distribution.
Each permutation triggers a full MCMC re-fit, so cost scales linearly with n_permutations.
Parameters
outcome: str-
Outcome variable name.
treatment: str-
Treatment variable to replace with a placebo. Must be an observed (non-latent) data column.
values: tuple[float, float] = (0.0, 1.0)-
(lo, hi)intervention values for the ATE contrast (default(0.0, 1.0)), matching ate(). n_permutations: int = 4-
Number of placebo permutations / folds (default 4). Four is a floor — the between-fold volatility tau_het is prior-dominated at small J; use 8+ for a data-driven null spread (each fold is a full MCMC re-fit, so cost is linear).
significance_level: float = 0.05-
Threshold for the effect_survives verdict (default 0.05).
sample_kwargs: dict | None = None-
Keyword arguments forwarded to
.fit()for each placebo re-fit and to the hierarchical null model’s sampler. random_seed: int | None = None- Seed for reproducible permutations and hierarchical fit.
Returns
PlaceboRefutationResult-
Placebo null summary, verdicts (
.passes_placebo,.effect_survives), calibration (.z_cal,.p_tail), and a.plot()helper.
Raises
RuntimeError-
If the model was created without data, called before
.fit(), or not created viapathmc.model(). ValueError-
If treatment/outcome are invalid or n_permutations < 2.
NotImplementedError- If the model is a panel model (not yet supported).
sample_prior_predictive()
Draw samples from the prior predictive distribution.
Usage
sample_prior_predictive(**kwargs)Useful for checking whether default or custom priors generate plausible data ranges before running MCMC.
Parameters
**kwargs: Any-
Forwarded to
pm.sample_prior_predictive().
Returns
xarray.DataTree- Prior predictive samples.
sensitivity()
Assess robustness of a causal effect to unmeasured confounding.
Usage
sensitivity(
outcome,
treatment,
gamma_range=(0.0, 1.0),
delta_range=(0.0, 1.0),
n_grid=20,
values=(0.0, 1.0),
**do_kwargs
)Hypothesizes an unmeasured confounder U with effect γ on the treatment and δ on the outcome. The confounding bias is γ × δ, so the adjusted ATE at each grid point is::
adjusted ATE = observed ATE − γ × δ
The result includes a contour plot showing which (γ, δ) combinations would overturn the causal conclusion.
Parameters
outcome: str-
Outcome variable name.
treatment: str-
Treatment variable name.
gamma_range: tuple[float, float] = (0.0, 1.0)-
(min, max)range for γ (confounder → treatment effect). delta_range: tuple[float, float] = (0.0, 1.0)-
(min, max)range for δ (confounder → outcome effect). n_grid: int = 20-
Number of grid points per dimension (default 20).
values: tuple[float, float] = (0.0, 1.0)-
(lo, hi)intervention values for computing the ATE (default(0.0, 1.0)). **do_kwargs: Any-
Passed to ate() and thence to do() (e.g.
kind,simulate_over).
Returns
SensitivityResult-
Sensitivity analysis results with
.plot()method.
Raises
RuntimeError-
If called before
.fit(). ValueError-
If the ranges are invalid or
n_grid < 2.
set_priors()
Update prior distributions and recompile the model.
Usage
set_priors(overrides)Only the specified priors are changed; all others keep their current values. Any existing posterior samples are invalidated.
On a data-free model, priors are updated without recompilation.
Parameters
overrides: dict[str, Prior]-
Mapping from parameter name to
Priorobject. Use.equations()to see available parameter names.
Raises
ValueError- If a key does not match any model parameter.
slopes()
Finite-difference interventional slopes.
Usage
slopes(
outcome,
wrt,
*,
slope="dydx",
eps=0.0001,
conditional=None,
average_by="all"
)Parameters
outcome: str-
Outcome variable name.
wrt: str-
Variable to differentiate with respect to.
slope: str = "dydx"-
Slope type:
"dydx","eyex","eydx", or"dyex". eps: float = 0.0001-
Finite-difference step size.
conditional: dict[str, float | str] or None = None-
Additional variables held fixed at scalar values. A categorical predictor takes a fitted level label.
average_by: str or None = "all"-
"all"collapses to EstimandResult;Nonekeeps unit-level draws.
Returns
EstimandResult or InterpretResult
standardized()
Return stdyx-standardized coefficients for labeled effects.
Usage
standardized()Each coefficient is standardized as coef * sd(X) / sd(Y), giving the expected change in Y (in SD units) per SD change in X. coef is the business-unit coefficient reported by effects_summary(). sd(X) and sd(Y) are the observed marginal SDs of the data passed to model(), including on a scaled model whose internal columns were divided by per-unit factors. Binary 0/1 predictors are omitted: their SD depends on the base rate.
Returns
pd.DataFrame- Summary with mean, sd, and HDI for each standardized coefficient.
Raises
RuntimeError-
If the model was created without data, or called before
.fit().
summary()
Return a posterior summary table.
Usage
summary()Returns
pd.DataFrame-
ArviZ summary of all model parameters. Columns are full-precision floats; apply
.round()downstream as needed.
Raises
RuntimeError-
If the model was created without data, or called before
.fit().
test_implications()
Test all DAG-implied conditional independences against the data.
Usage
test_implications(alpha=0.05)For each implied independence X ⊥⊥ Y | Z, computes the partial correlation between X and Y controlling for Z and tests whether it is significantly different from zero.
A significant result flags a violation: the data show an association that the DAG says should not exist, suggesting a missing edge or incorrect structure. Each row also reports requested_k, effective_k, rank_collapsed, and df. When a conditioner is constant, duplicated, collinear with the others, or a single-level categorical that contributed no columns, rank_collapsed is set, significant is false, and a warning is emitted. The p-value still refers to the columns that entered, not the independence implied by the DAG. n_violations equals results["significant"].sum(). The display does not mark that row pass or fail. Drop the redundant variable and re-test.
Uses the observed data, not the posterior. Statements that name a latent or any other column absent from the data are skipped; implied_independences() may still list them. Only observable implications are returned.
Parameters
alpha: float = 0.05- Significance level for flagging violations (default 0.05).
Returns
ImplicationTestResult-
Test results with
.violations,.to_dataframe(), and rich display in Jupyter via_repr_html_().
to_graphviz()
Render the underlying PyMC model as a graphviz plate diagram.
Usage
to_graphviz()Delegates to pm.Model.to_graphviz() on the compiled model, showing random variables, deterministics, observed data, and plate structure.
Returns
graphviz.Digraph- Graphviz diagram of the PyMC model graph.
Raises
RuntimeError- If the model was created without data.