{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "47429a5c",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| hide\n",
    "!pip install -Uqq nixtla hierarchicalforecast"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca7f78a8",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| hide\n",
    "from nixtla.utils import in_colab"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96ff2d37",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| hide\n",
    "IN_COLAB = in_colab()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36b5fbe2",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| hide\n",
    "if not IN_COLAB:\n",
    "    from nixtla.utils import colab_badge\n",
    "    from itertools import product\n",
    "    from dotenv import load_dotenv"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0241d5c1-8d11-4cb9-9259-cda5fffa7ea8",
   "metadata": {},
   "source": [
    "# Hierarchical forecasting"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f73b521-05f2-4c53-b44c-d988bf8fe7b9",
   "metadata": {},
   "source": [
    "In forecasting, we often find ourselves in need of forecasts for both lower- and higher (temporal) granularities, such as product demand forecasts but also product category or product department forecasts. These granularities can be formalized through the use of a hierarchy. In hierarchical forecasting, we create forecasts that are coherent with respect to a pre-specified hierarchy of the underlying time series.\n",
    "\n",
    "With TimeGPT, we can create forecasts for multiple time series. We can subsequently post-process these forecasts using hierarchical forecasting techniques of [HierarchicalForecast](https://nixtlaverse.nixtla.io/hierarchicalforecast/index.html)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cdf86af8-08c0-4118-bd6b-0b88a6eac3f7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/markdown": [
       "[![](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Nixtla/nixtla/blob/main/nbs/docs/tutorials/14_hierarchical_forecasting.ipynb)"
      ],
      "text/plain": [
       "<IPython.core.display.Markdown object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#| echo: false\n",
    "if not IN_COLAB:\n",
    "    load_dotenv()\n",
    "    colab_badge('docs/tutorials/14_hierarchical_forecasting')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2997eb35",
   "metadata": {},
   "source": [
    "## 1. Import packages\n",
    "First, we import the required packages and initialize the Nixtla client."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8a5181e9-634c-4002-9c22-d65f0701fa14",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "from nixtla import NixtlaClient"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9872d00d-ecb1-4a14-9813-bf0c56e70392",
   "metadata": {},
   "outputs": [],
   "source": [
    "nixtla_client = NixtlaClient(\n",
    "    # defaults to os.environ.get(\"NIXTLA_API_KEY\")\n",
    "    api_key = 'my_api_key_provided_by_nixtla'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aacbea74",
   "metadata": {},
   "source": [
    "> 👍 Use an Azure AI endpoint\n",
    ">\n",
    "> To use an Azure AI endpoint, set the `base_url` argument:\n",
    ">\n",
    "> `nixtla_client = NixtlaClient(base_url=\"you azure ai endpoint\", api_key=\"your api_key\")`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50eac17f-3c72-4f68-988a-1d5d877dff2b",
   "metadata": {},
   "outputs": [],
   "source": [
    "#| hide\n",
    "if not IN_COLAB:\n",
    "    nixtla_client = NixtlaClient()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26b99aff-aa07-44a1-b0e1-e8301f7f62bf",
   "metadata": {},
   "source": [
    "## 2. Load data"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c77268e7",
   "metadata": {},
   "source": [
    "We use the Australian Tourism dataset, from [Forecasting, Principles and Practices](https://otexts.com/fpp3/) which contains data on Australian Tourism. We are interested in forecasts for Australia's 7 States, 27 Zones and 76 Regions. This constitutes a hierarchy, where forecasts for the lower levels (e.g. the regions Sidney, Blue Mountains and Hunter) should be coherent with the forecasts of the higher levels (e.g. New South Wales)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81f2b918",
   "metadata": {},
   "source": [
    "<img src=\"https://github.com/Nixtla/nixtla/blob/main/nbs/img/australia_tourism.png?raw=true\" alt=\"Map of Australia color coded by state. The states are from west to east, and then north to south - Western Australia, Northern Territory, South Australia, Queensland, New South Wales, Victoria. Australian Capital Territory is a small area within New South Wales. Tasmania is an island to the southeast.\" width=\"700\" />\n",
    "<img src=\"https://github.com/Nixtla/nixtla/blob/main/nbs/img/australia_hierarchy.png?raw=true\" alt=\"Australia hierarchy. Australia at the top with New South Wales and Queenslad below. Sidney, Blue Mountains, and Hunter in New South Wales. Brisbane and Cairns in Queensland.\" width=\"700\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32c684a2",
   "metadata": {},
   "source": [
    "The dataset only contains the time series at the lowest level, so we need to create the time series for all hierarchies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "31b1e602-f8ee-49c4-8819-fcc093d64ab1",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\ospra\\AppData\\Local\\Temp\\ipykernel_16668\\3753786659.py:6: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.\n",
      "  Y_df['ds'] = pd.to_datetime(Y_df['ds'])\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Country</th>\n",
       "      <th>Region</th>\n",
       "      <th>State</th>\n",
       "      <th>Purpose</th>\n",
       "      <th>ds</th>\n",
       "      <th>y</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Australia</td>\n",
       "      <td>Adelaide</td>\n",
       "      <td>South Australia</td>\n",
       "      <td>Business</td>\n",
       "      <td>1998-01-01</td>\n",
       "      <td>135.077690</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Australia</td>\n",
       "      <td>Adelaide</td>\n",
       "      <td>South Australia</td>\n",
       "      <td>Business</td>\n",
       "      <td>1998-04-01</td>\n",
       "      <td>109.987316</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Australia</td>\n",
       "      <td>Adelaide</td>\n",
       "      <td>South Australia</td>\n",
       "      <td>Business</td>\n",
       "      <td>1998-07-01</td>\n",
       "      <td>166.034687</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Australia</td>\n",
       "      <td>Adelaide</td>\n",
       "      <td>South Australia</td>\n",
       "      <td>Business</td>\n",
       "      <td>1998-10-01</td>\n",
       "      <td>127.160464</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Australia</td>\n",
       "      <td>Adelaide</td>\n",
       "      <td>South Australia</td>\n",
       "      <td>Business</td>\n",
       "      <td>1999-01-01</td>\n",
       "      <td>137.448533</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Australia</td>\n",
       "      <td>Adelaide</td>\n",
       "      <td>South Australia</td>\n",
       "      <td>Business</td>\n",
       "      <td>1999-04-01</td>\n",
       "      <td>199.912586</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>Australia</td>\n",
       "      <td>Adelaide</td>\n",
       "      <td>South Australia</td>\n",
       "      <td>Business</td>\n",
       "      <td>1999-07-01</td>\n",
       "      <td>169.355090</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>Australia</td>\n",
       "      <td>Adelaide</td>\n",
       "      <td>South Australia</td>\n",
       "      <td>Business</td>\n",
       "      <td>1999-10-01</td>\n",
       "      <td>134.357937</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>Australia</td>\n",
       "      <td>Adelaide</td>\n",
       "      <td>South Australia</td>\n",
       "      <td>Business</td>\n",
       "      <td>2000-01-01</td>\n",
       "      <td>154.034398</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>Australia</td>\n",
       "      <td>Adelaide</td>\n",
       "      <td>South Australia</td>\n",
       "      <td>Business</td>\n",
       "      <td>2000-04-01</td>\n",
       "      <td>168.776364</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     Country    Region            State   Purpose         ds           y\n",
       "0  Australia  Adelaide  South Australia  Business 1998-01-01  135.077690\n",
       "1  Australia  Adelaide  South Australia  Business 1998-04-01  109.987316\n",
       "2  Australia  Adelaide  South Australia  Business 1998-07-01  166.034687\n",
       "3  Australia  Adelaide  South Australia  Business 1998-10-01  127.160464\n",
       "4  Australia  Adelaide  South Australia  Business 1999-01-01  137.448533\n",
       "5  Australia  Adelaide  South Australia  Business 1999-04-01  199.912586\n",
       "6  Australia  Adelaide  South Australia  Business 1999-07-01  169.355090\n",
       "7  Australia  Adelaide  South Australia  Business 1999-10-01  134.357937\n",
       "8  Australia  Adelaide  South Australia  Business 2000-01-01  154.034398\n",
       "9  Australia  Adelaide  South Australia  Business 2000-04-01  168.776364"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Y_df = pd.read_csv('https://raw.githubusercontent.com/Nixtla/transfer-learning-time-series/main/datasets/tourism.csv')\n",
    "Y_df = Y_df.rename({'Trips': 'y', 'Quarter': 'ds'}, axis=1)\n",
    "Y_df.insert(0, 'Country', 'Australia')\n",
    "Y_df = Y_df[['Country', 'Region', 'State', 'Purpose', 'ds', 'y']]\n",
    "Y_df['ds'] = Y_df['ds'].str.replace(r'(\\d+) (Q\\d)', r'\\1-\\2', regex=True)\n",
    "Y_df['ds'] = pd.to_datetime(Y_df['ds'])\n",
    "\n",
    "Y_df.head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "457e0ab1",
   "metadata": {},
   "source": [
    "The dataset can be grouped in the following hierarchical structure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e736b97b",
   "metadata": {},
   "outputs": [],
   "source": [
    "spec = [\n",
    "    ['Country'],\n",
    "    ['Country', 'State'],\n",
    "    ['Country', 'Purpose'],\n",
    "    ['Country', 'State', 'Region'],\n",
    "    ['Country', 'State', 'Purpose'],\n",
    "    ['Country', 'State', 'Region', 'Purpose']\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3465a666",
   "metadata": {},
   "source": [
    "Using the `aggregate` function from `HierarchicalForecast` we can get the full set of time series."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86609206",
   "metadata": {},
   "source": [
    "::: {.callout-note}\n",
    "You can install `hierarchicalforecast` with `pip`:\n",
    "    \n",
    "```shell\n",
    "pip install hierarchicalforecast\n",
    "```\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aa45bcf7",
   "metadata": {},
   "outputs": [],
   "source": [
    "from hierarchicalforecast.utils import aggregate"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a0c9209",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>unique_id</th>\n",
       "      <th>ds</th>\n",
       "      <th>y</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Australia</td>\n",
       "      <td>1998-01-01</td>\n",
       "      <td>23182.197269</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Australia</td>\n",
       "      <td>1998-04-01</td>\n",
       "      <td>20323.380067</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Australia</td>\n",
       "      <td>1998-07-01</td>\n",
       "      <td>19826.640511</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Australia</td>\n",
       "      <td>1998-10-01</td>\n",
       "      <td>20830.129891</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Australia</td>\n",
       "      <td>1999-01-01</td>\n",
       "      <td>22087.353380</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Australia</td>\n",
       "      <td>1999-04-01</td>\n",
       "      <td>21458.373285</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>Australia</td>\n",
       "      <td>1999-07-01</td>\n",
       "      <td>19914.192508</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>Australia</td>\n",
       "      <td>1999-10-01</td>\n",
       "      <td>20027.925640</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>Australia</td>\n",
       "      <td>2000-01-01</td>\n",
       "      <td>22339.294779</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>Australia</td>\n",
       "      <td>2000-04-01</td>\n",
       "      <td>19941.063482</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   unique_id         ds             y\n",
       "0  Australia 1998-01-01  23182.197269\n",
       "1  Australia 1998-04-01  20323.380067\n",
       "2  Australia 1998-07-01  19826.640511\n",
       "3  Australia 1998-10-01  20830.129891\n",
       "4  Australia 1999-01-01  22087.353380\n",
       "5  Australia 1999-04-01  21458.373285\n",
       "6  Australia 1999-07-01  19914.192508\n",
       "7  Australia 1999-10-01  20027.925640\n",
       "8  Australia 2000-01-01  22339.294779\n",
       "9  Australia 2000-04-01  19941.063482"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Y_df, S_df, tags = aggregate(Y_df, spec)\n",
    "\n",
    "Y_df.head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9a0363d",
   "metadata": {},
   "source": [
    "We use the final two years (8 quarters) as test set."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6c33d4e4",
   "metadata": {},
   "outputs": [],
   "source": [
    "Y_test_df = Y_df.groupby('unique_id').tail(8)\n",
    "Y_train_df = Y_df.drop(Y_test_df.index)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2932318f",
   "metadata": {},
   "source": [
    "## 3. Hierarchical forecasting with TimeGPT\n",
    "\n",
    "First, we create base forecasts for all the time series with TimeGPT. Note that we set `add_history=True`, as we will need the in-sample fitted values of TimeGPT. \n",
    "\n",
    "We will predict 2 years (8 quarters), starting from 01-01-2016."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dda23c8c",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:nixtla.nixtla_client:Validating inputs...\n",
      "INFO:nixtla.nixtla_client:Preprocessing dataframes...\n",
      "INFO:nixtla.nixtla_client:Querying model metadata...\n",
      "INFO:nixtla.nixtla_client:Calling Forecast Endpoint...\n",
      "INFO:nixtla.nixtla_client:Calling Historical Forecast Endpoint...\n"
     ]
    }
   ],
   "source": [
    "timegpt_fcst = nixtla_client.forecast(df=Y_train_df, h=8, freq='QS', add_history=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f0db1ab",
   "metadata": {},
   "source": [
    "> 📘 Available models in Azure AI\n",
    ">\n",
    "> If you are using an Azure AI endpoint, please be sure to set `model=\"azureai\"`:\n",
    ">\n",
    "> `nixtla_client.forecast(..., model=\"azureai\")`\n",
    "> \n",
    "> For the public API, we support two models: `timegpt-1` and `timegpt-1-long-horizon`. \n",
    "> \n",
    "> By default, `timegpt-1` is used. Please see [this tutorial](https://docs.nixtla.io/docs/tutorials-long_horizon_forecasting) on how and when to use `timegpt-1-long-horizon`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6cff5131",
   "metadata": {},
   "outputs": [],
   "source": [
    "timegpt_fcst_insample = timegpt_fcst.query(\"ds < '2016-01-01'\")\n",
    "timegpt_fcst_outsample = timegpt_fcst.query(\"ds >= '2016-01-01'\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "000cc025",
   "metadata": {},
   "source": [
    "Let's plot some of the forecasts, starting from the highest aggregation level (`Australia`), to the lowest level (`Australia/Queensland/Brisbane/Holiday`). We can see that there is room for improvement in the forecasts."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13f74eaa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 214484 characters]",
      "text/plain": [
       "<Figure size 1600x700 with 4 Axes>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nixtla_client.plot(\n",
    "    Y_df,\n",
    "    timegpt_fcst_outsample,\n",
    "    max_insample_length=4 * 12,\n",
    "    unique_ids=['Australia', 'Australia/Queensland','Australia/Queensland/Brisbane', 'Australia/Queensland/Brisbane/Holiday']\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "967836fa",
   "metadata": {},
   "source": [
    "We can make these forecasts coherent to the specified hierarchy by using a `HierarchicalReconciliation` method from `NeuralForecast`. We will be using the [MinTrace](https://nixtlaverse.nixtla.io/hierarchicalforecast/methods.html) method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8f6bf69",
   "metadata": {},
   "outputs": [],
   "source": [
    "from hierarchicalforecast.methods import MinTrace\n",
    "from hierarchicalforecast.core import HierarchicalReconciliation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb7c9477",
   "metadata": {},
   "outputs": [],
   "source": [
    "reconcilers = [\n",
    "    MinTrace(method='ols'),\n",
    "    MinTrace(method='mint_shrink'),\n",
    "]\n",
    "hrec = HierarchicalReconciliation(reconcilers=reconcilers)\n",
    "\n",
    "Y_df_with_insample_fcsts = Y_df.copy()\n",
    "Y_df_with_insample_fcsts = timegpt_fcst_insample.merge(Y_df_with_insample_fcsts)\n",
    "\n",
    "Y_rec_df = hrec.reconcile(Y_hat_df=timegpt_fcst_outsample, Y_df=Y_df_with_insample_fcsts, S=S_df, tags=tags)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ff6ab82",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>unique_id</th>\n",
       "      <th>ds</th>\n",
       "      <th>TimeGPT</th>\n",
       "      <th>TimeGPT/MinTrace_method-ols</th>\n",
       "      <th>TimeGPT/MinTrace_method-mint_shrink</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Australia</td>\n",
       "      <td>2016-01-01</td>\n",
       "      <td>24967.19100</td>\n",
       "      <td>25044.408634</td>\n",
       "      <td>25394.406211</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Australia</td>\n",
       "      <td>2016-04-01</td>\n",
       "      <td>24528.88300</td>\n",
       "      <td>24503.089810</td>\n",
       "      <td>24327.212355</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Australia</td>\n",
       "      <td>2016-07-01</td>\n",
       "      <td>24221.77500</td>\n",
       "      <td>24083.107812</td>\n",
       "      <td>23813.826553</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Australia</td>\n",
       "      <td>2016-10-01</td>\n",
       "      <td>24559.44000</td>\n",
       "      <td>24548.038797</td>\n",
       "      <td>24174.894203</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Australia</td>\n",
       "      <td>2017-01-01</td>\n",
       "      <td>25570.33800</td>\n",
       "      <td>25669.248281</td>\n",
       "      <td>25560.277473</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3395</th>\n",
       "      <td>Australia/Western Australia/Experience Perth/V...</td>\n",
       "      <td>2016-10-01</td>\n",
       "      <td>427.81146</td>\n",
       "      <td>435.423617</td>\n",
       "      <td>434.047102</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3396</th>\n",
       "      <td>Australia/Western Australia/Experience Perth/V...</td>\n",
       "      <td>2017-01-01</td>\n",
       "      <td>450.71786</td>\n",
       "      <td>453.434056</td>\n",
       "      <td>459.954598</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3397</th>\n",
       "      <td>Australia/Western Australia/Experience Perth/V...</td>\n",
       "      <td>2017-04-01</td>\n",
       "      <td>452.17923</td>\n",
       "      <td>460.197847</td>\n",
       "      <td>470.009789</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3398</th>\n",
       "      <td>Australia/Western Australia/Experience Perth/V...</td>\n",
       "      <td>2017-07-01</td>\n",
       "      <td>450.68683</td>\n",
       "      <td>463.034888</td>\n",
       "      <td>482.645932</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3399</th>\n",
       "      <td>Australia/Western Australia/Experience Perth/V...</td>\n",
       "      <td>2017-10-01</td>\n",
       "      <td>443.31050</td>\n",
       "      <td>451.754435</td>\n",
       "      <td>474.403379</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>3400 rows × 5 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "                                              unique_id         ds  \\\n",
       "0                                             Australia 2016-01-01   \n",
       "1                                             Australia 2016-04-01   \n",
       "2                                             Australia 2016-07-01   \n",
       "3                                             Australia 2016-10-01   \n",
       "4                                             Australia 2017-01-01   \n",
       "...                                                 ...        ...   \n",
       "3395  Australia/Western Australia/Experience Perth/V... 2016-10-01   \n",
       "3396  Australia/Western Australia/Experience Perth/V... 2017-01-01   \n",
       "3397  Australia/Western Australia/Experience Perth/V... 2017-04-01   \n",
       "3398  Australia/Western Australia/Experience Perth/V... 2017-07-01   \n",
       "3399  Australia/Western Australia/Experience Perth/V... 2017-10-01   \n",
       "\n",
       "          TimeGPT  TimeGPT/MinTrace_method-ols  \\\n",
       "0     24967.19100                 25044.408634   \n",
       "1     24528.88300                 24503.089810   \n",
       "2     24221.77500                 24083.107812   \n",
       "3     24559.44000                 24548.038797   \n",
       "4     25570.33800                 25669.248281   \n",
       "...           ...                          ...   \n",
       "3395    427.81146                   435.423617   \n",
       "3396    450.71786                   453.434056   \n",
       "3397    452.17923                   460.197847   \n",
       "3398    450.68683                   463.034888   \n",
       "3399    443.31050                   451.754435   \n",
       "\n",
       "      TimeGPT/MinTrace_method-mint_shrink  \n",
       "0                            25394.406211  \n",
       "1                            24327.212355  \n",
       "2                            23813.826553  \n",
       "3                            24174.894203  \n",
       "4                            25560.277473  \n",
       "...                                   ...  \n",
       "3395                           434.047102  \n",
       "3396                           459.954598  \n",
       "3397                           470.009789  \n",
       "3398                           482.645932  \n",
       "3399                           474.403379  \n",
       "\n",
       "[3400 rows x 5 columns]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Y_rec_df"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "85a370f1",
   "metadata": {},
   "source": [
    "Again, we plot some of the forecasts. We can see a few, mostly minor differences in the forecasts."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "67e3933e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 239416 characters]",
      "text/plain": [
       "<Figure size 1600x700 with 4 Axes>"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nixtla_client.plot(\n",
    "    Y_df,\n",
    "    Y_rec_df,\n",
    "    max_insample_length=4 * 12,\n",
    "    unique_ids=['Australia', 'Australia/Queensland','Australia/Queensland/Brisbane', 'Australia/Queensland/Brisbane/Holiday']\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a587baa1",
   "metadata": {},
   "source": [
    "Let's numerically verify the forecasts to the situation where we don't apply a post-processing step. We can use `HierarchicalEvaluation` for this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4c73c041",
   "metadata": {},
   "outputs": [],
   "source": [
    "from hierarchicalforecast.evaluation import evaluate\n",
    "from utilsforecast.losses import rmse"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "016a37c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval_tags = {}\n",
    "eval_tags['Total'] = tags['Country']\n",
    "eval_tags['Purpose'] = tags['Country/Purpose']\n",
    "eval_tags['State'] = tags['Country/State']\n",
    "eval_tags['Regions'] = tags['Country/State/Region']\n",
    "eval_tags['Bottom'] = tags['Country/State/Region/Purpose']\n",
    "\n",
    "evaluation = evaluate(\n",
    "        df=Y_rec_df.merge(Y_test_df, on=['unique_id', 'ds']),\n",
    "        tags=eval_tags,\n",
    "        train_df=Y_train_df,\n",
    "        metrics=[rmse],\n",
    ")\n",
    "numeric_cols = evaluation.select_dtypes(np.number).columns\n",
    "evaluation[numeric_cols] = evaluation[numeric_cols].map('{:.2f}'.format)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "53dfb25f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>level</th>\n",
       "      <th>metric</th>\n",
       "      <th>TimeGPT</th>\n",
       "      <th>TimeGPT/MinTrace_method-ols</th>\n",
       "      <th>TimeGPT/MinTrace_method-mint_shrink</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Total</td>\n",
       "      <td>rmse</td>\n",
       "      <td>1433.07</td>\n",
       "      <td>1436.07</td>\n",
       "      <td>1627.43</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Purpose</td>\n",
       "      <td>rmse</td>\n",
       "      <td>482.09</td>\n",
       "      <td>475.64</td>\n",
       "      <td>507.50</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>State</td>\n",
       "      <td>rmse</td>\n",
       "      <td>275.85</td>\n",
       "      <td>278.39</td>\n",
       "      <td>294.28</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Regions</td>\n",
       "      <td>rmse</td>\n",
       "      <td>49.40</td>\n",
       "      <td>47.91</td>\n",
       "      <td>47.99</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Bottom</td>\n",
       "      <td>rmse</td>\n",
       "      <td>19.32</td>\n",
       "      <td>19.11</td>\n",
       "      <td>18.86</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>Overall</td>\n",
       "      <td>rmse</td>\n",
       "      <td>38.66</td>\n",
       "      <td>38.21</td>\n",
       "      <td>39.16</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     level metric  TimeGPT TimeGPT/MinTrace_method-ols  \\\n",
       "0    Total   rmse  1433.07                     1436.07   \n",
       "1  Purpose   rmse   482.09                      475.64   \n",
       "2    State   rmse   275.85                      278.39   \n",
       "3  Regions   rmse    49.40                       47.91   \n",
       "4   Bottom   rmse    19.32                       19.11   \n",
       "5  Overall   rmse    38.66                       38.21   \n",
       "\n",
       "  TimeGPT/MinTrace_method-mint_shrink  \n",
       "0                             1627.43  \n",
       "1                              507.50  \n",
       "2                              294.28  \n",
       "3                               47.99  \n",
       "4                               18.86  \n",
       "5                               39.16  "
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "evaluation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e66e8c60",
   "metadata": {},
   "source": [
    "We made a small improvement in overall RMSE by reconciling the forecasts with `MinTrace(ols)`, and made them slightly worse using `MinTrace(mint_shrink)`, indicating that the base forecasts were relatively strong already. \n",
    "\n",
    "However, we now have coherent forecasts too - so not only did we make a (small) accuracy improvement, we also got coherency to the hierarchy as a result of our reconciliation step."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef4ca550",
   "metadata": {},
   "source": [
    "**References**\n",
    "\n",
    "* [Hyndman, Rob J., and George Athanasopoulos (2021). \"Forecasting: Principles and Practice (3rd Ed)\"](https://otexts.com/fpp3/)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
