encompassing#

cultivars.forecast.comparison.encompassing(realized, forecast_a, forecast_b, *, horizon=1)[source]#

Test whether forecast A encompasses forecast B (Harvey, Leybourne & Newbold, 1998).

A encompasses B when the optimal combination of the two, \((1 - \lambda) f_A + \lambda f_B\), puts no weight on B, so combining them would not help. The test asks whether

\[d_t = e_{A,t}\,(e_{A,t} - e_{B,t})\]

has a positive mean, one-sided, with the Diebold-Mariano long-run variance and the Harvey-Leybourne-Newbold correction; a rejection says B carries information A lacks, and the reported weight is the share \(\lambda\) B would get in a least-squares combination. Run it both ways to learn whether either forecast is redundant.

Parameters:
  • realized (ArrayLike) – (T,) outcomes.

  • forecast_a (ArrayLike) – (T,) point forecasts claimed to encompass.

  • forecast_b (ArrayLike) – (T,) point forecasts claimed to be encompassed, aligned origin by origin.

  • horizon (int) – The forecast horizon behind the series.

Return type:

EncompassingTest

Returns:

The EncompassingTest.

Raises:

Note

Encompassing is not the same question as accuracy. A worse forecast can still carry information the better one lacks, in which case neither encompasses the other and a combination beats both; and the better forecast can encompass the worse one, in which case the comparison is settled and combining is pointless. The two directions together, with a ForecastComparison, are the full reading.

See also

References

Harvey, D. I., Leybourne, S. J., & Newbold, P. (1998). Tests for forecast encompassing. Journal of Business & Economic Statistics, 16(2), 254-259.

Example

Two predictors that each explain part of the outcome: the one missing z does not encompass the one that has it, and the one that has both encompasses the one that lacks z:

>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> x, z = rng.standard_normal(200), rng.standard_normal(200)
>>> y = x + z + 0.5 * rng.standard_normal(200)
>>> forward = encompassing(y, x, x + z)
>>> bool(forward.pvalue < 0.01), bool(abs(forward.weight - 1.0) < 0.1)  # x lacks z
(True, True)
>>> reverse = encompassing(y, x + z, x)
>>> bool(reverse.pvalue > 0.05), bool(abs(reverse.weight) < 0.1)  # x + z has it
(True, True)
>>> encompassing(y, x, x)
Traceback (most recent call last):
    ...
cultivars.exceptions.SpecificationError: the two forecasts are numerically identical; ...