diff --git a/hierarchicalforecast/methods.py b/hierarchicalforecast/methods.py
index 0c2e4e29..e3164c65 100644
--- a/hierarchicalforecast/methods.py
+++ b/hierarchicalforecast/methods.py
@@ -88,6 +88,8 @@ class BottomUp:
- [Orcutt, G.H., Watts, H.W., & Edwards, J.B.(1968). \"Data aggregation and information loss\". The American
Economic Review, 58 , 773{787)](http://www.jstor.org/stable/1815532).
"""
+ insample = False
+
def reconcile(self,
S: np.ndarray,
y_hat: np.ndarray,
@@ -253,12 +255,13 @@ class TopDown:
def __init__(self,
method: str):
self.method = method
+ self.insample = method in ['average_proportions', 'proportion_averages']
def reconcile(self,
S: np.ndarray,
y_hat: np.ndarray,
- y_insample: np.ndarray,
tags: Dict[str, np.ndarray],
+ y_insample: Optional[np.ndarray] = None,
sigmah: Optional[np.ndarray] = None,
level: Optional[List[int]] = None,
bootstrap: bool = False,
@@ -268,6 +271,8 @@ def reconcile(self,
**Parameters:**
`S`: Summing matrix of size (`base`, `bottom`).
`y_hat`: Forecast values of size (`base`, `horizon`).
+ `tags`: Each key is a level and each value its `S` indices.
+ `y_insample`: Insample values of size (`base`, `insample_size`). Optional for `forecast_proportions` method.
`idx_bottom`: Indices corresponding to the bottom level of `S`, size (`bottom`).
`sigmah`: float, estimate of the standard deviation of the h-step forecast of size (`base`, `horizon`)
`level`: float list 0-100, confidence levels for prediction intervals.
@@ -345,7 +350,7 @@ def middle_out(S: np.ndarray,
counter += idxs_len
td = top_down(S_node,
y_hat[idxs_node],
- y_insample[idxs_node],
+ y_insample[idxs_node] if y_insample is not None else None,
levels_node_,
method=top_down_method)
reconciled[idxs_node] = td['mean']
@@ -375,19 +380,20 @@ def __init__(self,
top_down_method: str):
self.middle_level = middle_level
self.top_down_method = top_down_method
+ self.insample = top_down_method in ['average_proportions', 'proportion_averages']
def reconcile(self,
S: np.ndarray,
y_hat: np.ndarray,
- y_insample: np.ndarray,
- tags: Dict[str, np.ndarray]):
+ tags: Dict[str, np.ndarray],
+ y_insample: Optional[np.ndarray] = None):
"""Middle Out Reconciliation Method.
**Parameters:**
`S`: Summing matrix of size (`base`, `bottom`).
`y_hat`: Forecast values of size (`base`, `horizon`).
- `y_insample`: Insample values of size (`base`, `insample_size`).
- `levels`: Each key is a level and each value its `S` indices.
+ `tags`: Each key is a level and each value its `S` indices.
+ `y_insample`: Insample values of size (`base`, `insample_size`). Only used for `forecast_proportions`
**Returns:**
`y_tilde`: Reconciliated y_hat using the Middle Out approach.
@@ -471,7 +477,7 @@ class MinTrace:
\mathbf{S}^{\intercal}\mathbf{W}^{-1}_{h}$$
**Parameters:**
- `method`: str, one of `ols`, `wls_struct`, `wls_var`, `mint_shrink`, `mint_co`.
+ `method`: str, one of `ols`, `wls_struct`, `wls_var`, `mint_shrink`, `mint_cov`.
**References:**
- [Wickramasuriya, S. L., Athanasopoulos, G., & Hyndman, R. J. (2019). \"Optimal forecast reconciliation for
@@ -481,12 +487,13 @@ class MinTrace:
def __init__(self,
method: str):
self.method = method
+ self.insample = method in ['wls_var', 'mint_cov', 'mint_shrink']
def reconcile(self,
S: np.ndarray,
y_hat: np.ndarray,
- y_insample: np.ndarray,
- y_hat_insample: np.ndarray,
+ y_insample: Optional[np.ndarray] = None,
+ y_hat_insample: Optional[np.ndarray] = None,
sigmah: Optional[np.ndarray] = None,
level: Optional[List[int]] = None,
bootstrap: bool = False,
@@ -496,7 +503,8 @@ def reconcile(self,
**Parameters:**
`S`: Summing matrix of size (`base`, `bottom`).
`y_hat`: Forecast values of size (`base`, `horizon`).
- `idx_bottom`: Indices corresponding to the bottom level of `S`, size (`bottom`).
+ `y_insample`: Insample values of size (`base`, `insample_size`). Only used by `wls_var`, `mint_cov`, `mint_shrink`
+ `y_hat_insample`: Insample fitted values of size (`base`, `insample_size`). Only used by `wls_var`, `mint_cov`, `mint_shrink`
`sigmah`: float, estimate of the standard deviation of the h-step forecast of size (`base`, `horizon`)
`level`: float list 0-100, confidence levels for prediction intervals.
`bootstrap`: bool, whether or not to use bootstraped prediction intervals, alternative normality assumption.
@@ -560,12 +568,11 @@ def __init__(self,
raise ValueError(f"Optimal Combination class does not support method: \"{method}\"")
self.method = method
+ self.insample = False
def reconcile(self,
S: np.ndarray,
y_hat: np.ndarray,
- y_insample: np.ndarray = None,
- y_hat_insample: np.ndarray = None,
sigmah: Optional[np.ndarray] = None,
level: Optional[List[int]] = None,
bootstrap: bool = False,
@@ -575,7 +582,6 @@ def reconcile(self,
**Parameters:**
`S`: Summing matrix of size (`base`, `bottom`).
`y_hat`: Forecast values of size (`base`, `horizon`).
- `idx_bottom`: Indices corresponding to the bottom level of `S`, size (`bottom`).
`sigmah`: float, estimate of the standard deviation of the h-step forecast of size (`base`, `horizon`)
`level`: float list 0-100, confidence levels for prediction intervals.
`bootstrap`: bool, whether or not to use bootstraped prediction intervals, alternative normality assumption.
@@ -586,8 +592,6 @@ def reconcile(self,
"""
return optimal_combination(S=S,
y_hat=y_hat,
- y_insample=y_insample,
- y_hat_insample=y_hat_insample,
method=self.method, sigmah=sigmah,
level=level, bootstrap=bootstrap,
bootstrap_samples=bootstrap_samples)
@@ -704,6 +708,7 @@ def __init__(self,
lambda_reg: float = 1e-2):
self.method = method
self.lambda_reg = lambda_reg
+ self.insample = True
def reconcile(self,
S: np.ndarray,
diff --git a/nbs/methods.ipynb b/nbs/methods.ipynb
index b101ae89..7a490a25 100644
--- a/nbs/methods.ipynb
+++ b/nbs/methods.ipynb
@@ -149,6 +149,8 @@
" - [Orcutt, G.H., Watts, H.W., & Edwards, J.B.(1968). \\\"Data aggregation and information loss\\\". The American \n",
" Economic Review, 58 , 773{787)](http://www.jstor.org/stable/1815532).\n",
" \"\"\"\n",
+ " insample = False\n",
+ " \n",
" def reconcile(self,\n",
" S: np.ndarray,\n",
" y_hat: np.ndarray,\n",
@@ -516,12 +518,13 @@
" def __init__(self, \n",
" method: str):\n",
" self.method = method\n",
+ " self.insample = method in ['average_proportions', 'proportion_averages']\n",
" \n",
" def reconcile(self, \n",
" S: np.ndarray,\n",
" y_hat: np.ndarray,\n",
- " y_insample: np.ndarray,\n",
" tags: Dict[str, np.ndarray],\n",
+ " y_insample: Optional[np.ndarray] = None,\n",
" sigmah: Optional[np.ndarray] = None,\n",
" level: Optional[List[int]] = None,\n",
" bootstrap: bool = False,\n",
@@ -531,6 +534,8 @@
" **Parameters:**
\n",
" `S`: Summing matrix of size (`base`, `bottom`).
\n",
" `y_hat`: Forecast values of size (`base`, `horizon`).
\n",
+ " `tags`: Each key is a level and each value its `S` indices.
\n",
+ " `y_insample`: Insample values of size (`base`, `insample_size`). Optional for `forecast_proportions` method.
\n",
" `idx_bottom`: Indices corresponding to the bottom level of `S`, size (`bottom`).
\n",
" `sigmah`: float, estimate of the standard deviation of the h-step forecast of size (`base`, `horizon`)
\n",
" `level`: float list 0-100, confidence levels for prediction intervals.
\n",
@@ -583,15 +588,26 @@
"# but it is not a general case\n",
"for method in ['forecast_proportions', 'average_proportions', 'proportion_averages']:\n",
" cls_top_down = TopDown(method=method)\n",
- " test_close(\n",
- " cls_top_down(\n",
- " S=S, \n",
- " y_hat=S @ y_hat_bottom, \n",
- " y_insample=S @ y_bottom, \n",
- " tags=tags\n",
- " )['mean'],\n",
- " S @ y_hat_bottom\n",
- " )"
+ " if cls_top_down.insample:\n",
+ " assert method in ['average_proportions', 'proportion_averages']\n",
+ " test_close(\n",
+ " cls_top_down(\n",
+ " S=S, \n",
+ " y_hat=S @ y_hat_bottom, \n",
+ " y_insample=S @ y_bottom, \n",
+ " tags=tags\n",
+ " )['mean'],\n",
+ " S @ y_hat_bottom\n",
+ " )\n",
+ " else:\n",
+ " test_close(\n",
+ " cls_top_down(\n",
+ " S=S, \n",
+ " y_hat=S @ y_hat_bottom, \n",
+ " tags=tags\n",
+ " )['mean'],\n",
+ " S @ y_hat_bottom\n",
+ " )"
]
},
{
@@ -689,7 +705,7 @@
" counter += idxs_len\n",
" td = top_down(S_node, \n",
" y_hat[idxs_node], \n",
- " y_insample[idxs_node], \n",
+ " y_insample[idxs_node] if y_insample is not None else None, \n",
" levels_node_, \n",
" method=top_down_method)\n",
" reconciled[idxs_node] = td['mean']\n",
@@ -733,19 +749,20 @@
" top_down_method: str):\n",
" self.middle_level = middle_level\n",
" self.top_down_method = top_down_method \n",
+ " self.insample = top_down_method in ['average_proportions', 'proportion_averages']\n",
" \n",
" def reconcile(self, \n",
" S: np.ndarray,\n",
" y_hat: np.ndarray,\n",
- " y_insample: np.ndarray,\n",
- " tags: Dict[str, np.ndarray]):\n",
+ " tags: Dict[str, np.ndarray],\n",
+ " y_insample: Optional[np.ndarray] = None):\n",
" \"\"\"Middle Out Reconciliation Method.\n",
"\n",
" **Parameters:**
\n",
" `S`: Summing matrix of size (`base`, `bottom`).
\n",
" `y_hat`: Forecast values of size (`base`, `horizon`).
\n",
- " `y_insample`: Insample values of size (`base`, `insample_size`).
\n",
- " `levels`: Each key is a level and each value its `S` indices.
\n",
+ " `tags`: Each key is a level and each value its `S` indices.
\n",
+ " `y_insample`: Insample values of size (`base`, `insample_size`). Only used for `forecast_proportions`
\n",
"\n",
" **Returns:**
\n",
" `y_tilde`: Reconciliated y_hat using the Middle Out approach.\n",
@@ -792,15 +809,26 @@
"# but it is not a general case\n",
"for method in ['forecast_proportions', 'average_proportions', 'proportion_averages']:\n",
" cls_middle_out = MiddleOut(middle_level='level2', top_down_method=method)\n",
- " test_close(\n",
- " cls_middle_out(\n",
- " S=S, \n",
- " y_hat=S @ y_hat_bottom, \n",
- " y_insample=S @ y_bottom, \n",
- " tags=tags\n",
- " )['mean'],\n",
- " S @ y_hat_bottom\n",
- " )"
+ " if cls_middle_out.insample:\n",
+ " assert method in ['average_proportions', 'proportion_averages']\n",
+ " test_close(\n",
+ " cls_middle_out(\n",
+ " S=S, \n",
+ " y_hat=S @ y_hat_bottom, \n",
+ " y_insample=S @ y_bottom, \n",
+ " tags=tags\n",
+ " )['mean'],\n",
+ " S @ y_hat_bottom\n",
+ " )\n",
+ " else:\n",
+ " test_close(\n",
+ " cls_middle_out(\n",
+ " S=S, \n",
+ " y_hat=S @ y_hat_bottom, \n",
+ " tags=tags\n",
+ " )['mean'],\n",
+ " S @ y_hat_bottom\n",
+ " )"
]
},
{
@@ -901,7 +929,7 @@
" \\mathbf{S}^{\\intercal}\\mathbf{W}^{-1}_{h}$$\n",
" \n",
" **Parameters:**
\n",
- " `method`: str, one of `ols`, `wls_struct`, `wls_var`, `mint_shrink`, `mint_co`.
\n",
+ " `method`: str, one of `ols`, `wls_struct`, `wls_var`, `mint_shrink`, `mint_cov`.
\n",
"\n",
" **References:**
\n",
" - [Wickramasuriya, S. L., Athanasopoulos, G., & Hyndman, R. J. (2019). \\\"Optimal forecast reconciliation for\n",
@@ -911,12 +939,13 @@
" def __init__(self, \n",
" method: str):\n",
" self.method = method\n",
+ " self.insample = method in ['wls_var', 'mint_cov', 'mint_shrink']\n",
"\n",
" def reconcile(self, \n",
" S: np.ndarray,\n",
" y_hat: np.ndarray,\n",
- " y_insample: np.ndarray,\n",
- " y_hat_insample: np.ndarray,\n",
+ " y_insample: Optional[np.ndarray] = None,\n",
+ " y_hat_insample: Optional[np.ndarray] = None,\n",
" sigmah: Optional[np.ndarray] = None,\n",
" level: Optional[List[int]] = None,\n",
" bootstrap: bool = False,\n",
@@ -926,7 +955,8 @@
" **Parameters:**
\n",
" `S`: Summing matrix of size (`base`, `bottom`).
\n",
" `y_hat`: Forecast values of size (`base`, `horizon`).
\n",
- " `idx_bottom`: Indices corresponding to the bottom level of `S`, size (`bottom`).
\n",
+ " `y_insample`: Insample values of size (`base`, `insample_size`). Only used by `wls_var`, `mint_cov`, `mint_shrink`
\n",
+ " `y_hat_insample`: Insample fitted values of size (`base`, `insample_size`). Only used by `wls_var`, `mint_cov`, `mint_shrink`
\n",
" `sigmah`: float, estimate of the standard deviation of the h-step forecast of size (`base`, `horizon`)
\n",
" `level`: float list 0-100, confidence levels for prediction intervals.
\n",
" `bootstrap`: bool, whether or not to use bootstraped prediction intervals, alternative normality assumption.
\n",
@@ -985,15 +1015,25 @@
"#| hide\n",
"for method in ['ols', 'wls_struct', 'wls_var', 'mint_shrink']:\n",
" cls_min_trace = MinTrace(method=method)\n",
- " test_close(\n",
- " cls_min_trace(\n",
- " S=S, \n",
- " y_hat=S @ y_hat_bottom, \n",
- " y_insample=S @ y_bottom,\n",
- " y_hat_insample=S @ y_hat_bottom_insample\n",
- " )['mean'],\n",
- " S @ y_hat_bottom\n",
- " )\n",
+ " if cls_min_trace.insample:\n",
+ " assert method in ['wls_var', 'mint_cov', 'mint_shrink']\n",
+ " test_close(\n",
+ " cls_min_trace(\n",
+ " S=S, \n",
+ " y_hat=S @ y_hat_bottom, \n",
+ " y_insample=S @ y_bottom,\n",
+ " y_hat_insample=S @ y_hat_bottom_insample\n",
+ " )['mean'],\n",
+ " S @ y_hat_bottom\n",
+ " )\n",
+ " else:\n",
+ " test_close(\n",
+ " cls_min_trace(\n",
+ " S=S, \n",
+ " y_hat=S @ y_hat_bottom, \n",
+ " )['mean'],\n",
+ " S @ y_hat_bottom\n",
+ " )\n",
"with ExceptionExpected(regex='min_trace (mint_cov)*'):\n",
" cls_min_trace = MinTrace(method='mint_cov')\n",
" cls_min_trace(\n",
@@ -1101,12 +1141,11 @@
" raise ValueError(f\"Optimal Combination class does not support method: \\\"{method}\\\"\")\n",
"\n",
" self.method = method\n",
+ " self.insample = False\n",
"\n",
" def reconcile(self,\n",
" S: np.ndarray,\n",
" y_hat: np.ndarray,\n",
- " y_insample: np.ndarray = None,\n",
- " y_hat_insample: np.ndarray = None,\n",
" sigmah: Optional[np.ndarray] = None,\n",
" level: Optional[List[int]] = None,\n",
" bootstrap: bool = False,\n",
@@ -1116,7 +1155,6 @@
" **Parameters:**
\n",
" `S`: Summing matrix of size (`base`, `bottom`).
\n",
" `y_hat`: Forecast values of size (`base`, `horizon`).
\n",
- " `idx_bottom`: Indices corresponding to the bottom level of `S`, size (`bottom`).
\n",
" `sigmah`: float, estimate of the standard deviation of the h-step forecast of size (`base`, `horizon`)
\n",
" `level`: float list 0-100, confidence levels for prediction intervals.
\n",
" `bootstrap`: bool, whether or not to use bootstraped prediction intervals, alternative normality assumption.
\n",
@@ -1127,8 +1165,6 @@
" \"\"\"\n",
" return optimal_combination(S=S,\n",
" y_hat=y_hat,\n",
- " y_insample=y_insample,\n",
- " y_hat_insample=y_hat_insample,\n",
" method=self.method, sigmah=sigmah,\n",
" level=level, bootstrap=bootstrap,\n",
" bootstrap_samples=bootstrap_samples)\n",
@@ -1338,6 +1374,7 @@
" lambda_reg: float = 1e-2):\n",
" self.method = method\n",
" self.lambda_reg = lambda_reg\n",
+ " self.insample = True\n",
"\n",
" def reconcile(self, \n",
" S: np.ndarray,\n",
@@ -1480,13 +1517,6 @@
"### Bootstraped Prediction Intervals\n",
"- [Puwasala Gamakumara Ph. D. dissertation. Monash University, Econometrics and Business Statistics. \"Probabilistic Forecast Reconciliation\"](https://bridges.monash.edu/articles/thesis/Probabilistic_Forecast_Reconciliation_Theory_and_Applications/11869533)"
]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
}
],
"metadata": {