HierarchicalForecast turns disagreeing forecasts into one coherent set
Probabilistic Hierarchical forecasting 👑 with statistical and econometric methods.
At a glance
- What is it?
- HierarchicalForecast is a Python library that reconciles independent time series forecasts across aggregation levels, so regional totals, national totals and bottom level series stop contradicting each other. It reconciles rather than forecasts, which makes it a companion to StatsForecast rather than a replacement for it.
- Who is it for?
- Adopt HierarchicalForecast when you forecast many series that aggregate into each other and the totals must reconcile, and expect to pair it with StatsForecast or an equivalent base model. Skip it for single level series, for pure deep learning forecasting, or if a compiled Eigen and pybind11 build is a problem for your environment.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 6 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why incoherent totals break planning
Most retail and supply chain datasets arrive already aggregated: a national total, regional subtotals, and store level rows beneath them. Forecast each layer independently and the numbers stop adding up. A regional forecast that sums to more than the country forecast it sits inside is not a rounding artefact, it is a contradiction that propagates into every plan built on top of it.
HierarchicalForecast exists to remove that contradiction at the point where it is created. The library targets forecasters who already hold base predictions and need them to be mutually consistent across a hierarchy. It does not forecast on its own. It reconciles. Base forecasts come from a separate model, StatsForecast in the documented example, and this package adjusts them so that every level agrees with every other level.
The hierarchy specification drives everything
The core input is a specification of how series aggregate. In the example, HierarchicalData.load returns three objects: the long format series Y_df, a specification frame S_df that describes which series roll up into which, and a tags frame. The specification is what turns a stack of independent forecasts into a system that can be made coherent.
Internally the package builds a summation matrix from that specification and applies a reconciliation matrix so the corrected bottom level series still aggregate to the corrected totals. The projection depends on structure rather than on any single series, so the package ships a compiled extension. setup.py defines a pybind11 module named hierarchicalforecast._lib, compiled from src/*.cpp, with Eigen headers vendored under external_libs/eigen and a C++20 standard.
That build is where the friction sits. Compiler flags differ per platform. Linux and Windows receive OpenMP and fast math options, and macOS expects libomp installed through Homebrew. If brew is missing, setup.py catches the failure and continues without those include and library paths.
BottomUp, TopDown and MiddleOut in practice
Three of the classic methods share one interface and one call. BottomUp simply adds the lower levels to produce the upper levels, which is cheap and assumes the base series are more reliable than the aggregates. TopDown does the reverse, distributing a top level forecast down the tree. MiddleOut picks a level in the middle and anchors the base predictions there, using bottom up above that anchor and top down below it.
The reconcilers are constructed with the level names taken from your specification. The example anchors MiddleOut on Country/Purpose/State and asks TopDown for forecast proportions rather than an equal split.
The three imports the example needs are below. The packages statsforecast and datasetsforecast are additional requirements for this example and are not installed by the library itself.
from datasetsforecast.hierarchical import HierarchicalData
# compute base forecast no coherent
from statsforecast.core import StatsForecast
from statsforecast.models import AutoARIMA, Naive
#obtain hierarchical reconciliation methods and evaluation
from hierarchicalforecast.core import HierarchicalReconciliation
from hierarchicalforecast.evaluation import evaluate
from hierarchicalforecast.methods import BottomUp, TopDown, MiddleOut
from utilsforecast.losses import mseLoad the data, split it, and produce base forecasts
The workflow is short. Load the hierarchical dataset, keep the last four periods of each series as a test set, fit the base models on what remains, and hand the resulting predictions to the reconcilers. The frequency is set to QE, quarterly end, and the base model set is AutoARIMA alongside a Naive baseline so that the reconciled result can be compared against an unreconciled forecast.
Because the frequency is passed to StatsForecast and not to this library, the hierarchy specification and the base forecast frame have to agree on the time axis and on the unique_id naming. A mismatch in either shows up as a reconciliation that returns an empty or misaligned frame rather than an error you can read off the console.
After this step the frame Y_hat_df holds base forecasts for every series at every level, still incoherent.
# Load TourismSmall dataset
Y_df, S_df, tags = HierarchicalData.load('./data', 'TourismSmall')
Y_df['ds'] = pd.to_datetime(Y_df['ds'])
S_df = S_df.reset_index(names="unique_id")
#split train/test sets
Y_test_df = Y_df.groupby('unique_id').tail(4)
Y_train_df = Y_df.drop(Y_test_df.index)
# Compute base auto-ARIMA predictions
fcst = StatsForecast(models=[AutoARIMA(season_length=4), Naive()],
freq='QE', n_jobs=-1)
Y_hat_df = fcst.forecast(df=Y_train_df, h=4)The reconciler object is where the method choice is made
Reconciliation happens through a single object, HierarchicalReconciliation, constructed with a list of reconcilers. Passing three reconcilers produces three reconciled sets, one per method, so an accuracy comparison across methods does not require rewriting the pipeline. The evaluation module and the evaluate function sit alongside it, and the example pulls mse from utilsforecast.losses to score the results.
Two of the documented TopDown variants are visible here. forecast_proportions distributes the top level according to the historical proportions of the series beneath it, which is the safer default when bottom level series have different scales. The MiddleOut instance takes the same top_down_method argument, so the propagation rule applies to the levels below the anchor only.
This design keeps the method selection declarative. The cost is that every method shares one call signature, so a method specific argument that does not apply to a given hierarchy raises at construction time rather than at reconcile time.
reconcilers = [
BottomUp(),
TopDown(method='forecast_proportions'),
MiddleOut(middle_level='Country/Purpose/State',
top_down_method='forecast_proportions')
]
hrec = HierarchicalReconciliation(reconcilers=reconcilers)MinTrace, ERM and the solver underneath
BottomUp and TopDown impose no error structure. MinTrace goes further: it minimises the total forecast variance of the space of coherent forecasts, using a closed form solution based on the variance covariance matrix of the base forecast errors. ERM takes a different route and optimises the reconciliation matrix under an L1 regularised objective, which the package solves with the qpsolvers dependency and its clarabel extra.
That solver dependency is the practical difference between the classical and the alternative methods. BottomUp and TopDown are arithmetic. MinTrace needs an invertible matrix and sensible error estimates. ERM needs an optimiser, and the practical hierarchy size at which it stops being a good idea is not documented anywhere in the project.
The package is explicit about where it sits in the stack. It is a library of statistical and econometric baselines with an accompanying paper, and the repository layout separates experiments, external libraries and documentation scripts from the package source itself.
Probabilistic reconciliation and the limits of conformal coverage
Coherence applies to distributions as much as to point forecasts, and the package ships four probabilistic methods. Normality uses the MinTrace variance covariance closed form under a normality assumption. Bootstrap generates a distribution of hierarchically reconciled predictions using the approach attributed to Gamakumara. PERMBU reconciles independent sample predictions by reinjecting multivariate dependence with estimated rank permutation copulas and then performing a bottom up aggregation. Conformal produces distribution free prediction intervals using conformal prediction.
Conformal carries an explicit condition. The project states that it provides valid coverage under exchangeability assumptions. If your series are not exchangeable, that is a modelling assumption you are making, and the guarantee does not hold. There is no tutorial here for diagnosing exchangeability, so this is a decision the practitioner has to make elsewhere.
Temporal hierarchies are supported too. All reconciliation methods except the in sample methods are available for temporal hierarchies, which means a weekly, monthly and yearly structure can be reconciled with the same code path as a cross sectional one.
Installing the package and its compiled extension
The project recommends uv as the package manager and gives the standard installer as an alternative. Both install the same distribution from the Python package index.
uv pip install hierarchicalforecastpip install hierarchicalforecastThe package metadata requires Python 3.10 up to 4.0, and the classifiers list 3.10 through 3.14. The distribution is marked as a beta release in its own metadata, which is worth knowing before you pin it in a production pipeline.
Dependencies are deliberately light: numpy, pandas, scikit-learn, matplotlib, narwhals, qpsolvers with clarabel, and utilsforecast. Polars is available as an optional extra, and the example notebook lives in the repository and can be opened directly in Colab, which is the shortest route to seeing a complete reconciliation run end to end.
What the library does not cover
Three boundaries are worth stating plainly. First, HierarchicalForecast does not produce base forecasts. Without a separate modelling step, such as the StatsForecast and AutoARIMA pairing shown here, there is nothing to reconcile, and the library cannot fill that gap.
Second, the hierarchy specification is your responsibility. The package will reconcile the tree you describe, correctly and without complaint, so an incorrect tree produces incorrect coherent forecasts rather than an error.
Third, the macOS build path assumes libomp can be located through brew. The setup script tolerates a missing Homebrew, but the resulting binary is compiled without the OpenMP flags that the Linux and Windows paths request, and the project does not describe the performance or build consequences of that difference.
On maintenance, the repository is not archived and the last push was on 2026-10-19, with release v1.5.3 published on 2026-09-24 after v1.5.2 on 2026-08-06. The licence is Apache-2.0, and a THIRD_PARTY_LICENSES.md file plus a pip-licenses based Makefile target indicate that bundled third party licences are tracked separately from the main licence.
Editorial conclusion
Adopt HierarchicalForecast when you forecast many series that aggregate into each other and the totals must reconcile, and expect to pair it with StatsForecast or an equivalent base model. Skip it for single level series, for pure deep learning forecasting, or if a compiled Eigen and pybind11 build is a problem for your environment. Before committing, check that your hierarchy specification S_df matches how your business actually aggregates, because the reconciliation matrix is derived from that specification and nothing in the library can correct a wrong tree.
Frequently asked questions
Does HierarchicalForecast produce forecasts on its own?
No. It reconciles base forecasts that come from another model, StatsForecast with AutoARIMA in the documented example, so that values across aggregation levels are consistent.
How do I install HierarchicalForecast?
The project recommends uv, with uv pip install hierarchicalforecast, and gives pip install hierarchicalforecast as the alternative. The distribution requires Python 3.10 or later below 4.0, and it builds a pybind11 and Eigen extension at install time.
Which reconciliation methods work with temporal hierarchies?
All of the reconciliation methods except the in sample methods are available for temporal hierarchies, so weekly, monthly and yearly structures can be reconciled the same way as cross sectional ones.
What does the HierarchicalForecast package require besides base forecasts?
A hierarchy specification describing which series aggregate into which, returned as S_df by HierarchicalData.load, plus matching unique_id names and time frequency between that specification and the base forecast frame.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nixtla-hierarchicalforecast)Was this useful?