GrangerCausalityResult#
- class impulso.results.GrangerCausalityResult(*, cause, effect, n_lags_tested, n_lags_fitted, augmentation, augmentation_source, standardize, scale, rope=None, coef_draws, integration_order_result=None)[source]#
Bases:
ImpulsoBaseModelPosterior Granger-causal strength for one ordered cause-effect pair.
The headline quantity is the Euclidean norm of the tested lag coefficients of cause in the effect equation — ‖b‖ = sqrt(sum_k b_k^2), where b_k multiplies cause_{t-k} — evaluated draw by draw, so the result is a posterior for a magnitude. That separates “the effect is small” from “the effect is imprecisely estimated”, which a Wald-style quadratic form (which divides through by the posterior covariance) deliberately conflates. Per-lag posteriors are reported alongside the norm, so a single dominant lag stays visible instead of being buried in it.
Granger causality is conditional predictive precedence, not intervention: the statement is that the past of cause improves the prediction of effect beyond effect’s own past, within this system of variables. Omitted drivers, temporal aggregation, and simultaneous feedback each break the step from that statement to a mechanism. The pair is ordered, and the two orderings are separate queries with unrelated answers: granger_causality(a, b) tells you nothing about granger_causality(b, a). Either direction, both, or neither may hold, so each direction has to be asked for explicitly and reported as its own result.
What `p_rope` is, and what it is not. p_rope is the posterior probability that the strength norm falls inside the region of practical equivalence (ROPE) the analyst supplied: P(‖b‖ < rope | data). It is NOT P(no causality), and it is not a Bayes factor. Under Impulso’s continuous coefficient priors the event b = 0 has probability zero both before and after seeing the data, so no dataset can raise it — a genuine posterior probability of exact non-causality needs a prior that puts point mass on the null (spike-and-slab / edge inclusion), which Impulso does not fit. What p_rope quantifies is the posterior probability that the tested coefficients are jointly practically negligible at the magnitude you declared. Choosing rope is the analyst’s job and there is no default, because there is no data-free notion of “small enough”; that the choice is explicit and recorded is the honesty of the statement. With rope=None the result reports the distribution only and p_rope is None.
Reporting units. With standardize=True (the default) the draws are multiplied by sd(cause) / sd(effect), both sample standard deviations of the estimation data, so a rope is read in standard deviations of the effect per standard deviation of the cause. The factor is recorded in scale. Under lag augmentation the model is fitted in levels, and the sample standard deviations of integrated series are inflated by their trends, so standardised magnitudes are most meaningful compared within one fitted model rather than across models.
Toda-Yamamoto metadata. Under the lag-augmented procedure the model is fitted with n_lags_fitted = n_lags_tested + augmentation lags and only the first n_lags_tested are tested; the augmented lags are never reported and n_lags_tested is never silently changed to match the fit. augmentation_source records where the augmentation came from: “none” when there is none, “user” when it was passed explicitly, and “integration_order” when the integration-order diagnostics were consulted — including the case where they returned d_max = 0, so the record shows that they were consulted. Those diagnostics are attached as integration_order_result whenever they were consulted. IntegrationOrderResult.d_max is a floor rather than a finding whenever its inconclusive list is non-empty (a variable still integrated at max_order is recorded at max_order), which is why toda_yamamoto refuses to run in that case rather than under-augmenting silently.
- Parameters:
cause (str)
effect (str)
n_lags_tested (int)
n_lags_fitted (int)
augmentation (int)
augmentation_source (Literal['none', 'integration_order', 'user'])
standardize (bool)
scale (float)
rope (Annotated[float | None, Gt(gt=0)])
coef_draws (ndarray)
integration_order_result (IntegrationOrderResult | None)
- augmentation_source#
“none”, “user”, or “integration_order”.
- Type:
Literal[‘none’, ‘integration_order’, ‘user’]
- scale#
The multiplier applied to the raw coefficient draws — the standardisation factor, or 1.0 when standardize is False.
- Type:
- rope#
The region of practical equivalence, in the reporting units, or None when none was supplied.
- Type:
float | None
- coef_draws#
Per-lag coefficient draws in the reporting units, shape (chains, draws, n_lags_tested), lag 1 first.
- Type:
- integration_order_result#
The integration-order diagnostics that fixed the augmentation, when they were consulted.
- Type:
- median()[source]#
Posterior median of the strength norm.
- Returns:
Median of ‖b‖ across all draws, in the reporting units.
- Return type:
- model_config = {'arbitrary_types_allowed': True, 'frozen': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- property p_rope: float | None#
Posterior probability that ‖b‖ falls inside the ROPE.
None when no rope was supplied. Read the class docstring before reporting it: this is not the probability of no causality.
- summary(prob=0.89)[source]#
Per-lag and overall posterior summary.
- Parameters:
prob (float) – Probability mass for the HDI columns. Default 0.89.
- Returns:
DataFrame indexed by [“L1”, …, “Lp”, “norm”] with columns median, hdi_lower, hdi_upper. When a rope was supplied a p_rope column is added, filled only on the norm row — the ROPE is a statement about the joint magnitude, not about any one lag.
- Return type: