cultivars.spectral.causality#
Geweke’s causality by frequency: when one variable’s history matters.
A time-domain Granger statement answers whether \(x\)’s history helps predict \(y\); Geweke’s (1982) spectral decomposition answers at which frequencies. In the pair’s innovation representation \((H, \Sigma)\), with the instantaneous correlation rotated out of \(x\)’s innovation, \(y\)’s spectrum splits at each frequency into an intrinsic part and a part attributable to \(x\)’s innovations, and the measure is the log ratio of the whole to the intrinsic,
so the integral over the circle recovers the time-domain measure – and a flat causality curve and a business-cycle-peaked one can carry the same headline Granger number while saying entirely different things about the economics.
Two objects, one honesty split. SpectralCausality computes the
unconditional pairwise measure for every ordered pair, purely as a
view of one fitted system: for a bivariate model the transfer function
and covariance are the model’s own; for a larger one, each pair’s
measure needs the innovation representation of that pair’s marginal
process, which is generally VARMA and matches no lag stack – so it is
recovered exactly from the pair’s marginal spectral density by Wilson’s
spectral factorization (the route of Dhamala, Rangarajan & Ding 2008),
still with nothing re-estimated. ConditionalSpectralCausality
computes Geweke’s (1984) conditional measure, which mathematically
requires a second, restricted model – there is no view-only shortcut
– so that model is an explicit argument the caller fits, never
something estimated silently inside a “view”, and the two models’
compatibility is reported as a diagnostic rather than assumed.
The family caveat applies with force: these are statements about fitted models, read as association across frequencies, not causal claims about the world; and pairwise unconditional measures can reflect common driving by a third variable, which is exactly what the conditional measure is for.
Layout. SpectralCausality produces a
SpectralCausalityResult, an (n, k, k) array of every ordered
pair with pair, band, and integrated readings;
ConditionalSpectralCausality produces a
ConditionalSpectralCausalityResult, one cause against every
effect. The numerics live in _core: _frequency_grid lays out
[0, pi], transfer_function evaluates \(\Psi(\omega)\) from
a lag stack, spectral_matrix forms \(\Psi \Sigma \Psi^\ast /
2\pi\), _pairwise_measure computes the bivariate measure from an
innovation representation, and ClosedSystemResult is the protocol a
fitted result must satisfy; _spectral_factor in _internals is
Wilson’s algorithm. The time-domain Granger test on the same relation
is granger_causality on the fitted result, and the spectral matrix
and coherence the measures decompose are in
density.
References
Geweke, J. (1982). Measurement of linear dependence and feedback between multiple time series. Journal of the American Statistical Association, 77(378), 304-313.
Geweke, J. (1984). Measures of conditional linear dependence and feedback between time series. Journal of the American Statistical Association, 79(388), 907-915.
Dhamala, M., Rangarajan, G., & Ding, M. (2008). Estimating Granger causality from Fourier and wavelet transforms of time series data. Physical Review Letters, 100(1), 018701.
Ding, M., Chen, Y., & Bressler, S. L. (2006). Granger causality: Basic theory and application to neuroscience. In Handbook of Time Series Analysis (pp. 437-460). Wiley.
Wilson, G. T. (1972). The factorization of matricial spectral densities. SIAM Journal on Applied Mathematics, 23(4), 420-426.
Example
A rate that feeds into output; the spectral measure integrates to the time-domain Granger measure, and the business-cycle band carries more of it than the short cycles:
>>> import numpy as np
>>> from cultivars.multivariate.reduced_form.vector_autoregression import VAR
>>> rng = np.random.default_rng(0)
>>> y = np.zeros((500, 2))
>>> for t in range(1, 500):
... y[t, 0] = 0.5 * y[t - 1, 0] + 0.4 * y[t - 1, 1] + rng.standard_normal()
... y[t, 1] = 0.5 * y[t - 1, 1] + rng.standard_normal()
>>> res = VAR(y, order=1, names=("gdp", "rate")).fit()
>>> causal = SpectralCausality(res, n_frequencies=64).compute()
>>> causal.integrated().round(3)
array([[0. , 0.154],
[0.001, 0. ]])
>>> cycle = causal.band("rate", "gdp", low_period=6, high_period=32)
>>> short = causal.band("rate", "gdp", low_period=2, high_period=4)
>>> round(cycle, 2), round(short, 2)
(0.26, 0.08)
Classes#
Geweke's (1984) conditional causality, from two fitted models. |
|
Geweke's conditional measure from one cause to every other variable. |
|
Unconditional pairwise Geweke causality of a fitted closed system. |
|
All ordered pairs' unconditional Geweke measures across frequency. |