API reference

andrey.granger#

andrey.granger(data, *, maxlag=2, cv=5)[source]#

Learn the lagged causal graph of a multivariate time series by lasso Granger causality.

A variable Granger-causes another when its past improves the prediction of the other’s present [1]. Each variable is regressed on the past maxlag steps of every variable by the lasso [2], with the penalty chosen by cross-validation; a nonzero coefficient is a lagged edge. Use it for one multivariate series whose effects take at least one time step.

Parameters:
  • data (ArrayLike) – A multivariate time series, one row per time step. Coerced to float64. A DataFrame’s column names become the structure’s labels.

  • maxlag (int) – Number of past time steps each regression uses, at least 1.

  • cv (int) – Number of cross-validation folds that choose each regression’s penalty, at least 2.

Returns:

Wraps a temporal TemporalStructure with lags 1 to maxlag and no lag 0. structure.lag(k) is a digraph with the edge i -> j when x_i(t - k) has a nonzero coefficient in the regression of x_j(t), self-loops included; structure.lag_weights[k - 1][i, j] is that coefficient, in the data’s units. ordering is None, and metadata["algorithm"] is "granger".

Return type:

StructureOutput

Raises:
  • TypeError – If maxlag or cv is not an int.

  • ValueError – If maxlag is less than 1, if cv is less than 2, if data has fewer than maxlag + cv rows, so that some fold would hold no time step, or if data has fewer than 2 columns.

Notes

An edge says that the past of one variable helps predict another beyond the past of every variable in data. Granger causality misses effects within one time step, and a common cause left out of data can produce edges.

Each regression is fitted on standardized variables, so the edges do not depend on the units of any variable; the coefficients are converted back to the data’s units. Cross-validation chooses the penalty that predicts best, which often keeps small coefficients on lags with no effect: judge the edges by the size of their weights. The folds are consecutive blocks of time steps, and the fit is deterministic. With andrey.config(num_workers=k), the regressions run on k threads and return the same coefficients.

The penalty is the plain lasso of Arnold et al. [2]. Shojaie and Michailidis [3] propose a truncating lasso penalty that also selects the lag order; it is not implemented here.

Limitations. Measured on simulated linear-Gaussian VAR(2) series with no same-step links, standardized, from 10 to 160 variables with 1,000 time steps, with maxlag=2. It found most true lagged edges but returned many false ones, so its median errors (missing and extra lagged edges) exceeded the empty graph’s at every size: on half the series at 10 variables and on all of them from 20 on. It assumes linear effects and tests no same-step effects. Each variable’s regression takes maxlag lags of every variable as inputs, so the work grows with maxlag and with the square of the number of variables.

References

Examples

>>> import numpy as np
>>> from andrey import granger
>>> rng = np.random.default_rng(0)
>>> x = rng.standard_normal(500)
>>> y = np.r_[0.0, 0.8 * x[:-1]] + rng.standard_normal(500)
>>> out = granger(np.column_stack([x, y]), maxlag=1)
>>> round(float(out.structure.lag_weights[0][0, 1]), 1)
0.8