conditional_forecast#
- cultivars.forecast.conditional.conditional_forecast(result, steps, conditions, *, n_draws=1000, seed=None)[source]#
Forecast a closed system given stated future values of some of its variables.
The Waggoner-Zha (1999) construction on whichever kind of result is passed. For a point result the unconditional mean path \(\mu\) is the result’s own
forecast(steps), the moving-average matrices \(\Psi_0, \dots, \Psi_{H-1}\) itsma_representation, and the impact matrix \(P\) the lower Cholesky factor ofsigma_u; the conditions become the restriction system\[R \varepsilon = r, \qquad r_i = c_{h_i, j_i} - \mu_{h_i, j_i},\]one row per conditioned cell, with \(R\) built from the rows of \(\Psi_j P\) that reach that cell, and
n_drawsshock paths are drawn from \(\mathcal{N}(\varepsilon^\ast, I - R^\top (R R^\top)^{-1} R)\) and propagated through the moving average. For a sampled result the same construction is repeated once per parameter draw:n_drawsdraws are picked evenly over the retained posterior, each supplies its own \(\mu\), \(\Psi\), and \(P\) frombeta_drawsandsigma_draws, and contributes one conditional path, so the paths mix parameter and shock uncertainty.- Parameters:
result (
object) – A fitted closed system. A point result must exposeforecast(steps),ma_representation(horizon),sigma_uandnames(every VAR-family result does); the conditional paths then carry shock uncertainty at the fitted parameters. A result carryingbeta_drawsandsigma_draws– the Bayesian VAR family – is propagated draw by draw, and the paths carry parameter uncertainty too.steps (
int) – Horizons ahead.conditions (
Mapping[str,Sequence[float|None]]) – Variable name to its conditioned path, one entry per horizon from the first,None(ornan) where the variable is free; a path shorter thanstepsleaves the remaining horizons free.n_draws (
int) – Conditional paths to simulate. Under a sampled result they are spread evenly over the retained parameter draws.
- Return type:
- Returns:
- Raises:
SpecificationError – If the horizon or draw count is not positive, a variable is unknown, nothing is conditioned, or the result cannot be propagated.
DimensionError – If a condition path is longer than the horizon.
NumericalError – If the conditions are linearly dependent or the covariance degenerates.
Note
The result is read by duck typing so that a system from outside the package can be conditioned on:
namesis required in every case, and then either the sampled triobeta_draws,sigma_draws,_stack_of(withendog,order,trend) or the point trioforecast,ma_representation,sigma_u. Aforecastthat returns the sampled families’(steps, k, 3)low/mean/high summary is read at its middle column. Conditions are hard, so a scenario that pins every variable at every horizon has no free shock directions and every path is the same; the modesty reading is then the only content. Under a sampled result the modesty norm is the average of \(\|\varepsilon^\ast\|^2\) over parameter draws, and its p-value is read against \(\chi^2_q\) as if it were a single draw’s norm.See also
ConditionalForecastResult– the record, with the modesty reading and the paths.VARResult– a point result whoseforecast,ma_representation, andsigma_uare what the point branch reads.fan_chart()– the bands the paths give.
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 point VAR with one variable pinned for two horizons, then a Bayesian VAR whose paths also mix over parameter draws:
>>> import numpy as np >>> from cultivars.multivariate.reduced_form.vector_autoregression import VAR >>> from cultivars.multivariate.large_dim.bayesian import BVAR >>> y = np.random.default_rng(0).standard_normal((120, 2)) >>> res = VAR(y, order=1).fit() >>> scenario = conditional_forecast(res, 3, {"y1": [0.5, 0.5]}, n_draws=200, seed=0) >>> scenario.n_conditions, scenario.paths.shape, scenario.parameter_uncertainty (2, (200, 3, 2), False) >>> bool(np.allclose(scenario.paths[:, :2, 0], 0.5)) True >>> posterior = BVAR(y, order=1).fit(n_draws=200, seed=0) >>> sampled = conditional_forecast(posterior, 3, {"y1": [0.5, 0.5]}, n_draws=50, seed=0) >>> sampled.source, sampled.parameter_uncertainty, sampled.n_draws ('BVARResult', True, 50)
A short path leaves later horizons free, and
Noneornanfrees a cell in the middle:>>> mixed = conditional_forecast( ... res, 3, {"y1": [0.5, None], "y2": [float("nan"), 0.2]}, n_draws=20, seed=0 ... ) >>> mixed.conditions array([[0.5, nan], [nan, 0.2], [nan, nan]]) >>> conditional_forecast(res, 3, {"y1": [None, None]}) Traceback (most recent call last): ... cultivars.exceptions.SpecificationError: every stated condition is None or nan; ...