> ## Documentation Index
> Fetch the complete documentation index at: https://nixtlaverse.nixtla.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Panel helpers

> DataFrame-free long/panel conversion, CV assembly and level/quantile helpers

### `PanelIndex`

```python theme={null}
PanelIndex(series_ids, start_offsets, lengths, timestamps)
```

Inversion metadata for a `(n_series, T)` panel.

## Attributes

series\_ids : tuple
Series identifiers in panel-row order (axis 0). Sorted ascending.
start\_offsets : NDArray\[np.integer], shape (n\_series,)
Column index of each series' first non-NaN observation.
lengths : NDArray\[np.integer], shape (n\_series,)
Per-series effective (non-NaN) observation count.
timestamps : tuple or None
Length-`T` time labels for the columns, or `None` when the panel
was built positionally.

#### `PanelIndex.lengths`

```python theme={null}
lengths: NDArray[np.integer]
```

#### `PanelIndex.series_ids`

```python theme={null}
series_ids: tuple[object, ...]
```

#### `PanelIndex.start_offsets`

```python theme={null}
start_offsets: NDArray[np.integer]
```

#### `PanelIndex.timestamps`

```python theme={null}
timestamps: tuple[object, ...] | None
```

### `long_to_panel`

```python theme={null}
long_to_panel(values, series_ids, time_index, *, dtype=np.float64)
```

Pivot long-format observations into a left-NaN-padded panel.

## Parameters

values : sequence of float, shape (n\_obs,)
Observation values.
series\_ids : sequence, shape (n\_obs,)
Series identifier for each observation.
time\_index : sequence, shape (n\_obs,)
Time label for each observation. Labels must be mutually comparable
(e.g. all timestamps, or all ints).
dtype : numpy float dtype, default float64
Output panel dtype.

## Returns

panel : NDArray, shape (n\_series, T)
Left-NaN-padded panel; `T` is the number of distinct timestamps.
index : PanelIndex
Inversion metadata.

## Raises

ValueError
If the input lengths mismatch, or a series has an interior gap
(a missing interior timestamp, which would produce an interior NaN).

### `panel_to_long`

```python theme={null}
panel_to_long(panel, index)
```

Flatten a panel back to long-format non-NaN rows.

## Parameters

panel : NDArray, shape (n\_series, T)
index : PanelIndex
Metadata from :func:`long_to_panel`. When `index.timestamps` is
`None`, column positions `0..T-1` are used as time labels.

## Returns

row\_series\_ids : list, shape (n\_rows,)
row\_timestamps : list, shape (n\_rows,)
row\_values : NDArray, shape (n\_rows,)
Non-NaN cells in `(series, time)` order.

### `cv_frames_to_arrays`

```python theme={null}
cv_frames_to_arrays(cubes, *, aligned)
```

Assemble calendar-cutoff CV cubes into per-series or aligned arrays.

## Parameters

cubes : sequence of NDArray, each shape (n\_series, n\_windows, horizon)
CV outputs sharing a NaN pattern (e.g. `[predictions, truths]`). A
`(series, window)` cell is valid iff it is non-NaN across the whole
horizon in **every** cube.
aligned : bool
`True` returns the cubes unchanged (NaN cells preserved for the
ragged calibration path). `False` returns dense per-series arrays: each
series keeps only its valid windows, right-aligned, truncated to the
minimum valid-window count across series.

## Returns

list of NDArray
One array per input cube, in the same order.

## Raises

ValueError
If `cubes` is empty, shapes disagree, or (`aligned=False`) any
series has zero valid windows.

### `exog_to_long`

```python theme={null}
exog_to_long(exog, series_ids)
```

Flatten a 4-D exogenous array to long-format rows.

## Parameters

exog : NDArray, shape (n\_series, n\_samples, horizon, n\_features)
series\_ids : sequence, shape (n\_series,)

## Returns

row\_series\_ids : list, shape (n\_rows,)
row\_sample\_idx : list of int, shape (n\_rows,)
row\_step\_idx : list of int, shape (n\_rows,)
Zero-based horizon step.
features : NDArray, shape (n\_rows, n\_features)
Rows in `(series, sample, step)` order.

## Raises

ValueError
If `exog` is not 4-D or its leading axis mismatches `series_ids`.

### `level_to_quantiles`

```python theme={null}
level_to_quantiles(level)
```

Convert a symmetric Nixtla interval level to a (lo, hi) quantile pair.

## Parameters

level : int
Interval level in (0, 100), e.g. `80`.

## Returns

tuple of float
`((100 - level) / 200, 1 - (100 - level) / 200)` — e.g. `(0.1, 0.9)`.

## Raises

ValueError
If `level` is not in (0, 100).

### `quantiles_to_level`

```python theme={null}
quantiles_to_level(q_lo, q_hi)
```

Convert a symmetric (lo, hi) quantile pair to a Nixtla interval level.

## Parameters

q\_lo, q\_hi : float
Quantiles in (0, 1) symmetric about 0.5 (`q_lo + q_hi == 1`).

## Returns

int
The interval level, e.g. `(0.1, 0.9) -> 80`.

## Raises

ValueError
If the pair is out of range or not symmetric about 0.5.


## Related topics

- [Ragged panels](/conformalforecast/docs/capabilities/ragged-panels.html.md)
- [Online updating](/conformalforecast/docs/capabilities/online-updating.html.md)
- [Architecture](/conformalforecast/docs/concepts/architecture.html.md)
- [Installation](/conformalforecast/docs/getting-started/installation.html.md)
- [Conformalized quantile regression](/conformalforecast/docs/methods/cqr.html.md)
