Pramaa
Pramaa 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 pramaa from PyPI:
pip install pramaa
# or
uv add pramaa
The vendor model adapters need an extra: stats (statsforecast), ml (mlforecast), or
neural (neuralforecast).
pip install "pramaa[stats]"
Quick start
import numpy as np
import pandas as pd
from pramaa import Absolute, BottomUp, Hierarchy, LeadTime, Panel, SeasonalNaive, Signed
from pramaa import SplitQuantile, Step, replay, rolling_forecasts
from pramaa.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 | pramaa.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. pramaa.online handles the origins, the delays, and the indexing. A new method
is one file in pramaa/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 pramaa.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
pramaa.online.state.flatten and continue from it.
Models
| Model | Kind | Install |
|---|---|---|
pramaa.forecast.models.naive.SeasonalNaive |
local | core |
pramaa.forecast.models.statsforecast.StatsForecastModel |
local, any statsforecast.models model |
pramaa[stats] |
pramaa.forecast.models.mlforecast.MLForecast |
global regressor with lags and covariates | pramaa[ml] |
pramaa.forecast.models.neuralforecast.NeuralForecast |
global network from neuralforecast.models |
pramaa[neural] |
from lightgbm import LGBMRegressor
from pramaa import BottomUp, Covariate, rolling_forecasts
from pramaa.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 pramaa.
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 pramaa 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 | Provenance | Actions |
|---|---|---|---|---|
| pramaa-0.1.0.tar.gz | 27.1 kB | Without provenance | Details for pramaa-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pramaa-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for pramaa-0.1.0-py3-none-any.whl |
Total release size: 68.4 kB
Release files / pramaa-0.1.0.tar.gz
| Download URL | pramaa-0.1.0.tar.gz |
|---|---|
| Size | 27.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1cfe59b097f71ed9a47bb641d5203b94b198b0f5a29e06b8fdb18777958e04b5
|
|
BLAKE2b-256 checksum How to use checksums |
86bd4fe13813caa8576852cf325d7d266757157656d4ab6d06e51c28319eeacb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / pramaa-0.1.0-py3-none-any.whl
| Download URL | pramaa-0.1.0-py3-none-any.whl |
|---|---|
| Size | 41.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
29f64c49eb8340742786a9f085b28b5df8fc25bca0cfdf0135471b6182863899
|
|
BLAKE2b-256 checksum How to use checksums |
818e2c378a40f69e608b0d5f1e6d6fbf062219ec3e2c6a74357942a37a6d2a8c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for pramaa 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.