Causal computation in pyAgrum
Causal Inference (a.k.a. do-calculus)
The main entry point for causal inference is pyagrum.causalImpact(), which computes the
causal effect of an intervention \(do(X=x)\) on a target variable \(Y\) within a
pyagrum.CausalModel, using do-calculus identification.
- pyagrum.causalImpact(cm, *, on, doing, knowing=None, values=None)
Identify and evaluate the causal effect of do(doing) on on, optionally conditioning on knowing.
The identification procedure tries in order: d-separation (no effect), backdoor adjustment, frontdoor adjustment, and general do-calculus (ID algorithm).
- Parameters:
cm (pyagrum.CausalModel) – The causal model.
on (str or set of str) – Target variable(s) of the causal query. A single string is automatically converted to a one-element set.
doing (str or set of str) – Intervened variable(s) (the do-operator applies to these). A single string is automatically converted to a one-element set.
knowing (str or set of str, optional) – Observed variable(s) to condition on. Default is empty.
values (dict of str → str, optional) – Specific values for the on/knowing variables as
{variable_name: value_name}. When provided, the returned tensor is sliced to those values. Default is no slicing.
- Returns:
The CausalImpact object encoding the identified formula.
The evaluated tensor P(on | do(doing), knowing), or None if the effect is not identifiable.
A string explaining the identification method used or why identification failed.
- Return type:
tuple (pyagrum.CausalImpact, pyagrum.Tensor or None, str)
Examples
>>> import pyagrum as gum >>> bn = pyagrum.BayesNet.fastPrototype('X->Y->Z') >>> cm = pyagrum.CausalModel(bn) >>> formula, tensor, expl = pyagrum.causalImpact(cm, on='Z', doing='X') >>> print(expl)
- class pyagrum.CausalImpact(cm, *, on, doing, knowing=None)
Represents the result of a causal identification query P(on | do(doing), knowing).
CausalImpact encodes the identified causal formula (when the effect is identifiable) as an expression tree that can be evaluated numerically. It also records the identification method used and an explanation string.
The identification procedure tries the following strategies in order: d-separation (no effect), backdoor adjustment, frontdoor adjustment, and general do-calculus (ID algorithm).
Notes
You may prefer to use the high-level function
pyagrum.causalImpact()instead of constructing a CausalImpact object directly.- CausalImpact(cm, *, on, doing, knowing=None) -> CausalImpact
- Parameters:
cm (pyagrum.CausalModel) – the causal model.
on (str or set of str) – target variable(s) of the query. A single string is automatically converted to a one-element set. Keyword-only.
doing (str or set of str) – intervened variable(s) (the do-operator applies to these). A single string is automatically converted to a one-element set. Keyword-only.
knowing (str or set of str, optional) – observed variable(s) to condition on. A single string is automatically converted to a one-element set. Default is empty. Keyword-only.
Examples
>>> import pyagrum as gum >>> bn = pyagrum.BayesNet.fastPrototype('X->Y->Z') >>> cm = pyagrum.CausalModel(bn) >>> formula, tensor, expl = pyagrum.causalImpact(cm, on='Z', doing='X') >>> print(expl)
- cm()
Return the causal model associated with this query.
- Returns:
The causal model.
- Return type:
- doing()
Return the NodeIds of the intervened variables.
- Returns:
NodeIds of the variables in the doing-set (do-operator).
- Return type:
set of int
- doingNames()
Return the names of the intervened variables.
- Returns:
Variable names in the doing-set.
- Return type:
tuple of str
- eval()
Evaluate the identified causal formula and return the result as a tensor.
- Returns:
The distribution P(on | do(doing), knowing).
- Return type:
- Raises:
pyagrum.OperationNotAllowed – If the causal effect is not identified (
isIdentified()is False).
- explanation()
Return a human-readable explanation of the identification result.
Describes the method used (e.g. ‘d-separation’, ‘backdoor adjustment’, ‘frontdoor adjustment’, ‘do-calculus (ID)’) or explains why identification failed.
- Returns:
Explanation of the identification outcome.
- Return type:
str
- isIdentified()
Return True if the causal effect was successfully identified.
If False, the effect cannot be computed from the available data given the causal structure, and
eval()will raise an exception.- Returns:
True if the query is identifiable.
- Return type:
bool
- knowing()
Return the NodeIds of the observed variables.
- Returns:
NodeIds of the variables in the knowing-set.
- Return type:
set of int
- knowingNames()
Return the names of the observed variables.
- Returns:
Variable names in the knowing-set.
- Return type:
tuple of str
- latexQuery(*args)
Return a LaTeX string for the original causal query before identification.
The query has the form P(on | do(doing), knowing).
- Parameters:
doOperatorPrefix (str, optional) – Prefix for the do-operator notation. Default is ‘do(‘.
doOperatorSuffix (str, optional) – Suffix for the do-operator notation. Default is ‘)’.
- Returns:
LaTeX string of the causal query.
- Return type:
str
- on()
Return the NodeIds of the target variables.
- Returns:
NodeIds of the variables in the on-set.
- Return type:
set of int
- onNames()
Return the names of the target variables.
- Returns:
Variable names in the on-set.
- Return type:
tuple of str
- print_ast()
Print the AST of a CausalImpact function in a human readable way.
- Parameters:
impact (pyagrum.CausalImpact) – the function whose AST we want to print
- toDict()
Return the identified causal formula as a JSON-serialisable dictionary.
The dictionary mirrors the AST node hierarchy. Each node is a dict with an
"op"key identifying its type, plus type-specific keys:Binary operators (
+,-,*,/):{"op": "+", "op1": {...}, "op2": {...}}Conditional probability
P(vars | knowing):{"op": "P", "vars": [...], "knowing": [...]}Joint probability
P(vars):{"op": "P", "vars": [...]}Summation / marginalisation \(\sum_{\text{var}}\):
{"op": "sum", "var": "...", "term": {...}}
Returns
Noneif the effect is not identified (i.e.isIdentified()is False).- Returns:
The AST as a nested dict, or None if not identifiable.
- Return type:
dict or None
Examples
>>> import pyagrum as gum >>> bn = pyagrum.BayesNet.fastPrototype('X->Y->Z') >>> cm = pyagrum.CausalModel(bn) >>> ci = pyagrum.CausalImpact(cm, on='Z', doing='X') >>> import json >>> print(json.dumps(ci.toDict(), indent=2))
- toLatex(*, doOperatorPrefix=None, doOperatorSuffix=None)
Return a LaTeX string representation of the causal impact formula.
- Parameters:
doOperatorPrefix (str, optional) – LaTeX prefix for the do-operator. Defaults to the value in pyAgrum config.
doOperatorSuffix (str, optional) – LaTeX suffix for the do-operator. Defaults to the value in pyAgrum config.
- Returns:
LaTeX representation of the causal impact expression.
- Return type:
str
- toString()
Return a string representation of the identified causal formula.
- Returns:
Human-readable form of the formula (e.g. a sum-product expression over conditional probabilities).
- Return type:
str
Counterfactual reasoning
- pyagrum.counterfactual(cm, *, on, whatif, profile=None, values=None)
Compute a counterfactual distribution using Pearl’s twin network method.
Answers the question: ‘Given that we observed profile, what would on have been if whatif had been set as specified in values?’
The computation follows the three-step algorithm from Pearl (2018), The Book of Why, chapter 8: abduction (update parentless node priors from the profile), action (apply do(whatif) on the twin model), and prediction (evaluate the causal effect on the twin).
- Parameters:
cm (pyagrum.CausalModel) – The causal model.
on (str or set of str) – Target variable(s) of the counterfactual query. A single string is automatically converted to a one-element set.
whatif (str or set of str) – Variable(s) whose values are changed in the counterfactual scenario. A single string is automatically converted to a one-element set.
profile (dict of str → str, optional) – The factual observation as
{variable_name: value_name}. This grounds the counterfactual (step 1: abduction). Default is empty (no factual observation).values (dict of str → str, optional) – Counterfactual values for the whatif variables as
{variable_name: value_name}. If omitted, the full distribution over all whatif values is returned.
- Returns:
The counterfactual distribution P(on | do(whatif)) evaluated on the twin model, optionally sliced by values.
- Return type:
Examples
>>> import pyagrum as gum >>> bn = pyagrum.BayesNet.fastPrototype('X->Y->Z') >>> cm = pyagrum.CausalModel(bn) >>> t = pyagrum.counterfactual(cm, on='Z', whatif='X', ... profile={'Y': 'True'}, values={'X': 'False'})
- pyagrum.counterfactualModel(cm, profile=None, whatif=None)
Build the twin causal model for a counterfactual query.
Implements steps 1-2 of Pearl’s three-step counterfactual algorithm: compute the posterior of parentless (idiosyncratic) nodes in the observational BN given profile as evidence, then replace their priors in a copy of the model with those posteriors.
- Parameters:
cm (pyagrum.CausalModel) – The original causal model.
profile (dict of str → str, optional) – The factual observation as
{variable_name: value_name}. Default is empty (no observation; the twin model equals the original).whatif (str or set of str, optional) – Intervened variable(s) in the counterfactual scenario. These are excluded from the set of idiosyncratic nodes that get updated. A single string is automatically converted to a one-element set. Default is empty.
- Returns:
The twin causal model ready for the prediction step.
- Return type:
Examples
>>> import pyagrum as gum >>> bn = pyagrum.BayesNet.fastPrototype('X->Y->Z') >>> cm = pyagrum.CausalModel(bn) >>> twin = pyagrum.counterfactualModel(cm, profile={'Y': 'True'}, whatif='X')