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

# Forecasting

> Forecast a univariate time series with Nori and return point and quantile predictions.

`NoriTSForecaster` converts a time series into tabular features and predicts a
requested number of future steps. It returns a pandas DataFrame containing the
median forecast and any requested quantiles. Forecasting runs locally and
requires the `timeseries` extra.

## Quickstart

<Steps>
  <Step title="Install the forecasting dependencies">
    Follow [Installation](/setup/installation#forecasting) to install the
    `timeseries` extra for `synthefy-nori`.
  </Step>

  <Step title="Create a history DataFrame">
    Provide `timestamp` and `target` columns, ordered from oldest to newest.

    ```python theme={null}
    import numpy as np
    import pandas as pd
    from synthefy_nori.nori_ts import NoriTSForecaster

    periods = 500
    history = pd.DataFrame({
        "timestamp": pd.date_range("2024-01-01", periods=periods, freq="h"),
        "target": 10 + np.sin(2 * np.pi * np.arange(periods) / 24),
    })
    ```
  </Step>

  <Step title="Forecast the horizon">
    `predict_df` returns one row per future timestamp.

    ```python theme={null}
    forecaster = NoriTSForecaster(
        model="nori-30m",
        quantiles=[0.1, 0.5, 0.9],
    )
    forecast = forecaster.predict_df(history, prediction_length=48)

    print(forecast.columns.tolist())
    print(forecast.index.names)
    print(forecast.shape)
    ```

    ```text theme={null}
    ['target', '0.1', '0.5', '0.9']
    ['item_id', 'timestamp']
    (48, 4)
    ```

    `target` is the median forecast. The other columns contain the requested
    quantiles.
  </Step>
</Steps>

## Multiple series

Add an `item_id` column to forecast several series in one call. Nori fits and
forecasts each series independently.

```python theme={null}
import numpy as np
import pandas as pd
from synthefy_nori.nori_ts import NoriTSForecaster

periods = 300
t = np.arange(periods)
timestamps = pd.date_range("2024-01-01", periods=periods, freq="h")
history = pd.concat(
    [
        pd.DataFrame({
            "item_id": "north",
            "timestamp": timestamps,
            "target": 10 + np.sin(2 * np.pi * t / 24),
        }),
        pd.DataFrame({
            "item_id": "south",
            "timestamp": timestamps,
            "target": 20 + np.sin(2 * np.pi * t / 24 + np.pi / 4),
        }),
    ],
    ignore_index=True,
)

forecast = NoriTSForecaster(
    model="nori-30m",
    quantiles=[0.1, 0.5, 0.9],
).predict_df(history, prediction_length=24)

print(forecast.groupby(level="item_id").size().to_dict())
```

```text theme={null}
{'north': 24, 'south': 24}
```

## Future-known covariates

Use the lower-level `predict` method when a numeric feature is known across the
forecast horizon. For example, a planned promotion is known before prediction
and can be supplied for both the history and future rows.

```python theme={null}
import numpy as np
import pandas as pd
from synthefy_nori.nori_ts import NoriTSForecaster
from synthefy_nori.nori_ts.tsfeatures import TimeSeriesDataFrame

periods = 300
horizon = 24
t = np.arange(periods)
timestamps = pd.date_range("2024-01-01", periods=periods, freq="h")
promotion = ((t % 72) >= 60).astype(float)

history = pd.DataFrame({
    "item_id": "store-a",
    "timestamp": timestamps,
    "target": 10 + np.sin(2 * np.pi * t / 24) + 3 * promotion,
    "promotion": promotion,
})

future_timestamps = pd.date_range(
    timestamps[-1] + pd.Timedelta(hours=1), periods=horizon, freq="h"
)
future = pd.DataFrame({
    "item_id": "store-a",
    "timestamp": future_timestamps,
    "target": np.nan,
    "promotion": np.r_[np.zeros(12), np.ones(12)],
})

forecast = NoriTSForecaster(
    model="nori-30m",
    quantiles=[0.1, 0.5, 0.9],
).predict(
    TimeSeriesDataFrame.from_data_frame(history),
    TimeSeriesDataFrame.from_data_frame(future),
)

print(forecast.columns.tolist())
print(forecast.shape)
```

```text theme={null}
['target', '0.1', '0.5', '0.9']
(24, 4)
```

The target remains univariate; the additional `promotion` column is an input
feature. Only provide values available at prediction time, such as planned
promotions, holidays, or scheduled prices. A realized future target or any value
observed only after the forecast timestamp would be target leakage.

## How it works

<AccordionGroup>
  <Accordion title="Tabular conversion" icon="table-cells">
    For each series, the forecaster creates rows indexed by
    `(item_id, timestamp)`. Historical rows contain the observed target, and
    future rows contain generated time features. Numeric columns supplied in
    both the history and horizon are also used as covariates. Nori fits on the
    historical rows and predicts the future rows.
  </Accordion>

  <Accordion title="The time features" icon="wave-square">
    The default features include a running index, calendar sine and cosine
    values, and seasonal periods detected from the history. You can replace
    them with custom feature generators through `features=`.
  </Accordion>

  <Accordion title="Quantile output" icon="chart-area">
    Set `quantiles=` to choose the returned quantile columns. The `target`
    column contains the median forecast.
  </Accordion>
</AccordionGroup>

## Configuration

<AccordionGroup>
  <Accordion title="NoriTSForecaster parameters" icon="sliders">
    | Parameter        | Default       | Description                                                                                     |
    | ---------------- | ------------- | ----------------------------------------------------------------------------------------------- |
    | `quantiles`      | `[0.1 … 0.9]` | Forecast quantile levels; the median is the point forecast.                                     |
    | `context_length` | `4096`        | Cap on history rows per series (last-N kept).                                                   |
    | `device`         | `None`        | Torch device (e.g. `"cuda:0"`); `None` auto-selects.                                            |
    | `features`       | `None`        | Feature generators; defaults to the standard set.                                               |
    | `model`          | `"nori-6m"`   | Nori checkpoint variant; use `"nori-6m"` or `"nori-30m"`.                                       |
    | `model_path`     | `None`        | Explicit checkpoint path; overrides `model`. `None` resolves the named model from Hugging Face. |
  </Accordion>

  <Accordion title="predict_df(context_df, prediction_length)" icon="code">
    `context_df` needs a `timestamp` and a `target` column (an optional
    `item_id` for multi-series frames). Returns a DataFrame indexed by
    `(item_id, timestamp)` over the horizon, with a `target` (median) column and
    one column per quantile level (`"0.1"`, `"0.5"`, …).

    ```python theme={null}
    forecast = NoriTSForecaster(
        model="nori-30m",
        quantiles=[0.1, 0.5, 0.9],
    ).predict_df(
        history,
        prediction_length=48,
    )
    ```

    For frames already in `(item_id, timestamp)` form, `predict(train_tsdf,
            test_tsdf)` is the lower-level entry point. Use it when the horizon includes
    future-known covariates; `predict_df` constructs the horizon from timestamps
    alone.
  </Accordion>
</AccordionGroup>

## Next steps

Continue with [Text Features](/nori/text-features), or browse more local Nori
workflows in [Examples](/nori/examples).
