ConditionalForecastResult#
- class cultivars.forecast.conditional.ConditionalForecastResult(*, names, steps, conditions, unconditional, mean, paths, implied_shocks, modesty, modesty_pvalue, n_conditions, source, parameter_uncertainty)[source]#
A forecast of the whole system given stated future paths of some of it.
Waggoner and Zha (1999): the future is written in moving-average form,
\[y_{T+h} = \mu_h + \sum_{j=0}^{h-1} \Psi_j P\, \varepsilon_{T+h-j}, \qquad \varepsilon_t \sim \mathcal{N}(0, I_k),\]with \(P\) the lower Cholesky factor of the innovation covariance, so that each conditioned cell is one linear restriction on the stacked future shocks, \(R \varepsilon = r\), and the forecast is the system’s law given those restrictions: the minimum-norm shock path that delivers the conditions at the center,
\[\varepsilon^\ast = R^\top (R R^\top)^{-1} r,\]and the conditional Gaussian of the remaining shock directions around it, \(\varepsilon \mid R\varepsilon = r \sim \mathcal{N}\bigl( \varepsilon^\ast,\ I - R^\top (R R^\top)^{-1} R\bigr)\). Every conditioned cell is met exactly on every path; the free cells spread over what the conditions leave undetermined. Under a sampled result every retained parameter draw contributes its own conditional paths, so the bands carry parameter uncertainty as well.
The record also says whether the scenario is one the model believes. Under the model \(\|\varepsilon^\ast\|^2\) is \(\chi^2\) on the number of conditions \(q\), so
modesty\(= \|\varepsilon^\ast\|^2 / q\) is one when the scenario needs shocks of typical size and large when it needs shocks the model deems improbable (Leeper & Zha, 2003); the paths of a scenario like that describe a future agents would notice was engineered.- Variables:
names – Series labels.
steps – Horizons ahead.
conditions –
(steps, k)conditioned values,nanwhere free.unconditional –
(steps, k)mean path with no conditions imposed (averaged over draws under a sampled result).mean –
(steps, k)conditional mean path.paths –
(S, steps, k)conditional predictive paths; conditioned cells equal their conditions on every path.implied_shocks –
(steps, k)minimum-norm standardized shocks that deliver the conditions, in the orthogonalized (impact matrix) basis; averaged over draws under a sampled result.modesty –
||eps*||**2of the minimum-norm shocks relative to the number of conditions – one when the scenario needs shocks of typical size, large when it needs shocks the model deems improbable (Leeper & Zha, 2003).modesty_pvalue – Upper-tail chi-squared probability of
||eps*||**2onn_conditionsdegrees of freedom.n_conditions – Cells conditioned.
source – The result forecast.
parameter_uncertainty – Whether the paths mix over parameter draws.
Note
Conditions are hard: each conditioned cell is met exactly, so the 16-84% band of a conditioned cell is degenerate at the condition. Interval (soft) conditions, which would widen it, are not implemented. The record satisfies the
PredictiveResultprotocol throughforecast_paths(), so a fan chart, a density score, or a model combination reads it like any other density forecaster, except that the horizon is fixed at construction.See also
conditional_forecast()– the producer.fan_chart()– the bands the paths give.DensityScore– scores the paths against an outcome.
References
Waggoner, D. F., & Zha, T. (1999). Conditional forecasts in dynamic multivariate models. Review of Economics and Statistics, 81(4), 639-651.
Leeper, E. M., & Zha, T. (2003). Modest policy interventions. Journal of Monetary Economics, 50(8), 1673-1700.
Example
A two-variable VAR with the rate held at one for two quarters:
>>> import numpy as np >>> from cultivars.multivariate.reduced_form.vector_autoregression import VAR >>> rng = np.random.default_rng(0) >>> y = np.zeros((200, 2)) >>> for t in range(1, 200): ... y[t] = np.array([[0.5, 0.2], [0.1, 0.6]]) @ y[t - 1] + rng.standard_normal(2) >>> res = VAR(y, order=1, names=("gdp", "rate")).fit() >>> scenario = conditional_forecast(res, 4, {"rate": [1.0, 1.0, None, None]}, seed=0) >>> scenario.n_conditions, scenario.paths.shape, scenario.parameter_uncertainty (2, (1000, 4, 2), False) >>> scenario.conditioned[:, 1] array([ True, True, False, False]) >>> bool(np.allclose(scenario.paths[:, :2, 1], 1.0)) True >>> round(scenario.modesty, 2), round(scenario.modesty_pvalue, 3) (1.38, 0.251)
The same model asked to hold the rate at eight, a scenario it finds implausible:
>>> ambitious = conditional_forecast(res, 4, {"rate": [8.0] * 4}, seed=0) >>> round(ambitious.modesty, 1), bool(ambitious.modesty_pvalue < 1e-10) (25.8, True)
Attributes#
Series labels, in the result's column order. |
|
Horizons ahead, \(H\); the horizon the paths were simulated for. |
|
|
|
|
|
|
|
|
|
|
|
\(\|\varepsilon^\ast\|^2 / q\), the shock norm per condition. |
|
Upper-tail \(\chi^2_q\) probability of \(\|\varepsilon^\ast\|^2\). |
|
Cells conditioned, \(q\), the count of finite entries in |
|
The class name of the result forecast, for the summary title. |
|
Whether the paths mix over parameter draws (a sampled result) or hold them fixed. |
|
|
|
Series forecast, \(k\), the last axis of every array. |
|
Conditional paths, \(S\), the first axis of |
Methods#
|
The conditional paths, in the shape every scorer and fan chart reads. |
|
|
|
The estimation summary, renderable as text, HTML, or a dataframe. |