Skip to main content

Calibre

CI License: MIT Python 3.12+

Calibre is a conformal forecasting library for panels of time series. It makes point forecasts at many origins, reconciles them over a hierarchy, and calibrates bands and bounds from out-of-sample residuals.

Install

The package is libcalibre on PyPI, and the import name is calibre:

pip install libcalibre
# or
uv add libcalibre

The vendor model adapters need an extra: stats (statsforecast), ml (mlforecast), or neural (neuralforecast).

pip install "libcalibre[stats]"

Quick start

import numpy as np
import pandas as pd

from calibre import Absolute, BottomUp, Hierarchy, LeadTime, Panel, SeasonalNaive, Signed
from calibre import SplitQuantile, Step, replay, rolling_forecasts
from calibre.metrics import coverage

rng = np.random.default_rng(0)
series = np.array(["a", "b", "c"])
periods = pd.date_range("2024-01-01", periods=120, freq="D")
panel = Panel(series, periods, rng.poisson(5.0, (3, 120)), "D")
hierarchy = Hierarchy.from_attributes(
    series, pd.DataFrame({"group": ["x", "x", "y"]}, index=series)
)

origins = np.arange(60, 113)  # each origin is the index of the last observed period
run = rolling_forecasts(panel, hierarchy, SeasonalNaive(7), BottomUp(hierarchy), origins, 7)
actuals = hierarchy.aggregate(panel.values)

calibrator = SplitQuantile(0.9, window=28)
bands = replay(run, actuals, target=Step(), score=Absolute(), calibrator=calibrator)
bound = replay(run, actuals, target=LeadTime(7), score=Signed(), calibrator=calibrator)
print(coverage(bands.target, bands.lower, bands.upper))

Concepts

Shapes use these axes: B bottom series, T periods, N nodes with bottoms first, S forecast series (B for BottomUp, N otherwise), O origins, H forecast steps, and C target columns (H for Step, 1 for LeadTime).

Panel and Hierarchy go to the Forecaster, base forecasts to the Reconciler, points to the Calibrator, bounds to order_up_to and settle

Stage Names
Data Panel, Hierarchy, Covariate
Forecast Forecaster, Reconciler (BottomUp, Identity, WlsStruct), rolling_forecasts
Calibrate Target (Step, LeadTime), Score (Absolute, Signed), Loss (Miss), Calibrator (SplitQuantile, ACI, QuantileTracker, RiskControl), step, replay
Decide critical_ratio, order_up_to, settle
Measure calibre.metrics: coverage, width, interval score, pinball, cost

Two rules hold everywhere. A model reads only its window, so a later value cannot change a point unless a covariate declares it known ahead. A calibrator sees a score only once its target is known, and before the origin that knows it issues.

Architecture has the shapes and the modules.

Write a calibrator

A calibrator owns its target, a level or a loss, and is three functions of an explicit state. calibre.online handles the origins, the delays, and the indexing. A new method is one file in calibre/conformal/calibrators/ that imports only calibrators.base, calibrators.ranks, and conformal.losses. This is the whole of a quantile tracker:

import numpy as np

from calibre.conformal.calibrators.base import Calibrator


class Tracker(Calibrator):
    def __init__(self, level, lr):
        self.level = level
        self.lr = lr

    def initial_state(self, n_nodes, n_columns):
        return {"q": np.zeros((n_nodes, n_columns))}

    def update(self, state, feedback):
        q = state["q"].copy()
        for row, column in enumerate(feedback.column):  # one row = one origin, one column
            miss = feedback.scores[row] > feedback.issued[row]
            q[:, column] += self.lr * (miss - (1 - self.level))
        return {"q": q}

    def threshold(self, state):
        return state["q"].astype(np.float32)

Keep the state in numpy arrays. Then a product can save it after each origin with calibre.online.state.flatten and continue from it.

Models

Model Kind Install
calibre.forecast.models.naive.SeasonalNaive local core
calibre.forecast.models.statsforecast.StatsForecastModel local, any statsforecast.models model libcalibre[stats]
calibre.forecast.models.mlforecast.MLForecast global regressor with lags and covariates libcalibre[ml]
calibre.forecast.models.neuralforecast.NeuralForecast global network from neuralforecast.models libcalibre[neural]
from lightgbm import LGBMRegressor
from calibre import BottomUp, Covariate, rolling_forecasts
from calibre.forecast.models.mlforecast import MLForecast

model = MLForecast(
    LGBMRegressor(), lags=[7, 14, 28], features=["price"], fit_periods=365, lookback=84
)
price = Covariate(prices, known_ahead=True, aggregate="mean")  # [B, T + H]
run = rolling_forecasts(
    panel,
    hierarchy,
    model,
    BottomUp(hierarchy),
    origins,
    28,
    refit_every=7,
    covariates={"price": price},
)

Benchmarks

bench/ is a folder of scripts. They load benchmark data from a directory, run benchmark protocols, and compare calibrators. They import the installed calibre. Raw data is not in Git.

Script Content
vn2.py load(path): weekly VN2 sales with the out-of-stock mask, hierarchy, and starting stock. play(data, bounds): six orders up to the bounds, eight settled weeks, holding 0.2 and shortage 1.0
m5.py load(path): daily M5 sales and the 12-level hierarchy (42,840 nodes). prices(horizon): the sell price covariate
compare.py compare(forecasts, actuals, calibrators, target, score, level): one row of metrics per method, on the cells where each method was ready

vn2_conformal.py runs the whole VN2 path in about one second:

uv run python bench/vn2_conformal.py data/vn2

Documents

Development

uv sync --group dev
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run ty check src/
uv build --no-sources

See Contributing.

License

MIT

Metadata

Release files for libcalibre 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for libcalibre 0.1.0
File Size Uploaded
libcalibre-0.1.0.tar.gz 27.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for libcalibre 0.1.0
File Interpreter ABI Platform
libcalibre-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 68.6 kB

Release files / libcalibre-0.1.0.tar.gz

Download URL libcalibre-0.1.0.tar.gz
Size 27.1 kB
Tags Source
SHA-256 checksum
How to use checksums
c96a7eebce9d38ec02dfe9a45b35333f3a5c21d65f22c1fb1b6e7808e7241c3c
BLAKE2b-256 checksum
How to use checksums
da9e4fea3b05645e079253f876e976c55e7495270c1117ff48c4d22cdb5405f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / libcalibre-0.1.0-py3-none-any.whl

Download URL libcalibre-0.1.0-py3-none-any.whl
Size 41.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ab61bfb7be81d35f6b626a3dd0f0f30fd4bea5355bd590dc12431ff773952ba5
BLAKE2b-256 checksum
How to use checksums
9db7cb02464c960f9f587e59170ce7647b3246df71bb0f87dc6f3c2b93214c43
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page