SparseVARResult#

class cultivars.multivariate.large_dim.sparse.SparseVARResult(*, endog, names, order, trend, coefficients, deterministic, sigma_u, resid, fittedvalues, penalty, lam, lambda_path, cv_errors, n_nonzero, nobs)[source]

Bases: _SummaryMixin

A fitted sparse VAR: the coefficients that survived, and the zeros.

The minimizer of

\[\tfrac{1}{2}\sum_t \bigl\lVert y_t - c - \textstyle\sum_{i=1}^{p} A_i y_{t-i} \bigr\rVert^2 + \lambda\, P(A_1, \ldots, A_p)\]

on the standardized panel, mapped back to original units, with \(P\) the lasso, adaptive-lasso, SCAD, MCP or lag-group penalty and \(\lambda\) either stated or chosen by rolling-origin one-step forecast error along a geometric path. Satisfies the closed-system contract, so identification models and Spillover consume it directly; the support it selects is read as a directed Granger network by granger_adjacency().

Deliberately absent: llf, information criteria, and standard errors. A penalized objective has no likelihood, its complexity is the nonzero count, and post-selection inference at this generality is an open problem the result will not paper over with naïve intervals.

Note

sigma_u is the residual scatter divided by the sample size less the average number of active regressors per equation – the penalized nonzeros plus the deterministic terms – so it is the degrees-of-freedom-corrected analogue of the OLS estimate, not the raw resid.T @ resid / nobs. lam is on the standardized scale and comparable across equations but not across penalty families, whose paths start from different zeroing levels. granger_adjacency reads the support at threshold=0.0 by default, so every surviving coefficient is an edge; the forecast-optimal penalty keeps many small ones, and a positive threshold or a nonconvex penalty gives the sparser network when support recovery is the object.

Variables:
  • endog – The observed panel.

  • names – Variable labels, in column order.

  • order – Autoregressive order.

  • trend – Deterministic specification.

  • coefficients – (p, k, k) lag stack at the selected penalty.

  • deterministic – Unpenalized deterministic coefficients.

  • sigma_u – Residual covariance, corrected by the average per-equation nonzero count.

  • resid – Residuals over the effective sample.

  • fittedvalues – One-step means over the effective sample.

  • penalty – The penalty family estimated under.

  • lam – The penalty level estimated at.

  • lambda_path – The candidate path, descending; empty when the caller stated lam.

  • cv_errors – Rolling one-step squared forecast errors along the path; empty when the caller stated lam.

  • n_nonzero – Nonzero penalized coefficients at the solution.

  • nobs – Effective sample size.

See also

  • SparseVAR – the model that produces this record.

  • GraphicalVARResult – this record plus the sparse residual precision.

  • GibbsBVARResult – shrinkage by prior rather than selection, with inclusion probabilities instead of a support.

References

Basu, S., & Michailidis, G. (2015). Regularized estimation in sparse high-dimensional time series models. Annals of Statistics, 43(4), 1535-1567.

Nicholson, W. B., Matteson, D. S., & Bien, J. (2017). VARX-L: Structured regularization for large vector autoregressions with exogenous variables. International Journal of Forecasting, 33(3), 627-651.

Example

A six-variable VAR(2) with two cross-variable links; the lasso support recovers them and the moving-average matrices follow the recursion:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> k, n = 6, 300
>>> first = 0.5 * np.eye(k)
>>> first[1, 0], first[3, 2] = 0.3, -0.3
>>> second = np.zeros((k, k))
>>> second[0, 0] = 0.2
>>> y = np.zeros((n, k))
>>> for t in range(2, n):
...     y[t] = first @ y[t - 1] + second @ y[t - 2] + rng.standard_normal(k)
>>> res = SparseVAR(y, order=2).fit(penalty="lasso")
>>> res.penalty, res.k_endog, res.nobs, res.coefficients.shape
('lasso', 6, 298, (2, 6, 6))
>>> res.lambda_path.shape, res.cv_errors.shape, bool(res.sparsity > 0.5)
((20,), (20,), True)
>>> edges = res.granger_adjacency(threshold=0.15)
>>> [(i, j) for i in range(k) for j in range(k) if edges[i, j]]
[(1, 0), (3, 2)]
>>> psi = res.ma_representation(3)
>>> bool(np.allclose(psi[2], res.coefficients[0] @ psi[1] + res.coefficients[1]))
True

Private methods#

_from_fit(fit, model)

Assemble the public result from a raw fit and its specification.

_summary_table()

Build the structured summary.