Skip to main content
Real panels rarely hold series of equal length: one product launched last quarter, another has reported for a decade. A panel like that is ragged. This page covers how conformalforecast represents it, what it guarantees, and where it will stop you.

Left-NaN padding

A Series array is always (n_series, T). Short series are padded with NaN on the left, so every series ends on the same column and column T-1 means “most recent” for all of them. That convention is enforced. An interior gap or a trailing NaN is an error, because either would silently break the “last column is now” invariant that every method relies on.
long_to_panel returns the matrix and a PanelIndex that remembers how to undo the pivot: the series ids, each series’ length, its offset into the matrix, and the timestamps. panel_to_long uses it to go back, dropping the padding.

Methods propagate NaN, they never impute

A NaN in a calibration array means “this window does not exist for this series”. A method carries that through scoring and takes per-(series, horizon) quantiles with NaN-aware order statistics, so a short series is calibrated on its own windows and nobody else’s. The consequence you can see is n_calibration_samples_ on the fitted object: an effective count per series, not the number you asked for.
Here every series is long enough that all 30 windows exist, so the counts are uniform. Ask for more windows than the shortest series can supply and the arithmetic changes.

The floor, and the error you get

Every series needs at least ceil(1 / alpha) effective windows. Below that the requested quantile is not attainable from the sample at all, and fit refuses rather than returning something that looks like an interval.
series_indices is the point of the exception: on a 500-series panel you need to know which ones are short, not merely that something was.
ceil(1/alpha) is arithmetic, not a heuristic The conformal quantile is the k = ceil((1 - alpha)(n + 1))-th order statistic. For that index to exist at all you need n >= 1/alpha - 1; below it the interval would have to be unbounded. The package rounds that up to the n >= ceil(1/alpha) the error message quotes, so alpha = 0.01 genuinely requires 100 windows. Lowering alpha is not free.

Dense windows vs calendar-aligned cutoffs

calibrate_cv has an aligned flag, and it decides what axis 1 of the calibration arrays means.
  • aligned=False (the default) — dense per-series windows. Window k is “k steps before this series’ own end”. Every series contributes the same number of windows, and no NaN is introduced.
  • aligned=True — calendar-aligned cutoffs. Window k is the same wall-clock cutoff for every series, and a series that did not exist yet gets a NaN cell.
For split conformal the distinction rarely matters: it pools windows and takes a quantile, and pooling is order-free. For the recency-weighted methods it matters a great deal — NexCP reads axis 1 as chronological age and weights by it, and SPCI feeds it to a regressor as a sequence. Aligning the axis to the calendar makes “age” mean the same thing across series.
Identical here, and that is worth understanding rather than glossing over. This panel was built so every series ends on the same timestamp; they differ only in where they start. The last 30 cutoffs are therefore the same 30 wall-clock times for all eight series, and every series is long enough to cover them. The two modes coincide. They diverge when series end at different times, or when a series is too young to reach the earliest aligned cutoff — then aligned=True produces the NaN cells that the dense mode would have quietly filled with a different series’ history.
Nixtla cross-validation has its own length requirement aligned is a conformalforecast concept, but the windows come from the underlying library. statsforecast raises ValueError: The following series are too short for the cross validation settings before either mode is reached if any series cannot supply n_windows cutoffs. Assemble the arrays yourself — with panel.cv_frames_to_arrays, in the panel API reference — if you need windows the library will not give you.
Which is easy enough to show, since nothing about a retired series is exotic. Truncate one series’ tail and the two modes stop agreeing.
Both modes still give every series its ten windows — dense compacts them, so n_calibration_samples_ is 10 either way. The difference is the axis they sit on. statsforecast counts cutoffs back from each series’ own end date, so the retired series’ ten cutoffs are 48 hours earlier than everyone else’s and share nothing with them. Dense mode says “window k is k steps before this series’ end” and returns a compact array. Aligned mode has to put every distinct cutoff on the calendar axis, so it returns a wider, mostly-NaN cube: each series occupies only the columns it actually reached.
Aligned mode is not free on a panel with many distinct end dates The calendar axis is the union of every series’ cutoffs. When end dates are spread out, that union grows with the number of series rather than with n_windows, and the cube grows with it. Worse, axis 1 stops meaning “chronological age”, which is exactly what NexCP and SPCI read it as. Prefer the default aligned=False unless you specifically need calendar comparability.

Per-series diagnostics

The real payoff of keeping series separate is that you can ask where the interval is failing.
A panel-level coverage figure is a weighted average of these bars, and this panel shows why that matters: the 85.2% marginal figure averages H1 at 63.5% with H50 at 100%, and five of the eight series sit below the 90% target. The headline names none of them — which is why coverage_by_series exists, and why the per-series calibration this page describes is worth its complexity.

Next

  • NexCP — the method that reads the window axis as time.
  • Diagnostics — the rest of the per-slice measurements.
  • Architecture — the shape conventions in full.