Conforme
Conforme 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
Install conforme from PyPI:
pip install conforme
# or
uv add conforme
The vendor model adapters need an extra: stats (statsforecast), ml (mlforecast), or
neural (neuralforecast).
pip install "conforme[stats]"
Quick start
import numpy as np
import pandas as pd
from conforme import Absolute, BottomUp, Hierarchy, LeadTime, Panel, SeasonalNaive, Signed
from conforme import SplitQuantile, Step, replay, rolling_forecasts
from conforme.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).
| 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 | conforme.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. conforme.online handles the origins, the delays, and the indexing. A new method
is one file in conforme/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 conforme.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
conforme.online.state.flatten and continue from it.
Models
| Model | Kind | Install |
|---|---|---|
conforme.forecast.models.naive.SeasonalNaive |
local | core |
conforme.forecast.models.statsforecast.StatsForecastModel |
local, any statsforecast.models model |
conforme[stats] |
conforme.forecast.models.mlforecast.MLForecast |
global regressor with lags and covariates | conforme[ml] |
conforme.forecast.models.neuralforecast.NeuralForecast |
global network from neuralforecast.models |
conforme[neural] |
from lightgbm import LGBMRegressor
from conforme import BottomUp, Covariate, rolling_forecasts
from conforme.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 conforme.
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
- Architecture: modules, array contracts, and costs.
- Semantics: the calibration rules.
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
Metadata
Release files for conforme 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| conforme-0.1.0.tar.gz | 27.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| conforme-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 68.6 kB
Release files / conforme-0.1.0.tar.gz
| Download URL | conforme-0.1.0.tar.gz |
|---|---|
| Size | 27.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ee2be13833878ab2dfebaf27cc0dd419cabe9ed74e1f5c2418e473048e3b6824
|
|
BLAKE2b-256 checksum How to use checksums |
111931657cfbb790965b8672fbacc357338b4544b2298be4f342954c85fc5de1
|
| 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 / conforme-0.1.0-py3-none-any.whl
| Download URL | conforme-0.1.0-py3-none-any.whl |
|---|---|
| Size | 41.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
357012af1819772a1c36265540e5c4bd84e93cd359d2ddadf135f7d566367fc5
|
|
BLAKE2b-256 checksum How to use checksums |
70c52cfadecd5f8795896f6a4e464e21a4860d1a3c3c81606a31679af15f95a0
|
| 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}
|