FunctionalVARResult#

class cultivars.multivariate.reduced_form.functional.FunctionalVARResult(*, curves, grid, basis, basis_matrix, mean_curve, scores, factors, explained_variance=None, decay=None)[source]

Bases: _SummaryMixin

A fitted functional vector autoregression.

Each curve \(y_t(\cdot)\), observed on a grid of \(n\) points, is written as a mean curve plus a combination of \(r\) basis functions,

\[y_t(s) \approx \bar y(s) + \sum_{j=1}^{r} \xi_{tj}\, \phi_j(s), \qquad \xi_t = \sum_{i=1}^{p} A_i\, \xi_{t-i} + c + u_t,\]

and the dynamics live entirely in the score vector \(\xi_t\). The curve-level surface is the score surface mapped through the basis matrix \(\Phi = [\phi_j(s_k)]\): fitted curves are \(\bar y + \hat\xi_t \Phi'\), forecast curves are the factor forecast through \(\Phi'\), and the response of the whole curve to a factor shock is the factor impulse response through \(\Phi\).

Composition, stated plainly: factors is a complete VARResult on the basis scores, and every factor-level question – coefficients, standard errors, Granger causality, stability, residual diagnostics, factor impulse responses – is answered there, not re-exported here one method at a time. This object owns what the factor result cannot know: the basis, the projection’s quality, and the maps back to curve space.

Note

Two floors bound every curve-level number. The projection floor, reconstruction_rmse, is how far the data sit from their own best representation in the basis before any dynamics enter; no fitted or forecast curve can beat it. The factor VAR’s own fit is the second, and the one-step curve error is the two combined. Under "fpca" the basis is estimated from these curves, so the floor is in-sample optimal and the retained share of variance is reported; under the fixed bases it is whatever the chosen functions allow, and explained_variance is None because the question has no analogue. The basis is a mean-square choice, not a dynamic one: a variance-share target on level-dominated curves (yields, say) can keep a single level component and discard the slope and curvature movements the dynamics are about, which is what n_components is for.

Variables:
  • curves – The sample, one curve per row.

  • grid – The evaluation points, one per column of curves.

  • basis – Which basis family was used.

  • basis_matrix – The (n_points, r) basis the scores live in.

  • mean_curve – The curve subtracted before projection – the sample mean under "fpca", zero under the fixed bases, whose level factor plays that role itself.

  • scores – The (nobs, r) projected sample the factor VAR was fitted to.

  • factors – The fitted factor VAR, carrying the entire reduced-form surface at factor level.

  • explained_variance – Share of centered sample variance each retained component carries, under "fpca"; None otherwise.

  • decay – The Nelson-Siegel decay used, under "nelson-siegel"; None otherwise.

See also

  • FunctionalVAR – the model whose fit() returns this record and chooses the basis.

  • VARResult – the type of factors, where all factor-level inference lives.

  • term_structure – the yield-curve models that fix the Nelson-Siegel structure and add the pricing restrictions this basis does not impose.

References

Bosq, D. (2000). Linear Processes in Function Spaces. Springer.

Diebold, F. X., & Li, C. (2006). Forecasting the term structure of government bond yields. Journal of Econometrics, 130(2), 337-364.

Ramsay, J. O., & Silverman, B. W. (2005). Functional Data Analysis. Springer.

Example

Curves that share a fixed shape and move through a random-walk level and an autoregressive tilt. Two principal components carry the two movements, the reconstruction floor is a fraction of the noise, and the curve forecast, the fitted curves and the curve impulse responses are the factor surface pushed through the basis:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> grid = np.linspace(0.0, 1.0, 25)
>>> level = 0.1 * np.cumsum(rng.standard_normal(80))
>>> tilt = np.zeros(80)
>>> for t in range(1, 80):
...     tilt[t] = 0.7 * tilt[t - 1] + 0.1 * rng.standard_normal()
>>> shape = np.sin(np.pi * grid)
>>> curves = level[:, None] + tilt[:, None] * (grid - 0.5) + shape
>>> curves = curves + 0.01 * rng.standard_normal((80, 25))
>>> res = FunctionalVAR(curves, grid, order=1, basis="fpca", n_components=2).fit()
>>> res.k_factors, res.n_points, res.nobs, res.factors.names
(2, 25, 80, ('pc1', 'pc2'))
>>> res.explained_variance.round(3), bool(res.reconstruction_rmse < 0.02)
(array([0.991, 0.008]), True)
>>> res.fitted_curves().shape, res.forecast_curves(3).shape, res.irf_curves(4).shape
((79, 25), (3, 25), (5, 25, 2))
>>> bool(np.allclose(res.scores, (curves - res.mean_curve) @ res.basis_matrix))
True

Private methods#

_summary_table()

Build the structured summary.