Skip to content

timecopilot.models.foundation

Chronos

Chronos(
    repo_id: str = "amazon/chronos-t5-large",
    batch_size: int = 16,
    alias: str = "Chronos",
)

Bases: Forecaster

Chronos models are large pre-trained models for time series forecasting, supporting both probabilistic and point forecasts. See the official repo for more details.

Parameters:

Name Type Description Default
repo_id str

The Hugging Face Hub model ID or local path to load the Chronos model from. Examples include "amazon/chronos-t5-tiny", "amazon/chronos-t5-large", or a local directory. Defaults to "amazon/chronos-t5-large". See the full list of available models at Hugging Face

'amazon/chronos-t5-large'
batch_size int

Batch size to use for inference. Larger models may require smaller batch sizes due to GPU memory constraints. Defaults to 16. For Chronos-Bolt models, higher batch sizes (e.g., 256) are possible.

16
alias str

Name to use for the model in output DataFrames and logs. Defaults to "Chronos".

'Chronos'
Notes

Available models:

Model ID Parameters
amazon/chronos-2 120M
amazon/chronos-bolt-tiny 9M
amazon/chronos-bolt-mini 21M
amazon/chronos-bolt-small 48M
amazon/chronos-bolt-base 205M
amazon/chronos-t5-tiny 8M
amazon/chronos-t5-mini 20M
amazon/chronos-t5-small 46M
amazon/chronos-t5-base 200M
amazon/chronos-t5-large 710M

Academic Reference:

Resources:

Technical Details:

  • The model is loaded onto the best available device (GPU if available, otherwise CPU).
  • For best performance with large models (e.g., "chronos-t5-large"), a CUDA-compatible GPU is recommended.
  • The model weights are loaded with torch_dtype=torch.bfloat16 for efficiency on supported hardware.
Source code in timecopilot/models/foundation/chronos.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
def __init__(
    self,
    repo_id: str = "amazon/chronos-t5-large",
    batch_size: int = 16,
    alias: str = "Chronos",
):
    # ruff: noqa: E501
    """
    Args:
        repo_id (str, optional): The Hugging Face Hub model ID or local
            path to load the Chronos model from. Examples include
            "amazon/chronos-t5-tiny", "amazon/chronos-t5-large", or a
            local directory. Defaults to "amazon/chronos-t5-large". See
            the full list of available models at
            [Hugging Face](https://huggingface.co/collections/
            amazon/chronos-models-65f1791d630a8d57cb718444)
        batch_size (int, optional): Batch size to use for inference.
            Larger models may require smaller batch sizes due to GPU
            memory constraints. Defaults to 16. For Chronos-Bolt models,
            higher batch sizes (e.g., 256) are possible.
        alias (str, optional): Name to use for the model in output
            DataFrames and logs. Defaults to "Chronos".

    Notes:
        **Available models:**

        | Model ID                                                               | Parameters |
        | ---------------------------------------------------------------------- | ---------- |
        | [`amazon/chronos-2`](https://huggingface.co/amazon/chronos-2)   | 120M         |
        | [`amazon/chronos-bolt-tiny`](https://huggingface.co/amazon/chronos-bolt-tiny)   | 9M         |
        | [`amazon/chronos-bolt-mini`](https://huggingface.co/amazon/chronos-bolt-mini)   | 21M        |
        | [`amazon/chronos-bolt-small`](https://huggingface.co/amazon/chronos-bolt-small) | 48M        |
        | [`amazon/chronos-bolt-base`](https://huggingface.co/amazon/chronos-bolt-base)   | 205M       |
        | [`amazon/chronos-t5-tiny`](https://huggingface.co/amazon/chronos-t5-tiny)   | 8M         |
        | [`amazon/chronos-t5-mini`](https://huggingface.co/amazon/chronos-t5-mini)   | 20M        |
        | [`amazon/chronos-t5-small`](https://huggingface.co/amazon/chronos-t5-small) | 46M        |
        | [`amazon/chronos-t5-base`](https://huggingface.co/amazon/chronos-t5-base)   | 200M       |
        | [`amazon/chronos-t5-large`](https://huggingface.co/amazon/chronos-t5-large) | 710M       |

        **Academic Reference:**

        - Paper: [Chronos: Learning the Language of Time Series](https://arxiv.org/abs/2403.07815)

        **Resources:**

        - GitHub: [amazon-science/chronos-forecasting](https://github.com/amazon-science/chronos-forecasting)
        - HuggingFace: [amazon/chronos-models](https://huggingface.co/collections/amazon/chronos-models-65f1791d630a8d57cb718444)

        **Technical Details:**

        - The model is loaded onto the best available device (GPU if
          available, otherwise CPU).
        - For best performance with large models (e.g., "chronos-t5-large"),
          a CUDA-compatible GPU is recommended.
        - The model weights are loaded with torch_dtype=torch.bfloat16 for
          efficiency on supported hardware.

    """
    self.repo_id = repo_id
    self.batch_size = batch_size
    self.alias = alias

forecast

forecast(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Generate forecasts for time series data using the model.

This method produces point forecasts and, optionally, prediction intervals or quantile forecasts. The input DataFrame can contain one or multiple time series in stacked (long) format.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). If provided, the returned DataFrame will include lower and upper interval columns for each specified level.

None
quantiles list[float]

List of quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. When provided, the output DataFrame will contain additional columns named in the format "model-q-{percentile}", where {percentile} = 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing forecast results. Includes:

- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.

For multi-series data, the output retains the same unique identifiers as the input DataFrame.

Source code in timecopilot/models/foundation/chronos.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
def forecast(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """Generate forecasts for time series data using the model.

    This method produces point forecasts and, optionally, prediction
    intervals or quantile forecasts. The input DataFrame can contain one
    or multiple time series in stacked (long) format.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.org/
            pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for
            valid values. If not provided, the frequency will be inferred
            from the data.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). If provided, the returned
            DataFrame will include lower and upper interval columns for
            each specified level.
        quantiles (list[float], optional):
            List of quantiles to forecast, expressed as floats between 0
            and 1. Should not be used simultaneously with `level`. When
            provided, the output DataFrame will contain additional columns
            named in the format "model-q-{percentile}", where {percentile}
            = 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing forecast results. Includes:

                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.

            For multi-series data, the output retains the same unique
            identifiers as the input DataFrame.
    """
    freq = self._maybe_infer_freq(df, freq)
    qc = QuantileConverter(level=level, quantiles=quantiles)
    dataset = TimeSeriesDataset.from_df(df, batch_size=self.batch_size)
    fcst_df = dataset.make_future_dataframe(h=h, freq=freq)
    with self._get_model() as model:
        fcsts_mean_np, fcsts_quantiles_np = self._predict(
            model,
            dataset,
            h,
            quantiles=qc.quantiles,
        )
    fcst_df[self.alias] = fcsts_mean_np.reshape(-1, 1)
    if qc.quantiles is not None and fcsts_quantiles_np is not None:
        for i, q in enumerate(qc.quantiles):
            fcst_df[f"{self.alias}-q-{int(q * 100)}"] = fcsts_quantiles_np[
                ..., i
            ].reshape(-1, 1)
        fcst_df = qc.maybe_convert_quantiles_to_level(
            fcst_df,
            models=[self.alias],
        )
    return fcst_df

cross_validation

cross_validation(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Perform cross-validation on time series data.

This method splits the time series into multiple training and testing windows and generates forecasts for each window. It enables evaluating forecast accuracy over different historical periods. Supports point forecasts and, optionally, prediction intervals or quantile forecasts.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict in each window.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. Defaults to 1.

1
step_size int

Step size between the start of consecutive windows. If None, it defaults to h.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). When specified, the output DataFrame includes lower and upper interval columns for each level.

None
quantiles list[float]

Quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. If provided, additional columns named "model-q-{percentile}" will appear in the output, where {percentile} is 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- "cutoff" column to indicate which window each forecast
  belongs to.
- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.
Source code in timecopilot/models/utils/forecaster.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def cross_validation(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """
    Perform cross-validation on time series data.

    This method splits the time series into multiple training and testing
    windows and generates forecasts for each window. It enables evaluating
    forecast accuracy over different historical periods. Supports point
    forecasts and, optionally, prediction intervals or quantile forecasts.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict in
            each window.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate. Defaults to 1.
        step_size (int, optional):
            Step size between the start of consecutive windows. If None, it
            defaults to `h`.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). When specified, the output
            DataFrame includes lower and upper interval columns for each
            level.
        quantiles (list[float], optional):
            Quantiles to forecast, expressed as floats between 0 and 1.
            Should not be used simultaneously with `level`. If provided,
            additional columns named "model-q-{percentile}" will appear in
            the output, where {percentile} is 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - "cutoff" column to indicate which window each forecast
                  belongs to.
                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    # mlforecast cv code
    results = []
    sort_idxs = maybe_compute_sort_indices(df, "unique_id", "ds")
    if sort_idxs is not None:
        df = take_rows(df, sort_idxs)
    splits = backtest_splits(
        df,
        n_windows=n_windows,
        h=h,
        id_col="unique_id",
        time_col="ds",
        freq=pd.tseries.frequencies.to_offset(freq),
        step_size=h if step_size is None else step_size,
    )
    for _, (cutoffs, train, valid) in tqdm(enumerate(splits)):
        if len(valid.columns) > 3:
            raise NotImplementedError(
                "Cross validation with exogenous variables is not yet supported."
            )
        y_pred = self.forecast(
            df=train,
            h=h,
            freq=freq,
            level=level,
            quantiles=quantiles,
        )
        y_pred = join(y_pred, cutoffs, on="unique_id", how="left")
        result = join(
            valid[["unique_id", "ds", "y"]],
            y_pred,
            on=["unique_id", "ds"],
        )
        if result.shape[0] < valid.shape[0]:
            raise ValueError(
                "Cross validation result produced less results than expected. "
                "Please verify that the frequency parameter (freq) "
                "matches your series' "
                "and that there aren't any missing periods."
            )
        results.append(result)
    out = vertical_concat(results)
    out = drop_index_if_pandas(out)
    first_out_cols = ["unique_id", "ds", "cutoff", "y"]
    remaining_cols = [c for c in out.columns if c not in first_out_cols]
    fcst_cv_df = out[first_out_cols + remaining_cols]
    return fcst_cv_df

detect_anomalies

detect_anomalies(
    df: DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> DataFrame

Detect anomalies in time-series using a cross-validated z-score test.

This method uses rolling-origin cross-validation to (1) produce adjusted (out-of-sample) predictions and (2) estimate the standard deviation of forecast errors. It then computes a per-point z-score, flags values outside a two-sided prediction interval (with confidence level), and returns a DataFrame with results.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to detect anomalies.

required
h int

Forecast horizon specifying how many future steps to predict. In each cross validation window. If not provided, the seasonality of the data (inferred from the frequency) is used.

None
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. If not provided, the maximum number of windows (computed by the shortest time series) is used. If provided, the number of windows is the minimum between the maximum number of windows (computed by the shortest time series) and the number of windows provided.

None
level int | float

Confidence levels for z-score, expressed as percentages (e.g. 80, 95). Default is 99.

99

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- model column to indicate the model.
- lower prediction interval.
- upper prediction interval.
- anomaly column to indicate if the value is an anomaly.
    an anomaly is defined as a value that is outside of the
    prediction interval (True or False).
Source code in timecopilot/models/utils/forecaster.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
def detect_anomalies(
    self,
    df: pd.DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> pd.DataFrame:
    """
    Detect anomalies in time-series using a cross-validated z-score test.

    This method uses rolling-origin cross-validation to (1) produce
    adjusted (out-of-sample) predictions and (2) estimate the
    standard deviation of forecast errors. It then computes a per-point z-score,
    flags values outside a two-sided prediction interval (with confidence `level`),
    and returns a DataFrame with results.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to detect anomalies.
        h (int, optional):
            Forecast horizon specifying how many future steps to predict.
            In each cross validation window. If not provided, the seasonality
            of the data (inferred from the frequency) is used.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate.
            If not provided, the maximum number of windows
            (computed by the shortest time series) is used.
            If provided, the number of windows is the minimum
            between the maximum number of windows
            (computed by the shortest time series)
            and the number of windows provided.
        level (int | float):
            Confidence levels for z-score, expressed as
            percentages (e.g. 80, 95). Default is 99.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - model column to indicate the model.
                - lower prediction interval.
                - upper prediction interval.
                - anomaly column to indicate if the value is an anomaly.
                    an anomaly is defined as a value that is outside of the
                    prediction interval (True or False).
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    if h is None:
        h = self._maybe_get_seasonality(freq)
    min_series_length = df.groupby("unique_id").size().min()
    # we require at least one observation before the first forecast
    max_possible_windows = (min_series_length - 1) // h
    if n_windows is None:
        _n_windows = max_possible_windows
    else:
        _n_windows = min(n_windows, max_possible_windows)
    if _n_windows < 1:
        raise ValueError(
            f"Cannot perform anomaly detection: series too short. "
            f"Minimum series length required: {h + 1}, "
            f"actual minimum length: {min_series_length}"
        )
    cv_results = self.cross_validation(
        df=df,
        h=h,
        freq=freq,
        n_windows=_n_windows,
        step_size=h,  # this is the default but who knows, anxiety
    )
    cv_results["residuals"] = cv_results["y"] - cv_results[self.alias]
    residual_stats = (
        cv_results.groupby("unique_id")["residuals"].std().reset_index()
    )
    residual_stats.columns = ["unique_id", "residual_std"]
    cv_results = cv_results.merge(residual_stats, on="unique_id", how="left")
    cv_results["z_score"] = cv_results["residuals"] / cv_results["residual_std"]
    alpha = 1 - level / 100
    critical_z = stats.norm.ppf(1 - alpha / 2)
    an_col = f"{self.alias}-anomaly"
    cv_results[an_col] = np.abs(cv_results["z_score"]) > critical_z
    lo_col = f"{self.alias}-lo-{int(level)}"
    hi_col = f"{self.alias}-hi-{int(level)}"
    margin = critical_z * cv_results["residual_std"]
    cv_results[lo_col] = cv_results[self.alias] - margin
    cv_results[hi_col] = cv_results[self.alias] + margin
    output_cols = [
        "unique_id",
        "ds",
        "cutoff",
        "y",
        self.alias,
        lo_col,
        hi_col,
        an_col,
    ]
    result = cv_results[output_cols].copy()
    result = drop_index_if_pandas(result)
    return result

plot staticmethod

plot(
    df: DataFrame | None = None,
    forecasts_df: DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: Axes | ndarray | Figure | None = None,
)

Plot forecasts and insample values.

Parameters:

Name Type Description Default
df DataFrame

DataFrame with columns [unique_id, ds, y]. Defaults to None.

None
forecasts_df DataFrame

DataFrame with columns [unique_id, ds] and models. Defaults to None.

None
ids list[str]

Time Series to plot. If None, time series are selected randomly. Defaults to None.

None
plot_random bool

Select time series to plot randomly. Defaults to True.

True
max_ids int

Maximum number of ids to plot. Defaults to 8.

8
models list[str]

Models to plot. Defaults to None.

None
level list[float]

Prediction intervals to plot. Defaults to None.

None
max_insample_length int

Maximum number of train/insample observations to be plotted. Defaults to None.

None
plot_anomalies bool

Plot anomalies for each prediction interval. Defaults to False.

False
engine str

Library used to plot. 'plotly', 'plotly-resampler' or 'matplotlib'. Defaults to 'matplotlib'.

'matplotlib'
palette str

Name of the matplotlib colormap to use for the plots. If None, uses the current style. Defaults to None.

None
seed int

Seed used for the random number generator. Only used if plot_random is True. Defaults to 0.

None
resampler_kwargs dict

Keyword arguments to be passed to plotly-resampler constructor. For further custumization ("show_dash") call the method, store the plotting object and add the extra arguments to its show_dash method. Defaults to None.

None
ax matplotlib axes, array of matplotlib axes or plotly Figure

Object where plots will be added. Defaults to None.

None
Source code in timecopilot/models/utils/forecaster.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
@staticmethod
def plot(
    df: pd.DataFrame | None = None,
    forecasts_df: pd.DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: plt.Axes | np.ndarray | plotly.graph_objects.Figure | None = None,
):
    """Plot forecasts and insample values.

    Args:
        df (pd.DataFrame, optional): DataFrame with columns
            [`unique_id`, `ds`, `y`]. Defaults to None.
        forecasts_df (pd.DataFrame, optional): DataFrame with
            columns [`unique_id`, `ds`] and models. Defaults to None.
        ids (list[str], optional): Time Series to plot. If None, time series
            are selected randomly. Defaults to None.
        plot_random (bool, optional): Select time series to plot randomly.
            Defaults to True.
        max_ids (int, optional): Maximum number of ids to plot. Defaults to 8.
        models (list[str], optional): Models to plot. Defaults to None.
        level (list[float], optional): Prediction intervals to plot.
            Defaults to None.
        max_insample_length (int, optional): Maximum number of train/insample
            observations to be plotted. Defaults to None.
        plot_anomalies (bool, optional): Plot anomalies for each prediction
            interval. Defaults to False.
        engine (str, optional): Library used to plot. 'plotly', 'plotly-resampler'
            or 'matplotlib'. Defaults to 'matplotlib'.
        palette (str, optional): Name of the matplotlib colormap to use for the
            plots. If None, uses the current style. Defaults to None.
        seed (int, optional): Seed used for the random number generator. Only
            used if plot_random is True. Defaults to 0.
        resampler_kwargs (dict, optional): Keyword arguments to be passed to
            plotly-resampler constructor. For further custumization ("show_dash")
            call the method, store the plotting object and add the extra arguments
            to its `show_dash` method. Defaults to None.
        ax (matplotlib axes, array of matplotlib axes or plotly Figure, optional):
            Object where plots will be added. Defaults to None.
    """
    df = ensure_time_dtype(df, time_col="ds")
    if forecasts_df is not None:
        forecasts_df = ensure_time_dtype(forecasts_df, time_col="ds")
        if any("anomaly" in col for col in forecasts_df.columns):
            df = None
            models = [
                col.split("-")[0]
                for col in forecasts_df.columns
                if col.endswith("-anomaly")
            ]
            forecasts_df = ufp.drop_columns(
                forecasts_df,
                [f"{model}-anomaly" for model in models],
            )
            lv_cols = [
                c.replace(f"{model}-lo-", "")
                for model in models
                for c in forecasts_df.columns
                if f"{model}-lo-" in c
            ]
            level = [float(c) if "." in c else int(c) for c in lv_cols]
            level = list(set(level))
            plot_anomalies = True
    return plot_series(
        df=df,
        forecasts_df=forecasts_df,
        ids=ids,
        plot_random=plot_random,
        max_ids=max_ids,
        models=models,
        level=level,
        max_insample_length=max_insample_length,
        plot_anomalies=plot_anomalies,
        engine=engine,
        resampler_kwargs=resampler_kwargs,
        palette=palette,
        seed=seed,
        id_col="unique_id",
        time_col="ds",
        target_col="y",
        ax=ax,
    )

FlowState

FlowState(
    repo_id: str = "ibm-research/flowstate",
    scale_factor: float | None = None,
    context_length: int = 2048,
    batch_size: int = 1024,
    alias: str = "FlowState",
)

Bases: Forecaster

FlowState is the first time-scale adjustable Time Series Foundation Model (TSFM), open-sourced by IBM Research. Combining a State Space Model (SSM) Encoder with a Functional Basis Decoder allows FlowState to transition into a timescale invariant coefficient space and make a continuous forecast from this space. This allows FlowState to seamlessly adjust to all possible sampling rates.

See the official repo and paper for more details.

Initialize FlowState time series foundation model.

Parameters:

Name Type Description Default
repo_id str

The Hugging Face Hub model ID or local path to load the FlowState model from. Supported models:

  • ibm-research/flowstate (default)
  • ibm-granite/granite-timeseries-flowstate-r1.
'ibm-research/flowstate'
scale_factor float

Scale factor for temporal adaptation. If None, will be automatically determined based on the time series frequency. The scale factor adjusts the model to different sampling rates. For example, if your data has seasonality every N=96 time steps (quarter hourly with daily cycle), scale_factor = 24/96 = 0.25.

None
context_length int

Maximum context length (input window size) for the model. Controls how much history is used for each forecast. Defaults to 2,048. The model supports flexible context lengths.

2048
batch_size int

Batch size for inference. Defaults to 1,024. Adjust based on available memory and model size. Larger batch sizes can improve throughput but require more GPU memory.

1024
alias str

Name to use for the model in output DataFrames and logs. Defaults to "FlowState".

'FlowState'
Notes

Academic Reference:

Resources:

Technical Details:

  • The model is loaded onto the best available device (GPU if available, otherwise CPU).
  • FlowState uses State Space Model (SSM) encoder with Functional Basis Decoder (FBD) for time-scale invariant forecasting.
  • Recommended forecasting horizon: no more than 30 seasons.

Supported Models:

  • ibm-research/flowstate (default)
  • ibm-granite/granite-timeseries-flowstate-r1.
Source code in timecopilot/models/foundation/flowstate.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def __init__(
    self,
    repo_id: str = "ibm-research/flowstate",
    scale_factor: float | None = None,
    context_length: int = 2_048,
    batch_size: int = 1_024,
    alias: str = "FlowState",
):
    """
    Initialize FlowState time series foundation model.

    Args:
        repo_id (str, optional): The Hugging Face Hub model ID or local path to
            load the FlowState model from. Supported models:

            - `ibm-research/flowstate` (default)
            - `ibm-granite/granite-timeseries-flowstate-r1`.

        scale_factor (float, optional): Scale factor for temporal adaptation.
            If None, will be automatically determined based on the time series
            frequency. The scale factor adjusts the model to different sampling
            rates. For example, if your data has seasonality every N=96 time steps
            (quarter hourly with daily cycle), scale_factor = 24/96 = 0.25.
        context_length (int, optional): Maximum context length (input window size)
            for the model. Controls how much history is used for each forecast.
            Defaults to 2,048. The model supports flexible context lengths.
        batch_size (int, optional): Batch size for inference. Defaults to 1,024.
            Adjust based on available memory and model size. Larger batch sizes
            can improve throughput but require more GPU memory.
        alias (str, optional): Name to use for the model in output DataFrames and
            logs. Defaults to "FlowState".

    Notes:
        **Academic Reference:**

        - Paper: [FlowState: Sampling Rate Invariant Time Series Forecasting](https://arxiv.org/abs/2508.05287)

        **Resources:**

        - GitHub: [ibm-granite/granite-tsfm](https://github.com/ibm-granite/granite-tsfm)
        - HuggingFace Models: [ibm-granite/granite-timeseries-flowstate-r1](
            https://huggingface.co/ibm-granite/granite-timeseries-flowstate-r1
          ), [ibm-research/flowstate](https://huggingface.co/ibm-research/flowstate).

        **Technical Details:**

        - The model is loaded onto the best available device (GPU if
          available, otherwise CPU).
        - FlowState uses State Space Model (SSM) encoder with Functional
          Basis Decoder (FBD) for time-scale invariant forecasting.
        - Recommended forecasting horizon: no more than 30 seasons.

        **Supported Models:**

        - `ibm-research/flowstate` (default)
        - `ibm-granite/granite-timeseries-flowstate-r1`.
    """
    self.repo_id = repo_id
    self.scale_factor = scale_factor
    self.context_length = context_length
    self.batch_size = batch_size
    self.alias = alias
    self.device = "cuda" if torch.cuda.is_available() else "cpu"
    self.dtype = torch.float32

forecast

forecast(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Generate forecasts for time series data using the model.

This method produces point forecasts and, optionally, prediction intervals or quantile forecasts. The input DataFrame can contain one or multiple time series in stacked (long) format.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). If provided, the returned DataFrame will include lower and upper interval columns for each specified level.

None
quantiles list[float]

List of quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. When provided, the output DataFrame will contain additional columns named in the format "model-q-{percentile}", where {percentile} = 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing forecast results. Includes:

- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.

For multi-series data, the output retains the same unique identifiers as the input DataFrame.

Source code in timecopilot/models/foundation/flowstate.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
def forecast(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """Generate forecasts for time series data using the model.

    This method produces point forecasts and, optionally, prediction
    intervals or quantile forecasts. The input DataFrame can contain one
    or multiple time series in stacked (long) format.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.org/
            pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for
            valid values. If not provided, the frequency will be inferred
            from the data.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). If provided, the returned
            DataFrame will include lower and upper interval columns for
            each specified level.
        quantiles (list[float], optional):
            List of quantiles to forecast, expressed as floats between 0
            and 1. Should not be used simultaneously with `level`. When
            provided, the output DataFrame will contain additional columns
            named in the format "model-q-{percentile}", where {percentile}
            = 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing forecast results. Includes:

                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.

            For multi-series data, the output retains the same unique
            identifiers as the input DataFrame.
    """
    freq = self._maybe_infer_freq(df, freq)
    qc = QuantileConverter(level=level, quantiles=quantiles)
    dataset = TimeSeriesDataset.from_df(
        df,
        batch_size=self.batch_size,
        dtype=self.dtype,
    )
    fcst_df = dataset.make_future_dataframe(h=h, freq=freq)
    scale_factor = self.scale_factor or get_fixed_factor(freq)
    with self._get_model() as model:
        cfg = model.config
        supported_quantiles = cfg.quantiles
        if qc.quantiles is not None and not np.allclose(
            qc.quantiles,
            supported_quantiles,
        ):
            raise ValueError(
                "FlowState only supports the default quantiles, "
                f"supported quantiles are {supported_quantiles}, "
                "please use the default quantiles or default level, "
            )
        fcsts_mean_np, fcsts_quantiles_np = self._predict(
            model,
            dataset,
            h,
            quantiles=qc.quantiles,
            supported_quantiles=supported_quantiles,
            scale_factor=scale_factor,
        )
    fcst_df[self.alias] = fcsts_mean_np.reshape(-1, 1)
    if qc.quantiles is not None and fcsts_quantiles_np is not None:
        for i, q in enumerate(qc.quantiles):
            fcst_df[f"{self.alias}-q-{int(q * 100)}"] = fcsts_quantiles_np[
                ..., i
            ].reshape(-1, 1)
        fcst_df = qc.maybe_convert_quantiles_to_level(
            fcst_df,
            models=[self.alias],
        )
    return fcst_df

cross_validation

cross_validation(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Perform cross-validation on time series data.

This method splits the time series into multiple training and testing windows and generates forecasts for each window. It enables evaluating forecast accuracy over different historical periods. Supports point forecasts and, optionally, prediction intervals or quantile forecasts.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict in each window.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. Defaults to 1.

1
step_size int

Step size between the start of consecutive windows. If None, it defaults to h.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). When specified, the output DataFrame includes lower and upper interval columns for each level.

None
quantiles list[float]

Quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. If provided, additional columns named "model-q-{percentile}" will appear in the output, where {percentile} is 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- "cutoff" column to indicate which window each forecast
  belongs to.
- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.
Source code in timecopilot/models/utils/forecaster.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def cross_validation(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """
    Perform cross-validation on time series data.

    This method splits the time series into multiple training and testing
    windows and generates forecasts for each window. It enables evaluating
    forecast accuracy over different historical periods. Supports point
    forecasts and, optionally, prediction intervals or quantile forecasts.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict in
            each window.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate. Defaults to 1.
        step_size (int, optional):
            Step size between the start of consecutive windows. If None, it
            defaults to `h`.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). When specified, the output
            DataFrame includes lower and upper interval columns for each
            level.
        quantiles (list[float], optional):
            Quantiles to forecast, expressed as floats between 0 and 1.
            Should not be used simultaneously with `level`. If provided,
            additional columns named "model-q-{percentile}" will appear in
            the output, where {percentile} is 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - "cutoff" column to indicate which window each forecast
                  belongs to.
                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    # mlforecast cv code
    results = []
    sort_idxs = maybe_compute_sort_indices(df, "unique_id", "ds")
    if sort_idxs is not None:
        df = take_rows(df, sort_idxs)
    splits = backtest_splits(
        df,
        n_windows=n_windows,
        h=h,
        id_col="unique_id",
        time_col="ds",
        freq=pd.tseries.frequencies.to_offset(freq),
        step_size=h if step_size is None else step_size,
    )
    for _, (cutoffs, train, valid) in tqdm(enumerate(splits)):
        if len(valid.columns) > 3:
            raise NotImplementedError(
                "Cross validation with exogenous variables is not yet supported."
            )
        y_pred = self.forecast(
            df=train,
            h=h,
            freq=freq,
            level=level,
            quantiles=quantiles,
        )
        y_pred = join(y_pred, cutoffs, on="unique_id", how="left")
        result = join(
            valid[["unique_id", "ds", "y"]],
            y_pred,
            on=["unique_id", "ds"],
        )
        if result.shape[0] < valid.shape[0]:
            raise ValueError(
                "Cross validation result produced less results than expected. "
                "Please verify that the frequency parameter (freq) "
                "matches your series' "
                "and that there aren't any missing periods."
            )
        results.append(result)
    out = vertical_concat(results)
    out = drop_index_if_pandas(out)
    first_out_cols = ["unique_id", "ds", "cutoff", "y"]
    remaining_cols = [c for c in out.columns if c not in first_out_cols]
    fcst_cv_df = out[first_out_cols + remaining_cols]
    return fcst_cv_df

detect_anomalies

detect_anomalies(
    df: DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> DataFrame

Detect anomalies in time-series using a cross-validated z-score test.

This method uses rolling-origin cross-validation to (1) produce adjusted (out-of-sample) predictions and (2) estimate the standard deviation of forecast errors. It then computes a per-point z-score, flags values outside a two-sided prediction interval (with confidence level), and returns a DataFrame with results.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to detect anomalies.

required
h int

Forecast horizon specifying how many future steps to predict. In each cross validation window. If not provided, the seasonality of the data (inferred from the frequency) is used.

None
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. If not provided, the maximum number of windows (computed by the shortest time series) is used. If provided, the number of windows is the minimum between the maximum number of windows (computed by the shortest time series) and the number of windows provided.

None
level int | float

Confidence levels for z-score, expressed as percentages (e.g. 80, 95). Default is 99.

99

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- model column to indicate the model.
- lower prediction interval.
- upper prediction interval.
- anomaly column to indicate if the value is an anomaly.
    an anomaly is defined as a value that is outside of the
    prediction interval (True or False).
Source code in timecopilot/models/utils/forecaster.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
def detect_anomalies(
    self,
    df: pd.DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> pd.DataFrame:
    """
    Detect anomalies in time-series using a cross-validated z-score test.

    This method uses rolling-origin cross-validation to (1) produce
    adjusted (out-of-sample) predictions and (2) estimate the
    standard deviation of forecast errors. It then computes a per-point z-score,
    flags values outside a two-sided prediction interval (with confidence `level`),
    and returns a DataFrame with results.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to detect anomalies.
        h (int, optional):
            Forecast horizon specifying how many future steps to predict.
            In each cross validation window. If not provided, the seasonality
            of the data (inferred from the frequency) is used.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate.
            If not provided, the maximum number of windows
            (computed by the shortest time series) is used.
            If provided, the number of windows is the minimum
            between the maximum number of windows
            (computed by the shortest time series)
            and the number of windows provided.
        level (int | float):
            Confidence levels for z-score, expressed as
            percentages (e.g. 80, 95). Default is 99.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - model column to indicate the model.
                - lower prediction interval.
                - upper prediction interval.
                - anomaly column to indicate if the value is an anomaly.
                    an anomaly is defined as a value that is outside of the
                    prediction interval (True or False).
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    if h is None:
        h = self._maybe_get_seasonality(freq)
    min_series_length = df.groupby("unique_id").size().min()
    # we require at least one observation before the first forecast
    max_possible_windows = (min_series_length - 1) // h
    if n_windows is None:
        _n_windows = max_possible_windows
    else:
        _n_windows = min(n_windows, max_possible_windows)
    if _n_windows < 1:
        raise ValueError(
            f"Cannot perform anomaly detection: series too short. "
            f"Minimum series length required: {h + 1}, "
            f"actual minimum length: {min_series_length}"
        )
    cv_results = self.cross_validation(
        df=df,
        h=h,
        freq=freq,
        n_windows=_n_windows,
        step_size=h,  # this is the default but who knows, anxiety
    )
    cv_results["residuals"] = cv_results["y"] - cv_results[self.alias]
    residual_stats = (
        cv_results.groupby("unique_id")["residuals"].std().reset_index()
    )
    residual_stats.columns = ["unique_id", "residual_std"]
    cv_results = cv_results.merge(residual_stats, on="unique_id", how="left")
    cv_results["z_score"] = cv_results["residuals"] / cv_results["residual_std"]
    alpha = 1 - level / 100
    critical_z = stats.norm.ppf(1 - alpha / 2)
    an_col = f"{self.alias}-anomaly"
    cv_results[an_col] = np.abs(cv_results["z_score"]) > critical_z
    lo_col = f"{self.alias}-lo-{int(level)}"
    hi_col = f"{self.alias}-hi-{int(level)}"
    margin = critical_z * cv_results["residual_std"]
    cv_results[lo_col] = cv_results[self.alias] - margin
    cv_results[hi_col] = cv_results[self.alias] + margin
    output_cols = [
        "unique_id",
        "ds",
        "cutoff",
        "y",
        self.alias,
        lo_col,
        hi_col,
        an_col,
    ]
    result = cv_results[output_cols].copy()
    result = drop_index_if_pandas(result)
    return result

plot staticmethod

plot(
    df: DataFrame | None = None,
    forecasts_df: DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: Axes | ndarray | Figure | None = None,
)

Plot forecasts and insample values.

Parameters:

Name Type Description Default
df DataFrame

DataFrame with columns [unique_id, ds, y]. Defaults to None.

None
forecasts_df DataFrame

DataFrame with columns [unique_id, ds] and models. Defaults to None.

None
ids list[str]

Time Series to plot. If None, time series are selected randomly. Defaults to None.

None
plot_random bool

Select time series to plot randomly. Defaults to True.

True
max_ids int

Maximum number of ids to plot. Defaults to 8.

8
models list[str]

Models to plot. Defaults to None.

None
level list[float]

Prediction intervals to plot. Defaults to None.

None
max_insample_length int

Maximum number of train/insample observations to be plotted. Defaults to None.

None
plot_anomalies bool

Plot anomalies for each prediction interval. Defaults to False.

False
engine str

Library used to plot. 'plotly', 'plotly-resampler' or 'matplotlib'. Defaults to 'matplotlib'.

'matplotlib'
palette str

Name of the matplotlib colormap to use for the plots. If None, uses the current style. Defaults to None.

None
seed int

Seed used for the random number generator. Only used if plot_random is True. Defaults to 0.

None
resampler_kwargs dict

Keyword arguments to be passed to plotly-resampler constructor. For further custumization ("show_dash") call the method, store the plotting object and add the extra arguments to its show_dash method. Defaults to None.

None
ax matplotlib axes, array of matplotlib axes or plotly Figure

Object where plots will be added. Defaults to None.

None
Source code in timecopilot/models/utils/forecaster.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
@staticmethod
def plot(
    df: pd.DataFrame | None = None,
    forecasts_df: pd.DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: plt.Axes | np.ndarray | plotly.graph_objects.Figure | None = None,
):
    """Plot forecasts and insample values.

    Args:
        df (pd.DataFrame, optional): DataFrame with columns
            [`unique_id`, `ds`, `y`]. Defaults to None.
        forecasts_df (pd.DataFrame, optional): DataFrame with
            columns [`unique_id`, `ds`] and models. Defaults to None.
        ids (list[str], optional): Time Series to plot. If None, time series
            are selected randomly. Defaults to None.
        plot_random (bool, optional): Select time series to plot randomly.
            Defaults to True.
        max_ids (int, optional): Maximum number of ids to plot. Defaults to 8.
        models (list[str], optional): Models to plot. Defaults to None.
        level (list[float], optional): Prediction intervals to plot.
            Defaults to None.
        max_insample_length (int, optional): Maximum number of train/insample
            observations to be plotted. Defaults to None.
        plot_anomalies (bool, optional): Plot anomalies for each prediction
            interval. Defaults to False.
        engine (str, optional): Library used to plot. 'plotly', 'plotly-resampler'
            or 'matplotlib'. Defaults to 'matplotlib'.
        palette (str, optional): Name of the matplotlib colormap to use for the
            plots. If None, uses the current style. Defaults to None.
        seed (int, optional): Seed used for the random number generator. Only
            used if plot_random is True. Defaults to 0.
        resampler_kwargs (dict, optional): Keyword arguments to be passed to
            plotly-resampler constructor. For further custumization ("show_dash")
            call the method, store the plotting object and add the extra arguments
            to its `show_dash` method. Defaults to None.
        ax (matplotlib axes, array of matplotlib axes or plotly Figure, optional):
            Object where plots will be added. Defaults to None.
    """
    df = ensure_time_dtype(df, time_col="ds")
    if forecasts_df is not None:
        forecasts_df = ensure_time_dtype(forecasts_df, time_col="ds")
        if any("anomaly" in col for col in forecasts_df.columns):
            df = None
            models = [
                col.split("-")[0]
                for col in forecasts_df.columns
                if col.endswith("-anomaly")
            ]
            forecasts_df = ufp.drop_columns(
                forecasts_df,
                [f"{model}-anomaly" for model in models],
            )
            lv_cols = [
                c.replace(f"{model}-lo-", "")
                for model in models
                for c in forecasts_df.columns
                if f"{model}-lo-" in c
            ]
            level = [float(c) if "." in c else int(c) for c in lv_cols]
            level = list(set(level))
            plot_anomalies = True
    return plot_series(
        df=df,
        forecasts_df=forecasts_df,
        ids=ids,
        plot_random=plot_random,
        max_ids=max_ids,
        models=models,
        level=level,
        max_insample_length=max_insample_length,
        plot_anomalies=plot_anomalies,
        engine=engine,
        resampler_kwargs=resampler_kwargs,
        palette=palette,
        seed=seed,
        id_col="unique_id",
        time_col="ds",
        target_col="y",
        ax=ax,
    )

Moirai

Moirai(
    repo_id: str = "Salesforce/moirai-1.0-R-large",
    filename: str = "model.ckpt",
    context_length: int = 4096,
    patch_size: int = 32,
    num_samples: int = 100,
    target_dim: int = 1,
    feat_dynamic_real_dim: int = 0,
    past_feat_dynamic_real_dim: int = 0,
    batch_size: int = 32,
    alias: str = "Moirai",
)

Bases: GluonTSForecaster

Moirai is a universal foundation model for time series forecasting, designed to handle a wide range of frequencies, multivariate series, and covariates. It uses a masked encoder-based transformer architecture with multi-patch size projection layers and Any-Variate Attention, enabling zero-shot and probabilistic forecasting. See the official repo, Hugging Face, and arXiv:2402.02592 for more details.

Parameters:

Name Type Description Default
repo_id str

The Hugging Face Hub model ID or local path to load the Moirai model from. Examples include "Salesforce/moirai-1.0-R-large". Defaults to "Salesforce/moirai-1.0-R-large". See the full list of models at Hugging Face.

'Salesforce/moirai-1.0-R-large'
filename str

Checkpoint filename for the model weights. Defaults to "model.ckpt".

'model.ckpt'
context_length int

Maximum context length (input window size) for the model. Controls how much history is used for each forecast. Defaults to 4096.

4096
patch_size int

Patch size for patch-based input encoding. Can be set to "auto" or a specific value (e.g., 8, 16, 32, 64, 128). Defaults to 32. See the Moirai paper for recommended values by frequency. Not used for Moirai-2.0.

32
num_samples int

Number of samples for probabilistic forecasting. Controls the number of forecast samples drawn for uncertainty estimation. Defaults to 100. Not used for Moirai-2.0.

100
target_dim int

Number of target variables (for multivariate forecasting). Defaults to 1.

1
feat_dynamic_real_dim int

Number of dynamic real covariates known in the future. Defaults to 0.

0
past_feat_dynamic_real_dim int

Number of past dynamic real covariates. Defaults to 0.

0
batch_size int

Batch size to use for inference. Defaults to 32. Adjust based on available memory and model size.

32
alias str

Name to use for the model in output DataFrames and logs. Defaults to "Moirai".

'Moirai'
Notes

Academic Reference:

Resources:

Technical Details:

  • The model is loaded onto the best available device (GPU if available, otherwise CPU).
Source code in timecopilot/models/foundation/moirai.py
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def __init__(
    self,
    repo_id: str = "Salesforce/moirai-1.0-R-large",
    filename: str = "model.ckpt",
    context_length: int = 4096,
    patch_size: int = 32,
    num_samples: int = 100,
    target_dim: int = 1,
    feat_dynamic_real_dim: int = 0,
    past_feat_dynamic_real_dim: int = 0,
    batch_size: int = 32,
    alias: str = "Moirai",
):
    """
    Args:
        repo_id (str, optional): The Hugging Face Hub model ID or local path to
            load the Moirai model from. Examples include
            "Salesforce/moirai-1.0-R-large". Defaults to
            "Salesforce/moirai-1.0-R-large". See the full list of models at
            [Hugging Face](https://huggingface.co/collections/Salesforce/
            moirai-r-models-65c8d3a94c51428c300e0742).
        filename (str, optional): Checkpoint filename for the model weights.
            Defaults to "model.ckpt".
        context_length (int, optional): Maximum context length (input window size)
            for the model. Controls how much history is used for each forecast.
            Defaults to 4096.
        patch_size (int, optional): Patch size for patch-based input encoding.
            Can be set to "auto" or a specific value (e.g., 8, 16, 32, 64, 128).
            Defaults to 32. See the Moirai paper for recommended values by
            frequency. Not used for Moirai-2.0.
        num_samples (int, optional): Number of samples for probabilistic
            forecasting. Controls the number of forecast samples drawn for
            uncertainty estimation. Defaults to 100.
            Not used for Moirai-2.0.
        target_dim (int, optional): Number of target variables (for multivariate
            forecasting). Defaults to 1.
        feat_dynamic_real_dim (int, optional): Number of dynamic real covariates
            known in the future. Defaults to 0.
        past_feat_dynamic_real_dim (int, optional): Number of past dynamic real
            covariates. Defaults to 0.
        batch_size (int, optional): Batch size to use for inference. Defaults to
            32. Adjust based on available memory and model size.
        alias (str, optional): Name to use for the model in output DataFrames and
            logs. Defaults to "Moirai".

    Notes:
        **Academic Reference:**

        - Paper: [Unified Training of Universal Time Series Forecasting Transformers](https://arxiv.org/abs/2402.02592)

        **Resources:**

        - GitHub: [SalesforceAIResearch/uni2ts](https://github.com/SalesforceAIResearch/uni2ts)
        - HuggingFace: [Salesforce/moirai-r-models](https://huggingface.co/collections/Salesforce/moirai-r-models-65c8d3a94c51428c300e0742)

        **Technical Details:**

        - The model is loaded onto the best available device (GPU if available,
          otherwise CPU).
    """
    super().__init__(
        repo_id=repo_id,
        filename=filename,
        alias=alias,
        num_samples=num_samples,
    )
    self.context_length = context_length
    self.patch_size = patch_size
    self.target_dim = target_dim
    self.feat_dynamic_real_dim = feat_dynamic_real_dim
    self.past_feat_dynamic_real_dim = past_feat_dynamic_real_dim
    self.batch_size = batch_size

forecast

forecast(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Generate forecasts for time series data using the model.

This method produces point forecasts and, optionally, prediction intervals or quantile forecasts. The input DataFrame can contain one or multiple time series in stacked (long) format.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If None, the frequency will be inferred from the data.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). If provided, the returned DataFrame will include lower and upper interval columns for each specified level.

None
quantiles list[float]

List of quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. When provided, the output DataFrame will contain additional columns named in the format "model-q-{percentile}", where {percentile} = 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing forecast results. Includes:

- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.

For multi-series data, the output retains the same unique identifiers as the input DataFrame.

Source code in timecopilot/models/utils/gluonts_forecaster.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
def forecast(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """Generate forecasts for time series data using the model.

    This method produces point forecasts and, optionally, prediction
    intervals or quantile forecasts. The input DataFrame can contain one
    or multiple time series in stacked (long) format.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.org/
            pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for
            valid values. If None, the frequency will be inferred from the data.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). If provided, the returned
            DataFrame will include lower and upper interval columns for
            each specified level.
        quantiles (list[float], optional):
            List of quantiles to forecast, expressed as floats between 0
            and 1. Should not be used simultaneously with `level`. When
            provided, the output DataFrame will contain additional columns
            named in the format "model-q-{percentile}", where {percentile}
            = 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing forecast results. Includes:

                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.

            For multi-series data, the output retains the same unique
            identifiers as the input DataFrame.
    """
    df = maybe_convert_col_to_float32(df, "y")
    freq = self._maybe_infer_freq(df, freq)
    qc = QuantileConverter(level=level, quantiles=quantiles)
    gluonts_dataset = PandasDataset.from_long_dataframe(
        df.copy(deep=False),
        target="y",
        item_id="unique_id",
        timestamp="ds",
        freq=fix_freq(freq),
    )
    with self.get_predictor(prediction_length=h) as predictor:
        fcsts = predictor.predict(
            gluonts_dataset,
            num_samples=self.num_samples,
        )
    fcst_df = self.gluonts_fcsts_to_df(
        fcsts,
        freq=freq,
        model_name=self.alias,
        quantiles=qc.quantiles,
    )
    if qc.quantiles is not None:
        fcst_df = qc.maybe_convert_quantiles_to_level(
            fcst_df,
            models=[self.alias],
        )

    return fcst_df

cross_validation

cross_validation(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Perform cross-validation on time series data.

This method splits the time series into multiple training and testing windows and generates forecasts for each window. It enables evaluating forecast accuracy over different historical periods. Supports point forecasts and, optionally, prediction intervals or quantile forecasts.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict in each window.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. Defaults to 1.

1
step_size int

Step size between the start of consecutive windows. If None, it defaults to h.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). When specified, the output DataFrame includes lower and upper interval columns for each level.

None
quantiles list[float]

Quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. If provided, additional columns named "model-q-{percentile}" will appear in the output, where {percentile} is 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- "cutoff" column to indicate which window each forecast
  belongs to.
- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.
Source code in timecopilot/models/utils/forecaster.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def cross_validation(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """
    Perform cross-validation on time series data.

    This method splits the time series into multiple training and testing
    windows and generates forecasts for each window. It enables evaluating
    forecast accuracy over different historical periods. Supports point
    forecasts and, optionally, prediction intervals or quantile forecasts.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict in
            each window.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate. Defaults to 1.
        step_size (int, optional):
            Step size between the start of consecutive windows. If None, it
            defaults to `h`.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). When specified, the output
            DataFrame includes lower and upper interval columns for each
            level.
        quantiles (list[float], optional):
            Quantiles to forecast, expressed as floats between 0 and 1.
            Should not be used simultaneously with `level`. If provided,
            additional columns named "model-q-{percentile}" will appear in
            the output, where {percentile} is 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - "cutoff" column to indicate which window each forecast
                  belongs to.
                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    # mlforecast cv code
    results = []
    sort_idxs = maybe_compute_sort_indices(df, "unique_id", "ds")
    if sort_idxs is not None:
        df = take_rows(df, sort_idxs)
    splits = backtest_splits(
        df,
        n_windows=n_windows,
        h=h,
        id_col="unique_id",
        time_col="ds",
        freq=pd.tseries.frequencies.to_offset(freq),
        step_size=h if step_size is None else step_size,
    )
    for _, (cutoffs, train, valid) in tqdm(enumerate(splits)):
        if len(valid.columns) > 3:
            raise NotImplementedError(
                "Cross validation with exogenous variables is not yet supported."
            )
        y_pred = self.forecast(
            df=train,
            h=h,
            freq=freq,
            level=level,
            quantiles=quantiles,
        )
        y_pred = join(y_pred, cutoffs, on="unique_id", how="left")
        result = join(
            valid[["unique_id", "ds", "y"]],
            y_pred,
            on=["unique_id", "ds"],
        )
        if result.shape[0] < valid.shape[0]:
            raise ValueError(
                "Cross validation result produced less results than expected. "
                "Please verify that the frequency parameter (freq) "
                "matches your series' "
                "and that there aren't any missing periods."
            )
        results.append(result)
    out = vertical_concat(results)
    out = drop_index_if_pandas(out)
    first_out_cols = ["unique_id", "ds", "cutoff", "y"]
    remaining_cols = [c for c in out.columns if c not in first_out_cols]
    fcst_cv_df = out[first_out_cols + remaining_cols]
    return fcst_cv_df

detect_anomalies

detect_anomalies(
    df: DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> DataFrame

Detect anomalies in time-series using a cross-validated z-score test.

This method uses rolling-origin cross-validation to (1) produce adjusted (out-of-sample) predictions and (2) estimate the standard deviation of forecast errors. It then computes a per-point z-score, flags values outside a two-sided prediction interval (with confidence level), and returns a DataFrame with results.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to detect anomalies.

required
h int

Forecast horizon specifying how many future steps to predict. In each cross validation window. If not provided, the seasonality of the data (inferred from the frequency) is used.

None
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. If not provided, the maximum number of windows (computed by the shortest time series) is used. If provided, the number of windows is the minimum between the maximum number of windows (computed by the shortest time series) and the number of windows provided.

None
level int | float

Confidence levels for z-score, expressed as percentages (e.g. 80, 95). Default is 99.

99

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- model column to indicate the model.
- lower prediction interval.
- upper prediction interval.
- anomaly column to indicate if the value is an anomaly.
    an anomaly is defined as a value that is outside of the
    prediction interval (True or False).
Source code in timecopilot/models/utils/forecaster.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
def detect_anomalies(
    self,
    df: pd.DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> pd.DataFrame:
    """
    Detect anomalies in time-series using a cross-validated z-score test.

    This method uses rolling-origin cross-validation to (1) produce
    adjusted (out-of-sample) predictions and (2) estimate the
    standard deviation of forecast errors. It then computes a per-point z-score,
    flags values outside a two-sided prediction interval (with confidence `level`),
    and returns a DataFrame with results.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to detect anomalies.
        h (int, optional):
            Forecast horizon specifying how many future steps to predict.
            In each cross validation window. If not provided, the seasonality
            of the data (inferred from the frequency) is used.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate.
            If not provided, the maximum number of windows
            (computed by the shortest time series) is used.
            If provided, the number of windows is the minimum
            between the maximum number of windows
            (computed by the shortest time series)
            and the number of windows provided.
        level (int | float):
            Confidence levels for z-score, expressed as
            percentages (e.g. 80, 95). Default is 99.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - model column to indicate the model.
                - lower prediction interval.
                - upper prediction interval.
                - anomaly column to indicate if the value is an anomaly.
                    an anomaly is defined as a value that is outside of the
                    prediction interval (True or False).
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    if h is None:
        h = self._maybe_get_seasonality(freq)
    min_series_length = df.groupby("unique_id").size().min()
    # we require at least one observation before the first forecast
    max_possible_windows = (min_series_length - 1) // h
    if n_windows is None:
        _n_windows = max_possible_windows
    else:
        _n_windows = min(n_windows, max_possible_windows)
    if _n_windows < 1:
        raise ValueError(
            f"Cannot perform anomaly detection: series too short. "
            f"Minimum series length required: {h + 1}, "
            f"actual minimum length: {min_series_length}"
        )
    cv_results = self.cross_validation(
        df=df,
        h=h,
        freq=freq,
        n_windows=_n_windows,
        step_size=h,  # this is the default but who knows, anxiety
    )
    cv_results["residuals"] = cv_results["y"] - cv_results[self.alias]
    residual_stats = (
        cv_results.groupby("unique_id")["residuals"].std().reset_index()
    )
    residual_stats.columns = ["unique_id", "residual_std"]
    cv_results = cv_results.merge(residual_stats, on="unique_id", how="left")
    cv_results["z_score"] = cv_results["residuals"] / cv_results["residual_std"]
    alpha = 1 - level / 100
    critical_z = stats.norm.ppf(1 - alpha / 2)
    an_col = f"{self.alias}-anomaly"
    cv_results[an_col] = np.abs(cv_results["z_score"]) > critical_z
    lo_col = f"{self.alias}-lo-{int(level)}"
    hi_col = f"{self.alias}-hi-{int(level)}"
    margin = critical_z * cv_results["residual_std"]
    cv_results[lo_col] = cv_results[self.alias] - margin
    cv_results[hi_col] = cv_results[self.alias] + margin
    output_cols = [
        "unique_id",
        "ds",
        "cutoff",
        "y",
        self.alias,
        lo_col,
        hi_col,
        an_col,
    ]
    result = cv_results[output_cols].copy()
    result = drop_index_if_pandas(result)
    return result

plot staticmethod

plot(
    df: DataFrame | None = None,
    forecasts_df: DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: Axes | ndarray | Figure | None = None,
)

Plot forecasts and insample values.

Parameters:

Name Type Description Default
df DataFrame

DataFrame with columns [unique_id, ds, y]. Defaults to None.

None
forecasts_df DataFrame

DataFrame with columns [unique_id, ds] and models. Defaults to None.

None
ids list[str]

Time Series to plot. If None, time series are selected randomly. Defaults to None.

None
plot_random bool

Select time series to plot randomly. Defaults to True.

True
max_ids int

Maximum number of ids to plot. Defaults to 8.

8
models list[str]

Models to plot. Defaults to None.

None
level list[float]

Prediction intervals to plot. Defaults to None.

None
max_insample_length int

Maximum number of train/insample observations to be plotted. Defaults to None.

None
plot_anomalies bool

Plot anomalies for each prediction interval. Defaults to False.

False
engine str

Library used to plot. 'plotly', 'plotly-resampler' or 'matplotlib'. Defaults to 'matplotlib'.

'matplotlib'
palette str

Name of the matplotlib colormap to use for the plots. If None, uses the current style. Defaults to None.

None
seed int

Seed used for the random number generator. Only used if plot_random is True. Defaults to 0.

None
resampler_kwargs dict

Keyword arguments to be passed to plotly-resampler constructor. For further custumization ("show_dash") call the method, store the plotting object and add the extra arguments to its show_dash method. Defaults to None.

None
ax matplotlib axes, array of matplotlib axes or plotly Figure

Object where plots will be added. Defaults to None.

None
Source code in timecopilot/models/utils/forecaster.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
@staticmethod
def plot(
    df: pd.DataFrame | None = None,
    forecasts_df: pd.DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: plt.Axes | np.ndarray | plotly.graph_objects.Figure | None = None,
):
    """Plot forecasts and insample values.

    Args:
        df (pd.DataFrame, optional): DataFrame with columns
            [`unique_id`, `ds`, `y`]. Defaults to None.
        forecasts_df (pd.DataFrame, optional): DataFrame with
            columns [`unique_id`, `ds`] and models. Defaults to None.
        ids (list[str], optional): Time Series to plot. If None, time series
            are selected randomly. Defaults to None.
        plot_random (bool, optional): Select time series to plot randomly.
            Defaults to True.
        max_ids (int, optional): Maximum number of ids to plot. Defaults to 8.
        models (list[str], optional): Models to plot. Defaults to None.
        level (list[float], optional): Prediction intervals to plot.
            Defaults to None.
        max_insample_length (int, optional): Maximum number of train/insample
            observations to be plotted. Defaults to None.
        plot_anomalies (bool, optional): Plot anomalies for each prediction
            interval. Defaults to False.
        engine (str, optional): Library used to plot. 'plotly', 'plotly-resampler'
            or 'matplotlib'. Defaults to 'matplotlib'.
        palette (str, optional): Name of the matplotlib colormap to use for the
            plots. If None, uses the current style. Defaults to None.
        seed (int, optional): Seed used for the random number generator. Only
            used if plot_random is True. Defaults to 0.
        resampler_kwargs (dict, optional): Keyword arguments to be passed to
            plotly-resampler constructor. For further custumization ("show_dash")
            call the method, store the plotting object and add the extra arguments
            to its `show_dash` method. Defaults to None.
        ax (matplotlib axes, array of matplotlib axes or plotly Figure, optional):
            Object where plots will be added. Defaults to None.
    """
    df = ensure_time_dtype(df, time_col="ds")
    if forecasts_df is not None:
        forecasts_df = ensure_time_dtype(forecasts_df, time_col="ds")
        if any("anomaly" in col for col in forecasts_df.columns):
            df = None
            models = [
                col.split("-")[0]
                for col in forecasts_df.columns
                if col.endswith("-anomaly")
            ]
            forecasts_df = ufp.drop_columns(
                forecasts_df,
                [f"{model}-anomaly" for model in models],
            )
            lv_cols = [
                c.replace(f"{model}-lo-", "")
                for model in models
                for c in forecasts_df.columns
                if f"{model}-lo-" in c
            ]
            level = [float(c) if "." in c else int(c) for c in lv_cols]
            level = list(set(level))
            plot_anomalies = True
    return plot_series(
        df=df,
        forecasts_df=forecasts_df,
        ids=ids,
        plot_random=plot_random,
        max_ids=max_ids,
        models=models,
        level=level,
        max_insample_length=max_insample_length,
        plot_anomalies=plot_anomalies,
        engine=engine,
        resampler_kwargs=resampler_kwargs,
        palette=palette,
        seed=seed,
        id_col="unique_id",
        time_col="ds",
        target_col="y",
        ax=ax,
    )

Sundial

Sundial(
    repo_id: str = "thuml/sundial-base-128m",
    num_samples: int = 100,
    context_length: int = 2880,
    batch_size: int = 1024,
    alias: str = "Sundial",
)

Bases: Forecaster

Sundial is a family of generative time series foundation models, pre-trained on TimeBench (10^12 time points). It uses the TimeFlow Loss to predict next-patch distributions, allowing Transformers to be trained without discrete tokenization and make non-deterministic predictions. The model supports both point and probabilistic zero-shot forecasting. See the official repo for more details.

Parameters:

Name Type Description Default
repo_id str

The Hugging Face Hub model ID or local path to load the Sundial model from. Examples include "thuml/sundial-base-128m". Defaults to "thuml/sundial-base-128m". See the full list of models at Hugging Face.

'thuml/sundial-base-128m'
num_samples int

Number of samples to generate for probabilistic forecasting. More samples provide better distribution estimates but increase computation time. Defaults to 100.

100
context_length int

Maximum context length (input window size) for the model. Controls how much history is used for each forecast. Defaults to 2,880. The model supports different lookback lengths.

2880
batch_size int

Batch size for inference. Defaults to 1,024. Adjust based on available memory and model size. Larger batch sizes can improve throughput but require more GPU memory.

1024
alias str

Name to use for the model in output DataFrames and logs. Defaults to "Sundial".

'Sundial'
Notes

Academic Reference:

Resources:

Technical Details:

  • The model is loaded onto the best available device (GPU if available, otherwise CPU).
  • The model weights are loaded with torch_dtype=torch.bfloat16 for efficiency on supported hardware.
  • The model is only available for Python < 3.13.
Source code in timecopilot/models/foundation/sundial.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
def __init__(
    self,
    repo_id: str = "thuml/sundial-base-128m",
    num_samples: int = 100,
    context_length: int = 2_880,
    batch_size: int = 1_024,
    alias: str = "Sundial",
):
    """
    Args:
        repo_id (str, optional): The Hugging Face Hub model ID or local path to
            load the Sundial model from. Examples include
            "thuml/sundial-base-128m". Defaults to "thuml/sundial-base-128m".
            See the full list of models at [Hugging Face](https://huggingface.co/
            thuml/sundial-base-128m).
        num_samples (int, optional): Number of samples to generate for
            probabilistic forecasting. More samples provide better distribution
            estimates but increase computation time. Defaults to 100.
        context_length (int, optional): Maximum context length (input window size)
            for the model. Controls how much history is used for each forecast.
            Defaults to 2,880. The model supports different lookback lengths.
        batch_size (int, optional): Batch size for inference. Defaults to 1,024.
            Adjust based on available memory and model size. Larger batch sizes
            can improve throughput but require more GPU memory.
        alias (str, optional): Name to use for the model in output DataFrames and
            logs. Defaults to "Sundial".

    Notes:
        **Academic Reference:**

        - Paper: [Sundial: A Family of Highly Capable Time Series Foundation Models](https://arxiv.org/abs/2502.00816)

        **Resources:**

        - GitHub: [thuml/Sundial](https://github.com/thuml/Sundial)
        - HuggingFace: [thuml/sundial-base-128m](https://huggingface.co/thuml/sundial-base-128m)

        **Technical Details:**

        - The model is loaded onto the best available device (GPU if
          available, otherwise CPU).
        - The model weights are loaded with torch_dtype=torch.bfloat16 for
          efficiency on supported hardware.
        - The model is only available for Python < 3.13.
    """
    self.repo_id = repo_id
    self.num_samples = num_samples
    self.context_length = context_length
    self.batch_size = batch_size
    self.alias = alias
    self.device = "cuda" if torch.cuda.is_available() else "cpu"
    self.dtype = torch.bfloat16

forecast

forecast(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Generate forecasts for time series data using the model.

This method produces point forecasts and, optionally, prediction intervals or quantile forecasts. The input DataFrame can contain one or multiple time series in stacked (long) format.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). If provided, the returned DataFrame will include lower and upper interval columns for each specified level.

None
quantiles list[float]

List of quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. When provided, the output DataFrame will contain additional columns named in the format "model-q-{percentile}", where {percentile} = 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing forecast results. Includes:

- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.

For multi-series data, the output retains the same unique identifiers as the input DataFrame.

Source code in timecopilot/models/foundation/sundial.py
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
def forecast(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """Generate forecasts for time series data using the model.

    This method produces point forecasts and, optionally, prediction
    intervals or quantile forecasts. The input DataFrame can contain one
    or multiple time series in stacked (long) format.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.org/
            pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for
            valid values. If not provided, the frequency will be inferred
            from the data.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). If provided, the returned
            DataFrame will include lower and upper interval columns for
            each specified level.
        quantiles (list[float], optional):
            List of quantiles to forecast, expressed as floats between 0
            and 1. Should not be used simultaneously with `level`. When
            provided, the output DataFrame will contain additional columns
            named in the format "model-q-{percentile}", where {percentile}
            = 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing forecast results. Includes:

                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.

            For multi-series data, the output retains the same unique
            identifiers as the input DataFrame.
    """
    freq = self._maybe_infer_freq(df, freq)
    qc = QuantileConverter(level=level, quantiles=quantiles)
    dataset = TimeSeriesDataset.from_df(df, batch_size=self.batch_size)
    fcst_df = dataset.make_future_dataframe(h=h, freq=freq)
    with self._get_model() as model:
        fcsts_mean_np, fcsts_quantiles_np = self._predict(
            model,
            dataset,
            h,
            quantiles=qc.quantiles,
        )
    fcst_df[self.alias] = fcsts_mean_np.reshape(-1, 1)
    if qc.quantiles is not None and fcsts_quantiles_np is not None:
        for i, q in enumerate(qc.quantiles):
            fcst_df[f"{self.alias}-q-{int(q * 100)}"] = fcsts_quantiles_np[
                ..., i
            ].reshape(-1, 1)
        fcst_df = qc.maybe_convert_quantiles_to_level(
            fcst_df,
            models=[self.alias],
        )
    return fcst_df

cross_validation

cross_validation(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Perform cross-validation on time series data.

This method splits the time series into multiple training and testing windows and generates forecasts for each window. It enables evaluating forecast accuracy over different historical periods. Supports point forecasts and, optionally, prediction intervals or quantile forecasts.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict in each window.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. Defaults to 1.

1
step_size int

Step size between the start of consecutive windows. If None, it defaults to h.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). When specified, the output DataFrame includes lower and upper interval columns for each level.

None
quantiles list[float]

Quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. If provided, additional columns named "model-q-{percentile}" will appear in the output, where {percentile} is 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- "cutoff" column to indicate which window each forecast
  belongs to.
- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.
Source code in timecopilot/models/utils/forecaster.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def cross_validation(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """
    Perform cross-validation on time series data.

    This method splits the time series into multiple training and testing
    windows and generates forecasts for each window. It enables evaluating
    forecast accuracy over different historical periods. Supports point
    forecasts and, optionally, prediction intervals or quantile forecasts.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict in
            each window.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate. Defaults to 1.
        step_size (int, optional):
            Step size between the start of consecutive windows. If None, it
            defaults to `h`.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). When specified, the output
            DataFrame includes lower and upper interval columns for each
            level.
        quantiles (list[float], optional):
            Quantiles to forecast, expressed as floats between 0 and 1.
            Should not be used simultaneously with `level`. If provided,
            additional columns named "model-q-{percentile}" will appear in
            the output, where {percentile} is 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - "cutoff" column to indicate which window each forecast
                  belongs to.
                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    # mlforecast cv code
    results = []
    sort_idxs = maybe_compute_sort_indices(df, "unique_id", "ds")
    if sort_idxs is not None:
        df = take_rows(df, sort_idxs)
    splits = backtest_splits(
        df,
        n_windows=n_windows,
        h=h,
        id_col="unique_id",
        time_col="ds",
        freq=pd.tseries.frequencies.to_offset(freq),
        step_size=h if step_size is None else step_size,
    )
    for _, (cutoffs, train, valid) in tqdm(enumerate(splits)):
        if len(valid.columns) > 3:
            raise NotImplementedError(
                "Cross validation with exogenous variables is not yet supported."
            )
        y_pred = self.forecast(
            df=train,
            h=h,
            freq=freq,
            level=level,
            quantiles=quantiles,
        )
        y_pred = join(y_pred, cutoffs, on="unique_id", how="left")
        result = join(
            valid[["unique_id", "ds", "y"]],
            y_pred,
            on=["unique_id", "ds"],
        )
        if result.shape[0] < valid.shape[0]:
            raise ValueError(
                "Cross validation result produced less results than expected. "
                "Please verify that the frequency parameter (freq) "
                "matches your series' "
                "and that there aren't any missing periods."
            )
        results.append(result)
    out = vertical_concat(results)
    out = drop_index_if_pandas(out)
    first_out_cols = ["unique_id", "ds", "cutoff", "y"]
    remaining_cols = [c for c in out.columns if c not in first_out_cols]
    fcst_cv_df = out[first_out_cols + remaining_cols]
    return fcst_cv_df

detect_anomalies

detect_anomalies(
    df: DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> DataFrame

Detect anomalies in time-series using a cross-validated z-score test.

This method uses rolling-origin cross-validation to (1) produce adjusted (out-of-sample) predictions and (2) estimate the standard deviation of forecast errors. It then computes a per-point z-score, flags values outside a two-sided prediction interval (with confidence level), and returns a DataFrame with results.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to detect anomalies.

required
h int

Forecast horizon specifying how many future steps to predict. In each cross validation window. If not provided, the seasonality of the data (inferred from the frequency) is used.

None
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. If not provided, the maximum number of windows (computed by the shortest time series) is used. If provided, the number of windows is the minimum between the maximum number of windows (computed by the shortest time series) and the number of windows provided.

None
level int | float

Confidence levels for z-score, expressed as percentages (e.g. 80, 95). Default is 99.

99

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- model column to indicate the model.
- lower prediction interval.
- upper prediction interval.
- anomaly column to indicate if the value is an anomaly.
    an anomaly is defined as a value that is outside of the
    prediction interval (True or False).
Source code in timecopilot/models/utils/forecaster.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
def detect_anomalies(
    self,
    df: pd.DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> pd.DataFrame:
    """
    Detect anomalies in time-series using a cross-validated z-score test.

    This method uses rolling-origin cross-validation to (1) produce
    adjusted (out-of-sample) predictions and (2) estimate the
    standard deviation of forecast errors. It then computes a per-point z-score,
    flags values outside a two-sided prediction interval (with confidence `level`),
    and returns a DataFrame with results.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to detect anomalies.
        h (int, optional):
            Forecast horizon specifying how many future steps to predict.
            In each cross validation window. If not provided, the seasonality
            of the data (inferred from the frequency) is used.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate.
            If not provided, the maximum number of windows
            (computed by the shortest time series) is used.
            If provided, the number of windows is the minimum
            between the maximum number of windows
            (computed by the shortest time series)
            and the number of windows provided.
        level (int | float):
            Confidence levels for z-score, expressed as
            percentages (e.g. 80, 95). Default is 99.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - model column to indicate the model.
                - lower prediction interval.
                - upper prediction interval.
                - anomaly column to indicate if the value is an anomaly.
                    an anomaly is defined as a value that is outside of the
                    prediction interval (True or False).
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    if h is None:
        h = self._maybe_get_seasonality(freq)
    min_series_length = df.groupby("unique_id").size().min()
    # we require at least one observation before the first forecast
    max_possible_windows = (min_series_length - 1) // h
    if n_windows is None:
        _n_windows = max_possible_windows
    else:
        _n_windows = min(n_windows, max_possible_windows)
    if _n_windows < 1:
        raise ValueError(
            f"Cannot perform anomaly detection: series too short. "
            f"Minimum series length required: {h + 1}, "
            f"actual minimum length: {min_series_length}"
        )
    cv_results = self.cross_validation(
        df=df,
        h=h,
        freq=freq,
        n_windows=_n_windows,
        step_size=h,  # this is the default but who knows, anxiety
    )
    cv_results["residuals"] = cv_results["y"] - cv_results[self.alias]
    residual_stats = (
        cv_results.groupby("unique_id")["residuals"].std().reset_index()
    )
    residual_stats.columns = ["unique_id", "residual_std"]
    cv_results = cv_results.merge(residual_stats, on="unique_id", how="left")
    cv_results["z_score"] = cv_results["residuals"] / cv_results["residual_std"]
    alpha = 1 - level / 100
    critical_z = stats.norm.ppf(1 - alpha / 2)
    an_col = f"{self.alias}-anomaly"
    cv_results[an_col] = np.abs(cv_results["z_score"]) > critical_z
    lo_col = f"{self.alias}-lo-{int(level)}"
    hi_col = f"{self.alias}-hi-{int(level)}"
    margin = critical_z * cv_results["residual_std"]
    cv_results[lo_col] = cv_results[self.alias] - margin
    cv_results[hi_col] = cv_results[self.alias] + margin
    output_cols = [
        "unique_id",
        "ds",
        "cutoff",
        "y",
        self.alias,
        lo_col,
        hi_col,
        an_col,
    ]
    result = cv_results[output_cols].copy()
    result = drop_index_if_pandas(result)
    return result

plot staticmethod

plot(
    df: DataFrame | None = None,
    forecasts_df: DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: Axes | ndarray | Figure | None = None,
)

Plot forecasts and insample values.

Parameters:

Name Type Description Default
df DataFrame

DataFrame with columns [unique_id, ds, y]. Defaults to None.

None
forecasts_df DataFrame

DataFrame with columns [unique_id, ds] and models. Defaults to None.

None
ids list[str]

Time Series to plot. If None, time series are selected randomly. Defaults to None.

None
plot_random bool

Select time series to plot randomly. Defaults to True.

True
max_ids int

Maximum number of ids to plot. Defaults to 8.

8
models list[str]

Models to plot. Defaults to None.

None
level list[float]

Prediction intervals to plot. Defaults to None.

None
max_insample_length int

Maximum number of train/insample observations to be plotted. Defaults to None.

None
plot_anomalies bool

Plot anomalies for each prediction interval. Defaults to False.

False
engine str

Library used to plot. 'plotly', 'plotly-resampler' or 'matplotlib'. Defaults to 'matplotlib'.

'matplotlib'
palette str

Name of the matplotlib colormap to use for the plots. If None, uses the current style. Defaults to None.

None
seed int

Seed used for the random number generator. Only used if plot_random is True. Defaults to 0.

None
resampler_kwargs dict

Keyword arguments to be passed to plotly-resampler constructor. For further custumization ("show_dash") call the method, store the plotting object and add the extra arguments to its show_dash method. Defaults to None.

None
ax matplotlib axes, array of matplotlib axes or plotly Figure

Object where plots will be added. Defaults to None.

None
Source code in timecopilot/models/utils/forecaster.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
@staticmethod
def plot(
    df: pd.DataFrame | None = None,
    forecasts_df: pd.DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: plt.Axes | np.ndarray | plotly.graph_objects.Figure | None = None,
):
    """Plot forecasts and insample values.

    Args:
        df (pd.DataFrame, optional): DataFrame with columns
            [`unique_id`, `ds`, `y`]. Defaults to None.
        forecasts_df (pd.DataFrame, optional): DataFrame with
            columns [`unique_id`, `ds`] and models. Defaults to None.
        ids (list[str], optional): Time Series to plot. If None, time series
            are selected randomly. Defaults to None.
        plot_random (bool, optional): Select time series to plot randomly.
            Defaults to True.
        max_ids (int, optional): Maximum number of ids to plot. Defaults to 8.
        models (list[str], optional): Models to plot. Defaults to None.
        level (list[float], optional): Prediction intervals to plot.
            Defaults to None.
        max_insample_length (int, optional): Maximum number of train/insample
            observations to be plotted. Defaults to None.
        plot_anomalies (bool, optional): Plot anomalies for each prediction
            interval. Defaults to False.
        engine (str, optional): Library used to plot. 'plotly', 'plotly-resampler'
            or 'matplotlib'. Defaults to 'matplotlib'.
        palette (str, optional): Name of the matplotlib colormap to use for the
            plots. If None, uses the current style. Defaults to None.
        seed (int, optional): Seed used for the random number generator. Only
            used if plot_random is True. Defaults to 0.
        resampler_kwargs (dict, optional): Keyword arguments to be passed to
            plotly-resampler constructor. For further custumization ("show_dash")
            call the method, store the plotting object and add the extra arguments
            to its `show_dash` method. Defaults to None.
        ax (matplotlib axes, array of matplotlib axes or plotly Figure, optional):
            Object where plots will be added. Defaults to None.
    """
    df = ensure_time_dtype(df, time_col="ds")
    if forecasts_df is not None:
        forecasts_df = ensure_time_dtype(forecasts_df, time_col="ds")
        if any("anomaly" in col for col in forecasts_df.columns):
            df = None
            models = [
                col.split("-")[0]
                for col in forecasts_df.columns
                if col.endswith("-anomaly")
            ]
            forecasts_df = ufp.drop_columns(
                forecasts_df,
                [f"{model}-anomaly" for model in models],
            )
            lv_cols = [
                c.replace(f"{model}-lo-", "")
                for model in models
                for c in forecasts_df.columns
                if f"{model}-lo-" in c
            ]
            level = [float(c) if "." in c else int(c) for c in lv_cols]
            level = list(set(level))
            plot_anomalies = True
    return plot_series(
        df=df,
        forecasts_df=forecasts_df,
        ids=ids,
        plot_random=plot_random,
        max_ids=max_ids,
        models=models,
        level=level,
        max_insample_length=max_insample_length,
        plot_anomalies=plot_anomalies,
        engine=engine,
        resampler_kwargs=resampler_kwargs,
        palette=palette,
        seed=seed,
        id_col="unique_id",
        time_col="ds",
        target_col="y",
        ax=ax,
    )

TabPFN

TabPFN(
    features: list[FeatureGenerator] | None = None,
    context_length: int = 4096,
    mode: TabPFNMode | None = None,
    api_key: str | None = None,
    alias: str = "TabPFN",
)

Bases: Forecaster

TabPFN is a zero-shot time series forecasting model that frames univariate forecasting as a tabular regression problem using TabPFNv2. It supports both point and probabilistic forecasts, and can incorporate exogenous variables via feature engineering. See the official repo for more details.

Parameters:

Name Type Description Default
features list[FeatureGenerator]

List of TabPFN-TS feature generators to use for feature engineering. If None, uses [RunningIndexFeature(), CalendarFeature(), AutoSeasonalFeature()] by default. See TabPFN-TS features.

None
context_length int

Maximum context length (input window size) for the model. Defaults to 4096. Controls how much history is used for each forecast.

4096
mode TabPFNMode

Inference mode for TabPFN. If None, uses LOCAL ("tabpfn-local") if a GPU is available, otherwise CLIENT (cloud inference via "tabpfn-client"). See TabPFN-TS docs for available modes.

None
api_key str

API key for tabpfn-client cloud inference. Required if using CLIENT mode and not already set in the environment.

None
alias str

Name to use for the model in output DataFrames and logs. Defaults to "TabPFN".

'TabPFN'
Notes

Academic Reference:

Resources:

Technical Details:

  • For LOCAL mode, a CUDA-capable GPU is recommended for best performance.
  • The model is only available for Python < 3.13.
Source code in timecopilot/models/foundation/tabpfn.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def __init__(
    self,
    features: list[FeatureGenerator] | None = None,
    context_length: int = 4096,
    mode: TabPFNMode | None = None,
    api_key: str | None = None,
    alias: str = "TabPFN",
):
    """
    Args:
        features (list[FeatureGenerator], optional): List of TabPFN-TS feature
            generators to use for feature engineering. If None, uses
            `[RunningIndexFeature(), CalendarFeature(), AutoSeasonalFeature()]`
            by default.
            See
            [TabPFN-TS features](https://github.com/PriorLabs/tabpfn-time-series/
            tree/main/tabpfn_time_series/features).
        context_length (int, optional): Maximum context length (input window size)
            for the model. Defaults to 4096. Controls how much history is used for
            each forecast.
        mode (TabPFNMode, optional): Inference mode for TabPFN. If None, uses LOCAL
            (`"tabpfn-local"`) if a GPU is available, otherwise CLIENT (cloud
            inference via `"tabpfn-client"`). See
            [TabPFN-TS docs](https://github.com/PriorLabs/tabpfn-time-series/
            blob/3cd61ad556466de837edd1c6036744176145c024/tabpfn_time_series/
            predictor.py#L11) for available modes.
        api_key (str, optional): API key for tabpfn-client cloud inference. Required
            if using CLIENT mode and not already set in the environment.
        alias (str, optional): Name to use for the model in output DataFrames and
            logs. Defaults to "TabPFN".

    Notes:
        **Academic Reference:**

        - Paper: [From Tables to Time: How TabPFN-v2 Outperforms
        Specialized Time Series Forecasting Models](https://arxiv.org/abs/2501.02945)

        **Resources:**

        - GitHub: [PriorLabs/tabpfn-time-series](https://github.com/PriorLabs/tabpfn-time-series)

        **Technical Details:**

        - For LOCAL mode, a CUDA-capable GPU is recommended for best performance.
        - The model is only available for Python < 3.13.
    """
    if features is None:
        features = [
            RunningIndexFeature(),
            CalendarFeature(),
            AutoSeasonalFeature(),
        ]
    self.feature_transformer = FeatureTransformer(features)
    self.context_length = context_length
    if mode is None:
        mode = TabPFNMode.LOCAL if torch.cuda.is_available() else TabPFNMode.CLIENT
    if mode == TabPFNMode.CLIENT and api_key is not None:
        set_access_token(api_key)
    self.mode = mode
    self.alias = alias

forecast

forecast(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Generate forecasts for time series data using the model.

This method produces point forecasts and, optionally, prediction intervals or quantile forecasts. The input DataFrame can contain one or multiple time series in stacked (long) format.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). If provided, the returned DataFrame will include lower and upper interval columns for each specified level.

None
quantiles list[float]

List of quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. When provided, the output DataFrame will contain additional columns named in the format "model-q-{percentile}", where {percentile} = 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing forecast results. Includes:

- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.

For multi-series data, the output retains the same unique identifiers as the input DataFrame.

Source code in timecopilot/models/foundation/tabpfn.py
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
def forecast(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """Generate forecasts for time series data using the model.

    This method produces point forecasts and, optionally, prediction
    intervals or quantile forecasts. The input DataFrame can contain one
    or multiple time series in stacked (long) format.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.org/
            pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for
            valid values. If not provided, the frequency will be inferred
            from the data.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). If provided, the returned
            DataFrame will include lower and upper interval columns for
            each specified level.
        quantiles (list[float], optional):
            List of quantiles to forecast, expressed as floats between 0
            and 1. Should not be used simultaneously with `level`. When
            provided, the output DataFrame will contain additional columns
            named in the format "model-q-{percentile}", where {percentile}
            = 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing forecast results. Includes:

                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.

            For multi-series data, the output retains the same unique
            identifiers as the input DataFrame.
    """
    freq = self._maybe_infer_freq(df, freq)
    qc = QuantileConverter(level=level, quantiles=quantiles)
    if qc.quantiles is not None and not np.allclose(
        qc.quantiles,
        TABPFN_TS_DEFAULT_QUANTILE_CONFIG,
    ):
        raise ValueError(
            "TabPFN only supports the default quantiles, "
            "please use the default quantiles or default level, "
        )
    with self._get_model() as model:
        fcst_df = self._forecast(
            model,
            df,
            h,
            quantiles=qc.quantiles,
        )
    if qc.quantiles is not None:
        fcst_df = qc.maybe_convert_quantiles_to_level(
            fcst_df,
            models=[self.alias],
        )
    return fcst_df

cross_validation

cross_validation(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Perform cross-validation on time series data.

This method splits the time series into multiple training and testing windows and generates forecasts for each window. It enables evaluating forecast accuracy over different historical periods. Supports point forecasts and, optionally, prediction intervals or quantile forecasts.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict in each window.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. Defaults to 1.

1
step_size int

Step size between the start of consecutive windows. If None, it defaults to h.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). When specified, the output DataFrame includes lower and upper interval columns for each level.

None
quantiles list[float]

Quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. If provided, additional columns named "model-q-{percentile}" will appear in the output, where {percentile} is 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- "cutoff" column to indicate which window each forecast
  belongs to.
- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.
Source code in timecopilot/models/utils/forecaster.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def cross_validation(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """
    Perform cross-validation on time series data.

    This method splits the time series into multiple training and testing
    windows and generates forecasts for each window. It enables evaluating
    forecast accuracy over different historical periods. Supports point
    forecasts and, optionally, prediction intervals or quantile forecasts.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict in
            each window.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate. Defaults to 1.
        step_size (int, optional):
            Step size between the start of consecutive windows. If None, it
            defaults to `h`.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). When specified, the output
            DataFrame includes lower and upper interval columns for each
            level.
        quantiles (list[float], optional):
            Quantiles to forecast, expressed as floats between 0 and 1.
            Should not be used simultaneously with `level`. If provided,
            additional columns named "model-q-{percentile}" will appear in
            the output, where {percentile} is 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - "cutoff" column to indicate which window each forecast
                  belongs to.
                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    # mlforecast cv code
    results = []
    sort_idxs = maybe_compute_sort_indices(df, "unique_id", "ds")
    if sort_idxs is not None:
        df = take_rows(df, sort_idxs)
    splits = backtest_splits(
        df,
        n_windows=n_windows,
        h=h,
        id_col="unique_id",
        time_col="ds",
        freq=pd.tseries.frequencies.to_offset(freq),
        step_size=h if step_size is None else step_size,
    )
    for _, (cutoffs, train, valid) in tqdm(enumerate(splits)):
        if len(valid.columns) > 3:
            raise NotImplementedError(
                "Cross validation with exogenous variables is not yet supported."
            )
        y_pred = self.forecast(
            df=train,
            h=h,
            freq=freq,
            level=level,
            quantiles=quantiles,
        )
        y_pred = join(y_pred, cutoffs, on="unique_id", how="left")
        result = join(
            valid[["unique_id", "ds", "y"]],
            y_pred,
            on=["unique_id", "ds"],
        )
        if result.shape[0] < valid.shape[0]:
            raise ValueError(
                "Cross validation result produced less results than expected. "
                "Please verify that the frequency parameter (freq) "
                "matches your series' "
                "and that there aren't any missing periods."
            )
        results.append(result)
    out = vertical_concat(results)
    out = drop_index_if_pandas(out)
    first_out_cols = ["unique_id", "ds", "cutoff", "y"]
    remaining_cols = [c for c in out.columns if c not in first_out_cols]
    fcst_cv_df = out[first_out_cols + remaining_cols]
    return fcst_cv_df

detect_anomalies

detect_anomalies(
    df: DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> DataFrame

Detect anomalies in time-series using a cross-validated z-score test.

This method uses rolling-origin cross-validation to (1) produce adjusted (out-of-sample) predictions and (2) estimate the standard deviation of forecast errors. It then computes a per-point z-score, flags values outside a two-sided prediction interval (with confidence level), and returns a DataFrame with results.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to detect anomalies.

required
h int

Forecast horizon specifying how many future steps to predict. In each cross validation window. If not provided, the seasonality of the data (inferred from the frequency) is used.

None
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. If not provided, the maximum number of windows (computed by the shortest time series) is used. If provided, the number of windows is the minimum between the maximum number of windows (computed by the shortest time series) and the number of windows provided.

None
level int | float

Confidence levels for z-score, expressed as percentages (e.g. 80, 95). Default is 99.

99

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- model column to indicate the model.
- lower prediction interval.
- upper prediction interval.
- anomaly column to indicate if the value is an anomaly.
    an anomaly is defined as a value that is outside of the
    prediction interval (True or False).
Source code in timecopilot/models/utils/forecaster.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
def detect_anomalies(
    self,
    df: pd.DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> pd.DataFrame:
    """
    Detect anomalies in time-series using a cross-validated z-score test.

    This method uses rolling-origin cross-validation to (1) produce
    adjusted (out-of-sample) predictions and (2) estimate the
    standard deviation of forecast errors. It then computes a per-point z-score,
    flags values outside a two-sided prediction interval (with confidence `level`),
    and returns a DataFrame with results.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to detect anomalies.
        h (int, optional):
            Forecast horizon specifying how many future steps to predict.
            In each cross validation window. If not provided, the seasonality
            of the data (inferred from the frequency) is used.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate.
            If not provided, the maximum number of windows
            (computed by the shortest time series) is used.
            If provided, the number of windows is the minimum
            between the maximum number of windows
            (computed by the shortest time series)
            and the number of windows provided.
        level (int | float):
            Confidence levels for z-score, expressed as
            percentages (e.g. 80, 95). Default is 99.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - model column to indicate the model.
                - lower prediction interval.
                - upper prediction interval.
                - anomaly column to indicate if the value is an anomaly.
                    an anomaly is defined as a value that is outside of the
                    prediction interval (True or False).
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    if h is None:
        h = self._maybe_get_seasonality(freq)
    min_series_length = df.groupby("unique_id").size().min()
    # we require at least one observation before the first forecast
    max_possible_windows = (min_series_length - 1) // h
    if n_windows is None:
        _n_windows = max_possible_windows
    else:
        _n_windows = min(n_windows, max_possible_windows)
    if _n_windows < 1:
        raise ValueError(
            f"Cannot perform anomaly detection: series too short. "
            f"Minimum series length required: {h + 1}, "
            f"actual minimum length: {min_series_length}"
        )
    cv_results = self.cross_validation(
        df=df,
        h=h,
        freq=freq,
        n_windows=_n_windows,
        step_size=h,  # this is the default but who knows, anxiety
    )
    cv_results["residuals"] = cv_results["y"] - cv_results[self.alias]
    residual_stats = (
        cv_results.groupby("unique_id")["residuals"].std().reset_index()
    )
    residual_stats.columns = ["unique_id", "residual_std"]
    cv_results = cv_results.merge(residual_stats, on="unique_id", how="left")
    cv_results["z_score"] = cv_results["residuals"] / cv_results["residual_std"]
    alpha = 1 - level / 100
    critical_z = stats.norm.ppf(1 - alpha / 2)
    an_col = f"{self.alias}-anomaly"
    cv_results[an_col] = np.abs(cv_results["z_score"]) > critical_z
    lo_col = f"{self.alias}-lo-{int(level)}"
    hi_col = f"{self.alias}-hi-{int(level)}"
    margin = critical_z * cv_results["residual_std"]
    cv_results[lo_col] = cv_results[self.alias] - margin
    cv_results[hi_col] = cv_results[self.alias] + margin
    output_cols = [
        "unique_id",
        "ds",
        "cutoff",
        "y",
        self.alias,
        lo_col,
        hi_col,
        an_col,
    ]
    result = cv_results[output_cols].copy()
    result = drop_index_if_pandas(result)
    return result

plot staticmethod

plot(
    df: DataFrame | None = None,
    forecasts_df: DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: Axes | ndarray | Figure | None = None,
)

Plot forecasts and insample values.

Parameters:

Name Type Description Default
df DataFrame

DataFrame with columns [unique_id, ds, y]. Defaults to None.

None
forecasts_df DataFrame

DataFrame with columns [unique_id, ds] and models. Defaults to None.

None
ids list[str]

Time Series to plot. If None, time series are selected randomly. Defaults to None.

None
plot_random bool

Select time series to plot randomly. Defaults to True.

True
max_ids int

Maximum number of ids to plot. Defaults to 8.

8
models list[str]

Models to plot. Defaults to None.

None
level list[float]

Prediction intervals to plot. Defaults to None.

None
max_insample_length int

Maximum number of train/insample observations to be plotted. Defaults to None.

None
plot_anomalies bool

Plot anomalies for each prediction interval. Defaults to False.

False
engine str

Library used to plot. 'plotly', 'plotly-resampler' or 'matplotlib'. Defaults to 'matplotlib'.

'matplotlib'
palette str

Name of the matplotlib colormap to use for the plots. If None, uses the current style. Defaults to None.

None
seed int

Seed used for the random number generator. Only used if plot_random is True. Defaults to 0.

None
resampler_kwargs dict

Keyword arguments to be passed to plotly-resampler constructor. For further custumization ("show_dash") call the method, store the plotting object and add the extra arguments to its show_dash method. Defaults to None.

None
ax matplotlib axes, array of matplotlib axes or plotly Figure

Object where plots will be added. Defaults to None.

None
Source code in timecopilot/models/utils/forecaster.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
@staticmethod
def plot(
    df: pd.DataFrame | None = None,
    forecasts_df: pd.DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: plt.Axes | np.ndarray | plotly.graph_objects.Figure | None = None,
):
    """Plot forecasts and insample values.

    Args:
        df (pd.DataFrame, optional): DataFrame with columns
            [`unique_id`, `ds`, `y`]. Defaults to None.
        forecasts_df (pd.DataFrame, optional): DataFrame with
            columns [`unique_id`, `ds`] and models. Defaults to None.
        ids (list[str], optional): Time Series to plot. If None, time series
            are selected randomly. Defaults to None.
        plot_random (bool, optional): Select time series to plot randomly.
            Defaults to True.
        max_ids (int, optional): Maximum number of ids to plot. Defaults to 8.
        models (list[str], optional): Models to plot. Defaults to None.
        level (list[float], optional): Prediction intervals to plot.
            Defaults to None.
        max_insample_length (int, optional): Maximum number of train/insample
            observations to be plotted. Defaults to None.
        plot_anomalies (bool, optional): Plot anomalies for each prediction
            interval. Defaults to False.
        engine (str, optional): Library used to plot. 'plotly', 'plotly-resampler'
            or 'matplotlib'. Defaults to 'matplotlib'.
        palette (str, optional): Name of the matplotlib colormap to use for the
            plots. If None, uses the current style. Defaults to None.
        seed (int, optional): Seed used for the random number generator. Only
            used if plot_random is True. Defaults to 0.
        resampler_kwargs (dict, optional): Keyword arguments to be passed to
            plotly-resampler constructor. For further custumization ("show_dash")
            call the method, store the plotting object and add the extra arguments
            to its `show_dash` method. Defaults to None.
        ax (matplotlib axes, array of matplotlib axes or plotly Figure, optional):
            Object where plots will be added. Defaults to None.
    """
    df = ensure_time_dtype(df, time_col="ds")
    if forecasts_df is not None:
        forecasts_df = ensure_time_dtype(forecasts_df, time_col="ds")
        if any("anomaly" in col for col in forecasts_df.columns):
            df = None
            models = [
                col.split("-")[0]
                for col in forecasts_df.columns
                if col.endswith("-anomaly")
            ]
            forecasts_df = ufp.drop_columns(
                forecasts_df,
                [f"{model}-anomaly" for model in models],
            )
            lv_cols = [
                c.replace(f"{model}-lo-", "")
                for model in models
                for c in forecasts_df.columns
                if f"{model}-lo-" in c
            ]
            level = [float(c) if "." in c else int(c) for c in lv_cols]
            level = list(set(level))
            plot_anomalies = True
    return plot_series(
        df=df,
        forecasts_df=forecasts_df,
        ids=ids,
        plot_random=plot_random,
        max_ids=max_ids,
        models=models,
        level=level,
        max_insample_length=max_insample_length,
        plot_anomalies=plot_anomalies,
        engine=engine,
        resampler_kwargs=resampler_kwargs,
        palette=palette,
        seed=seed,
        id_col="unique_id",
        time_col="ds",
        target_col="y",
        ax=ax,
    )

TimeGPT

TimeGPT(
    api_key: str | None = None,
    base_url: str | None = None,
    max_retries: int = 1,
    model: str = "timegpt-1",
    alias: str = "TimeGPT",
)

Bases: Forecaster

TimeGPT is a pre-trained foundation model for time series forecasting and anomaly detection, developed by Nixtla. It is based on a large encoder-decoder transformer architecture trained on over 100 billion data points from diverse domains. See the official repo, docs, and arXiv:2310.03589 for more details.

Parameters:

Name Type Description Default
api_key str

API key for authenticating with the Nixtla TimeGPT API. If not provided, will use the NIXTLA_API_KEY environment variable.

None
base_url str

Base URL for the TimeGPT API. Defaults to the official Nixtla endpoint.

None
max_retries int

Maximum number of retries for API requests. Defaults to 1.

1
model str

Model name or version to use. Defaults to "timegpt-1". See the Nixtla docs for available models.

'timegpt-1'
alias str

Name to use for the model in output DataFrames and logs. Defaults to "TimeGPT".

'TimeGPT'
Notes

Academic Reference:

Resources:

Technical Details:

  • TimeGPT is a foundation model for time series forecasting designed for production-ready forecasting with minimal setup.
  • Provides zero-shot forecasting capabilities across various domains and frequencies.
  • Requires a valid API key from Nixtla to use.
  • For more information, see the TimeGPT documentation.
Source code in timecopilot/models/foundation/timegpt.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
def __init__(
    self,
    api_key: str | None = None,
    base_url: str | None = None,
    max_retries: int = 1,
    model: str = "timegpt-1",
    alias: str = "TimeGPT",
):
    """
    Args:
        api_key (str, optional): API key for authenticating with the Nixtla TimeGPT
            API. If not provided, will use the `NIXTLA_API_KEY`
            environment variable.
        base_url (str, optional): Base URL for the TimeGPT API. Defaults to the
            official Nixtla endpoint.
        max_retries (int, optional): Maximum number of retries for API requests.
            Defaults to 1.
        model (str, optional): Model name or version to use. Defaults to
            "timegpt-1". See the [Nixtla docs](https://www.nixtla.io/docs) for
            available models.
        alias (str, optional): Name to use for the model in output DataFrames and
            logs. Defaults to "TimeGPT".

    Notes:
        **Academic Reference:**

        - Paper: [TimeGPT-1](https://arxiv.org/abs/2310.03589)

        **Resources:**

        - GitHub: [Nixtla/nixtla](https://github.com/Nixtla/nixtla)

        **Technical Details:**

        - TimeGPT is a foundation model for time series forecasting designed for
          production-ready forecasting with minimal setup.
        - Provides zero-shot forecasting capabilities across various
          domains and frequencies.
        - Requires a valid API key from Nixtla to use.
        - For more information, see the
          [TimeGPT documentation](https://www.nixtla.io/docs).
    """
    self.api_key = api_key
    self.base_url = base_url
    self.max_retries = max_retries
    self.model = model
    self.alias = alias

forecast

forecast(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Generate forecasts for time series data using the model.

This method produces point forecasts and, optionally, prediction intervals or quantile forecasts. The input DataFrame can contain one or multiple time series in stacked (long) format.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). If provided, the returned DataFrame will include lower and upper interval columns for each specified level.

None
quantiles list[float]

List of quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. When provided, the output DataFrame will contain additional columns named in the format "model-q-{percentile}", where {percentile} = 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing forecast results. Includes:

- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.

For multi-series data, the output retains the same unique identifiers as the input DataFrame.

Source code in timecopilot/models/foundation/timegpt.py
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def forecast(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """Generate forecasts for time series data using the model.

    This method produces point forecasts and, optionally, prediction
    intervals or quantile forecasts. The input DataFrame can contain one
    or multiple time series in stacked (long) format.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.org/
            pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for
            valid values. If not provided, the frequency will be inferred
            from the data.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). If provided, the returned
            DataFrame will include lower and upper interval columns for
            each specified level.
        quantiles (list[float], optional):
            List of quantiles to forecast, expressed as floats between 0
            and 1. Should not be used simultaneously with `level`. When
            provided, the output DataFrame will contain additional columns
            named in the format "model-q-{percentile}", where {percentile}
            = 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing forecast results. Includes:

                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.

            For multi-series data, the output retains the same unique
            identifiers as the input DataFrame.
    """
    freq = self._maybe_infer_freq(df, freq)
    client = self._get_client()
    fcst_df = client.forecast(
        df=df,
        h=h,
        freq=freq,
        model=self.model,
        level=level,
        quantiles=quantiles,
    )
    fcst_df["ds"] = pd.to_datetime(fcst_df["ds"])
    cols = [col.replace("TimeGPT", self.alias) for col in fcst_df.columns]
    fcst_df.columns = cols
    return fcst_df

cross_validation

cross_validation(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Perform cross-validation on time series data.

This method splits the time series into multiple training and testing windows and generates forecasts for each window. It enables evaluating forecast accuracy over different historical periods. Supports point forecasts and, optionally, prediction intervals or quantile forecasts.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict in each window.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. Defaults to 1.

1
step_size int

Step size between the start of consecutive windows. If None, it defaults to h.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). When specified, the output DataFrame includes lower and upper interval columns for each level.

None
quantiles list[float]

Quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. If provided, additional columns named "model-q-{percentile}" will appear in the output, where {percentile} is 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- "cutoff" column to indicate which window each forecast
  belongs to.
- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.
Source code in timecopilot/models/utils/forecaster.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def cross_validation(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """
    Perform cross-validation on time series data.

    This method splits the time series into multiple training and testing
    windows and generates forecasts for each window. It enables evaluating
    forecast accuracy over different historical periods. Supports point
    forecasts and, optionally, prediction intervals or quantile forecasts.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict in
            each window.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate. Defaults to 1.
        step_size (int, optional):
            Step size between the start of consecutive windows. If None, it
            defaults to `h`.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). When specified, the output
            DataFrame includes lower and upper interval columns for each
            level.
        quantiles (list[float], optional):
            Quantiles to forecast, expressed as floats between 0 and 1.
            Should not be used simultaneously with `level`. If provided,
            additional columns named "model-q-{percentile}" will appear in
            the output, where {percentile} is 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - "cutoff" column to indicate which window each forecast
                  belongs to.
                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    # mlforecast cv code
    results = []
    sort_idxs = maybe_compute_sort_indices(df, "unique_id", "ds")
    if sort_idxs is not None:
        df = take_rows(df, sort_idxs)
    splits = backtest_splits(
        df,
        n_windows=n_windows,
        h=h,
        id_col="unique_id",
        time_col="ds",
        freq=pd.tseries.frequencies.to_offset(freq),
        step_size=h if step_size is None else step_size,
    )
    for _, (cutoffs, train, valid) in tqdm(enumerate(splits)):
        if len(valid.columns) > 3:
            raise NotImplementedError(
                "Cross validation with exogenous variables is not yet supported."
            )
        y_pred = self.forecast(
            df=train,
            h=h,
            freq=freq,
            level=level,
            quantiles=quantiles,
        )
        y_pred = join(y_pred, cutoffs, on="unique_id", how="left")
        result = join(
            valid[["unique_id", "ds", "y"]],
            y_pred,
            on=["unique_id", "ds"],
        )
        if result.shape[0] < valid.shape[0]:
            raise ValueError(
                "Cross validation result produced less results than expected. "
                "Please verify that the frequency parameter (freq) "
                "matches your series' "
                "and that there aren't any missing periods."
            )
        results.append(result)
    out = vertical_concat(results)
    out = drop_index_if_pandas(out)
    first_out_cols = ["unique_id", "ds", "cutoff", "y"]
    remaining_cols = [c for c in out.columns if c not in first_out_cols]
    fcst_cv_df = out[first_out_cols + remaining_cols]
    return fcst_cv_df

detect_anomalies

detect_anomalies(
    df: DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> DataFrame

Detect anomalies in time-series using a cross-validated z-score test.

This method uses rolling-origin cross-validation to (1) produce adjusted (out-of-sample) predictions and (2) estimate the standard deviation of forecast errors. It then computes a per-point z-score, flags values outside a two-sided prediction interval (with confidence level), and returns a DataFrame with results.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to detect anomalies.

required
h int

Forecast horizon specifying how many future steps to predict. In each cross validation window. If not provided, the seasonality of the data (inferred from the frequency) is used.

None
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. If not provided, the maximum number of windows (computed by the shortest time series) is used. If provided, the number of windows is the minimum between the maximum number of windows (computed by the shortest time series) and the number of windows provided.

None
level int | float

Confidence levels for z-score, expressed as percentages (e.g. 80, 95). Default is 99.

99

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- model column to indicate the model.
- lower prediction interval.
- upper prediction interval.
- anomaly column to indicate if the value is an anomaly.
    an anomaly is defined as a value that is outside of the
    prediction interval (True or False).
Source code in timecopilot/models/utils/forecaster.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
def detect_anomalies(
    self,
    df: pd.DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> pd.DataFrame:
    """
    Detect anomalies in time-series using a cross-validated z-score test.

    This method uses rolling-origin cross-validation to (1) produce
    adjusted (out-of-sample) predictions and (2) estimate the
    standard deviation of forecast errors. It then computes a per-point z-score,
    flags values outside a two-sided prediction interval (with confidence `level`),
    and returns a DataFrame with results.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to detect anomalies.
        h (int, optional):
            Forecast horizon specifying how many future steps to predict.
            In each cross validation window. If not provided, the seasonality
            of the data (inferred from the frequency) is used.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate.
            If not provided, the maximum number of windows
            (computed by the shortest time series) is used.
            If provided, the number of windows is the minimum
            between the maximum number of windows
            (computed by the shortest time series)
            and the number of windows provided.
        level (int | float):
            Confidence levels for z-score, expressed as
            percentages (e.g. 80, 95). Default is 99.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - model column to indicate the model.
                - lower prediction interval.
                - upper prediction interval.
                - anomaly column to indicate if the value is an anomaly.
                    an anomaly is defined as a value that is outside of the
                    prediction interval (True or False).
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    if h is None:
        h = self._maybe_get_seasonality(freq)
    min_series_length = df.groupby("unique_id").size().min()
    # we require at least one observation before the first forecast
    max_possible_windows = (min_series_length - 1) // h
    if n_windows is None:
        _n_windows = max_possible_windows
    else:
        _n_windows = min(n_windows, max_possible_windows)
    if _n_windows < 1:
        raise ValueError(
            f"Cannot perform anomaly detection: series too short. "
            f"Minimum series length required: {h + 1}, "
            f"actual minimum length: {min_series_length}"
        )
    cv_results = self.cross_validation(
        df=df,
        h=h,
        freq=freq,
        n_windows=_n_windows,
        step_size=h,  # this is the default but who knows, anxiety
    )
    cv_results["residuals"] = cv_results["y"] - cv_results[self.alias]
    residual_stats = (
        cv_results.groupby("unique_id")["residuals"].std().reset_index()
    )
    residual_stats.columns = ["unique_id", "residual_std"]
    cv_results = cv_results.merge(residual_stats, on="unique_id", how="left")
    cv_results["z_score"] = cv_results["residuals"] / cv_results["residual_std"]
    alpha = 1 - level / 100
    critical_z = stats.norm.ppf(1 - alpha / 2)
    an_col = f"{self.alias}-anomaly"
    cv_results[an_col] = np.abs(cv_results["z_score"]) > critical_z
    lo_col = f"{self.alias}-lo-{int(level)}"
    hi_col = f"{self.alias}-hi-{int(level)}"
    margin = critical_z * cv_results["residual_std"]
    cv_results[lo_col] = cv_results[self.alias] - margin
    cv_results[hi_col] = cv_results[self.alias] + margin
    output_cols = [
        "unique_id",
        "ds",
        "cutoff",
        "y",
        self.alias,
        lo_col,
        hi_col,
        an_col,
    ]
    result = cv_results[output_cols].copy()
    result = drop_index_if_pandas(result)
    return result

plot staticmethod

plot(
    df: DataFrame | None = None,
    forecasts_df: DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: Axes | ndarray | Figure | None = None,
)

Plot forecasts and insample values.

Parameters:

Name Type Description Default
df DataFrame

DataFrame with columns [unique_id, ds, y]. Defaults to None.

None
forecasts_df DataFrame

DataFrame with columns [unique_id, ds] and models. Defaults to None.

None
ids list[str]

Time Series to plot. If None, time series are selected randomly. Defaults to None.

None
plot_random bool

Select time series to plot randomly. Defaults to True.

True
max_ids int

Maximum number of ids to plot. Defaults to 8.

8
models list[str]

Models to plot. Defaults to None.

None
level list[float]

Prediction intervals to plot. Defaults to None.

None
max_insample_length int

Maximum number of train/insample observations to be plotted. Defaults to None.

None
plot_anomalies bool

Plot anomalies for each prediction interval. Defaults to False.

False
engine str

Library used to plot. 'plotly', 'plotly-resampler' or 'matplotlib'. Defaults to 'matplotlib'.

'matplotlib'
palette str

Name of the matplotlib colormap to use for the plots. If None, uses the current style. Defaults to None.

None
seed int

Seed used for the random number generator. Only used if plot_random is True. Defaults to 0.

None
resampler_kwargs dict

Keyword arguments to be passed to plotly-resampler constructor. For further custumization ("show_dash") call the method, store the plotting object and add the extra arguments to its show_dash method. Defaults to None.

None
ax matplotlib axes, array of matplotlib axes or plotly Figure

Object where plots will be added. Defaults to None.

None
Source code in timecopilot/models/utils/forecaster.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
@staticmethod
def plot(
    df: pd.DataFrame | None = None,
    forecasts_df: pd.DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: plt.Axes | np.ndarray | plotly.graph_objects.Figure | None = None,
):
    """Plot forecasts and insample values.

    Args:
        df (pd.DataFrame, optional): DataFrame with columns
            [`unique_id`, `ds`, `y`]. Defaults to None.
        forecasts_df (pd.DataFrame, optional): DataFrame with
            columns [`unique_id`, `ds`] and models. Defaults to None.
        ids (list[str], optional): Time Series to plot. If None, time series
            are selected randomly. Defaults to None.
        plot_random (bool, optional): Select time series to plot randomly.
            Defaults to True.
        max_ids (int, optional): Maximum number of ids to plot. Defaults to 8.
        models (list[str], optional): Models to plot. Defaults to None.
        level (list[float], optional): Prediction intervals to plot.
            Defaults to None.
        max_insample_length (int, optional): Maximum number of train/insample
            observations to be plotted. Defaults to None.
        plot_anomalies (bool, optional): Plot anomalies for each prediction
            interval. Defaults to False.
        engine (str, optional): Library used to plot. 'plotly', 'plotly-resampler'
            or 'matplotlib'. Defaults to 'matplotlib'.
        palette (str, optional): Name of the matplotlib colormap to use for the
            plots. If None, uses the current style. Defaults to None.
        seed (int, optional): Seed used for the random number generator. Only
            used if plot_random is True. Defaults to 0.
        resampler_kwargs (dict, optional): Keyword arguments to be passed to
            plotly-resampler constructor. For further custumization ("show_dash")
            call the method, store the plotting object and add the extra arguments
            to its `show_dash` method. Defaults to None.
        ax (matplotlib axes, array of matplotlib axes or plotly Figure, optional):
            Object where plots will be added. Defaults to None.
    """
    df = ensure_time_dtype(df, time_col="ds")
    if forecasts_df is not None:
        forecasts_df = ensure_time_dtype(forecasts_df, time_col="ds")
        if any("anomaly" in col for col in forecasts_df.columns):
            df = None
            models = [
                col.split("-")[0]
                for col in forecasts_df.columns
                if col.endswith("-anomaly")
            ]
            forecasts_df = ufp.drop_columns(
                forecasts_df,
                [f"{model}-anomaly" for model in models],
            )
            lv_cols = [
                c.replace(f"{model}-lo-", "")
                for model in models
                for c in forecasts_df.columns
                if f"{model}-lo-" in c
            ]
            level = [float(c) if "." in c else int(c) for c in lv_cols]
            level = list(set(level))
            plot_anomalies = True
    return plot_series(
        df=df,
        forecasts_df=forecasts_df,
        ids=ids,
        plot_random=plot_random,
        max_ids=max_ids,
        models=models,
        level=level,
        max_insample_length=max_insample_length,
        plot_anomalies=plot_anomalies,
        engine=engine,
        resampler_kwargs=resampler_kwargs,
        palette=palette,
        seed=seed,
        id_col="unique_id",
        time_col="ds",
        target_col="y",
        ax=ax,
    )

TimesFM

TimesFM(
    repo_id: str = "google/timesfm-2.0-500m-pytorch",
    context_length: int = 2048,
    batch_size: int = 64,
    alias: str = "TimesFM",
    kwargs: dict | None = None,
)

Bases: Forecaster

TimesFM is a large time series model for time series forecasting, supporting both probabilistic and point forecasts. See the official repo for more details.

Parameters:

Name Type Description Default
repo_id str

The Hugging Face Hub model ID or local path to load the TimesFM model from. Examples include google/timesfm-2.0-500m-pytorch. Defaults to google/timesfm-2.0-500m-pytorch. See the full list of models at Hugging Face. Supported models:

  • google/timesfm-1.0-200m-pytorch
  • google/timesfm-2.0-500m-pytorch
  • google/timesfm-2.5-200m-pytorch
'google/timesfm-2.0-500m-pytorch'
context_length int

Maximum context length (input window size) for the model. Defaults to 2048. For TimesFM 2.0 models, max is 2048 (must be a multiple of 32). For TimesFM 1.0 models, max is 512. See TimesFM docs for details.

2048
batch_size int

Batch size for inference. Defaults to 64. Adjust based on available memory and model size.

64
alias str

Name to use for the model in output DataFrames and logs. Defaults to TimesFM.

'TimesFM'
kwargs dict

Additional keyword arguments to pass to the model. Defaults to None. Only used for TimesFM 2.5 models.

None
Notes

Academic Reference:

Resources:

Technical Details:

  • Only PyTorch checkpoints are currently supported. JAX is not supported.
  • The model is loaded onto the best available device (GPU if available, otherwise CPU).

Supported Models:

  • google/timesfm-1.0-200m-pytorch
  • google/timesfm-2.0-500m-pytorch
  • google/timesfm-2.5-200m-pytorch
Source code in timecopilot/models/foundation/timesfm.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
def __init__(
    self,
    repo_id: str = "google/timesfm-2.0-500m-pytorch",
    context_length: int = 2048,
    batch_size: int = 64,
    alias: str = "TimesFM",
    kwargs: dict | None = None,
):
    """
    Args:
        repo_id (str, optional): The Hugging Face Hub model ID or local path to
            load the TimesFM model from. Examples include
            `google/timesfm-2.0-500m-pytorch`. Defaults to
            `google/timesfm-2.0-500m-pytorch`. See the full list of models at
            [Hugging Face](https://huggingface.co/collections/google/timesfm-release-
            66e4be5fdb56e960c1e482a6). Supported models:

            - `google/timesfm-1.0-200m-pytorch`
            - `google/timesfm-2.0-500m-pytorch`
            - `google/timesfm-2.5-200m-pytorch`
        context_length (int, optional): Maximum context length (input window size)
            for the model. Defaults to 2048. For TimesFM 2.0 models, max is 2048
            (must be a multiple of 32). For TimesFM 1.0 models, max is 512. See
            [TimesFM docs](https://github.com/google-research/timesfm#loading-the-
            model) for details.
        batch_size (int, optional): Batch size for inference. Defaults to 64.
            Adjust based on available memory and model size.
        alias (str, optional): Name to use for the model in output DataFrames and
            logs. Defaults to `TimesFM`.
        kwargs (dict, optional): Additional keyword arguments to pass to the model.
            Defaults to None. Only used for TimesFM 2.5 models.

    Notes:
        **Academic Reference:**

        - Paper: [A decoder-only foundation model for time-series forecasting](https://arxiv.org/abs/2310.10688)

        **Resources:**

        - GitHub: [google-research/timesfm](https://github.com/google-research/timesfm)
        - HuggingFace: [google/timesfm-release](https://huggingface.co/collections/google/timesfm-release-66e4be5fdb56e960c1e482a6)

        **Technical Details:**

        - Only PyTorch checkpoints are currently supported. JAX is not supported.
        - The model is loaded onto the best available device (GPU if available,
          otherwise CPU).

        **Supported Models:**

        - `google/timesfm-1.0-200m-pytorch`
        - `google/timesfm-2.0-500m-pytorch`
        - `google/timesfm-2.5-200m-pytorch`
    """
    pass

forecast

forecast(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Generate forecasts for time series data using the model.

This method produces point forecasts and, optionally, prediction intervals or quantile forecasts. The input DataFrame can contain one or multiple time series in stacked (long) format.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). If provided, the returned DataFrame will include lower and upper interval columns for each specified level.

None
quantiles list[float]

List of quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. When provided, the output DataFrame will contain additional columns named in the format "model-q-{percentile}", where {percentile} = 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing forecast results. Includes:

- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.

For multi-series data, the output retains the same unique identifiers as the input DataFrame.

Source code in timecopilot/models/utils/forecaster.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
def forecast(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """Generate forecasts for time series data using the model.

    This method produces point forecasts and, optionally, prediction
    intervals or quantile forecasts. The input DataFrame can contain one
    or multiple time series in stacked (long) format.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.org/
            pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for
            valid values. If not provided, the frequency will be inferred
            from the data.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). If provided, the returned
            DataFrame will include lower and upper interval columns for
            each specified level.
        quantiles (list[float], optional):
            List of quantiles to forecast, expressed as floats between 0
            and 1. Should not be used simultaneously with `level`. When
            provided, the output DataFrame will contain additional columns
            named in the format "model-q-{percentile}", where {percentile}
            = 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing forecast results. Includes:

                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.

            For multi-series data, the output retains the same unique
            identifiers as the input DataFrame.
    """
    raise NotImplementedError("This method must be implemented in a subclass.")

cross_validation

cross_validation(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Perform cross-validation on time series data.

This method splits the time series into multiple training and testing windows and generates forecasts for each window. It enables evaluating forecast accuracy over different historical periods. Supports point forecasts and, optionally, prediction intervals or quantile forecasts.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict in each window.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. Defaults to 1.

1
step_size int

Step size between the start of consecutive windows. If None, it defaults to h.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). When specified, the output DataFrame includes lower and upper interval columns for each level.

None
quantiles list[float]

Quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. If provided, additional columns named "model-q-{percentile}" will appear in the output, where {percentile} is 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- "cutoff" column to indicate which window each forecast
  belongs to.
- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.
Source code in timecopilot/models/utils/forecaster.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def cross_validation(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """
    Perform cross-validation on time series data.

    This method splits the time series into multiple training and testing
    windows and generates forecasts for each window. It enables evaluating
    forecast accuracy over different historical periods. Supports point
    forecasts and, optionally, prediction intervals or quantile forecasts.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict in
            each window.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate. Defaults to 1.
        step_size (int, optional):
            Step size between the start of consecutive windows. If None, it
            defaults to `h`.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). When specified, the output
            DataFrame includes lower and upper interval columns for each
            level.
        quantiles (list[float], optional):
            Quantiles to forecast, expressed as floats between 0 and 1.
            Should not be used simultaneously with `level`. If provided,
            additional columns named "model-q-{percentile}" will appear in
            the output, where {percentile} is 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - "cutoff" column to indicate which window each forecast
                  belongs to.
                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    # mlforecast cv code
    results = []
    sort_idxs = maybe_compute_sort_indices(df, "unique_id", "ds")
    if sort_idxs is not None:
        df = take_rows(df, sort_idxs)
    splits = backtest_splits(
        df,
        n_windows=n_windows,
        h=h,
        id_col="unique_id",
        time_col="ds",
        freq=pd.tseries.frequencies.to_offset(freq),
        step_size=h if step_size is None else step_size,
    )
    for _, (cutoffs, train, valid) in tqdm(enumerate(splits)):
        if len(valid.columns) > 3:
            raise NotImplementedError(
                "Cross validation with exogenous variables is not yet supported."
            )
        y_pred = self.forecast(
            df=train,
            h=h,
            freq=freq,
            level=level,
            quantiles=quantiles,
        )
        y_pred = join(y_pred, cutoffs, on="unique_id", how="left")
        result = join(
            valid[["unique_id", "ds", "y"]],
            y_pred,
            on=["unique_id", "ds"],
        )
        if result.shape[0] < valid.shape[0]:
            raise ValueError(
                "Cross validation result produced less results than expected. "
                "Please verify that the frequency parameter (freq) "
                "matches your series' "
                "and that there aren't any missing periods."
            )
        results.append(result)
    out = vertical_concat(results)
    out = drop_index_if_pandas(out)
    first_out_cols = ["unique_id", "ds", "cutoff", "y"]
    remaining_cols = [c for c in out.columns if c not in first_out_cols]
    fcst_cv_df = out[first_out_cols + remaining_cols]
    return fcst_cv_df

detect_anomalies

detect_anomalies(
    df: DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> DataFrame

Detect anomalies in time-series using a cross-validated z-score test.

This method uses rolling-origin cross-validation to (1) produce adjusted (out-of-sample) predictions and (2) estimate the standard deviation of forecast errors. It then computes a per-point z-score, flags values outside a two-sided prediction interval (with confidence level), and returns a DataFrame with results.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to detect anomalies.

required
h int

Forecast horizon specifying how many future steps to predict. In each cross validation window. If not provided, the seasonality of the data (inferred from the frequency) is used.

None
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. If not provided, the maximum number of windows (computed by the shortest time series) is used. If provided, the number of windows is the minimum between the maximum number of windows (computed by the shortest time series) and the number of windows provided.

None
level int | float

Confidence levels for z-score, expressed as percentages (e.g. 80, 95). Default is 99.

99

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- model column to indicate the model.
- lower prediction interval.
- upper prediction interval.
- anomaly column to indicate if the value is an anomaly.
    an anomaly is defined as a value that is outside of the
    prediction interval (True or False).
Source code in timecopilot/models/utils/forecaster.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
def detect_anomalies(
    self,
    df: pd.DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> pd.DataFrame:
    """
    Detect anomalies in time-series using a cross-validated z-score test.

    This method uses rolling-origin cross-validation to (1) produce
    adjusted (out-of-sample) predictions and (2) estimate the
    standard deviation of forecast errors. It then computes a per-point z-score,
    flags values outside a two-sided prediction interval (with confidence `level`),
    and returns a DataFrame with results.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to detect anomalies.
        h (int, optional):
            Forecast horizon specifying how many future steps to predict.
            In each cross validation window. If not provided, the seasonality
            of the data (inferred from the frequency) is used.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate.
            If not provided, the maximum number of windows
            (computed by the shortest time series) is used.
            If provided, the number of windows is the minimum
            between the maximum number of windows
            (computed by the shortest time series)
            and the number of windows provided.
        level (int | float):
            Confidence levels for z-score, expressed as
            percentages (e.g. 80, 95). Default is 99.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - model column to indicate the model.
                - lower prediction interval.
                - upper prediction interval.
                - anomaly column to indicate if the value is an anomaly.
                    an anomaly is defined as a value that is outside of the
                    prediction interval (True or False).
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    if h is None:
        h = self._maybe_get_seasonality(freq)
    min_series_length = df.groupby("unique_id").size().min()
    # we require at least one observation before the first forecast
    max_possible_windows = (min_series_length - 1) // h
    if n_windows is None:
        _n_windows = max_possible_windows
    else:
        _n_windows = min(n_windows, max_possible_windows)
    if _n_windows < 1:
        raise ValueError(
            f"Cannot perform anomaly detection: series too short. "
            f"Minimum series length required: {h + 1}, "
            f"actual minimum length: {min_series_length}"
        )
    cv_results = self.cross_validation(
        df=df,
        h=h,
        freq=freq,
        n_windows=_n_windows,
        step_size=h,  # this is the default but who knows, anxiety
    )
    cv_results["residuals"] = cv_results["y"] - cv_results[self.alias]
    residual_stats = (
        cv_results.groupby("unique_id")["residuals"].std().reset_index()
    )
    residual_stats.columns = ["unique_id", "residual_std"]
    cv_results = cv_results.merge(residual_stats, on="unique_id", how="left")
    cv_results["z_score"] = cv_results["residuals"] / cv_results["residual_std"]
    alpha = 1 - level / 100
    critical_z = stats.norm.ppf(1 - alpha / 2)
    an_col = f"{self.alias}-anomaly"
    cv_results[an_col] = np.abs(cv_results["z_score"]) > critical_z
    lo_col = f"{self.alias}-lo-{int(level)}"
    hi_col = f"{self.alias}-hi-{int(level)}"
    margin = critical_z * cv_results["residual_std"]
    cv_results[lo_col] = cv_results[self.alias] - margin
    cv_results[hi_col] = cv_results[self.alias] + margin
    output_cols = [
        "unique_id",
        "ds",
        "cutoff",
        "y",
        self.alias,
        lo_col,
        hi_col,
        an_col,
    ]
    result = cv_results[output_cols].copy()
    result = drop_index_if_pandas(result)
    return result

plot staticmethod

plot(
    df: DataFrame | None = None,
    forecasts_df: DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: Axes | ndarray | Figure | None = None,
)

Plot forecasts and insample values.

Parameters:

Name Type Description Default
df DataFrame

DataFrame with columns [unique_id, ds, y]. Defaults to None.

None
forecasts_df DataFrame

DataFrame with columns [unique_id, ds] and models. Defaults to None.

None
ids list[str]

Time Series to plot. If None, time series are selected randomly. Defaults to None.

None
plot_random bool

Select time series to plot randomly. Defaults to True.

True
max_ids int

Maximum number of ids to plot. Defaults to 8.

8
models list[str]

Models to plot. Defaults to None.

None
level list[float]

Prediction intervals to plot. Defaults to None.

None
max_insample_length int

Maximum number of train/insample observations to be plotted. Defaults to None.

None
plot_anomalies bool

Plot anomalies for each prediction interval. Defaults to False.

False
engine str

Library used to plot. 'plotly', 'plotly-resampler' or 'matplotlib'. Defaults to 'matplotlib'.

'matplotlib'
palette str

Name of the matplotlib colormap to use for the plots. If None, uses the current style. Defaults to None.

None
seed int

Seed used for the random number generator. Only used if plot_random is True. Defaults to 0.

None
resampler_kwargs dict

Keyword arguments to be passed to plotly-resampler constructor. For further custumization ("show_dash") call the method, store the plotting object and add the extra arguments to its show_dash method. Defaults to None.

None
ax matplotlib axes, array of matplotlib axes or plotly Figure

Object where plots will be added. Defaults to None.

None
Source code in timecopilot/models/utils/forecaster.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
@staticmethod
def plot(
    df: pd.DataFrame | None = None,
    forecasts_df: pd.DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: plt.Axes | np.ndarray | plotly.graph_objects.Figure | None = None,
):
    """Plot forecasts and insample values.

    Args:
        df (pd.DataFrame, optional): DataFrame with columns
            [`unique_id`, `ds`, `y`]. Defaults to None.
        forecasts_df (pd.DataFrame, optional): DataFrame with
            columns [`unique_id`, `ds`] and models. Defaults to None.
        ids (list[str], optional): Time Series to plot. If None, time series
            are selected randomly. Defaults to None.
        plot_random (bool, optional): Select time series to plot randomly.
            Defaults to True.
        max_ids (int, optional): Maximum number of ids to plot. Defaults to 8.
        models (list[str], optional): Models to plot. Defaults to None.
        level (list[float], optional): Prediction intervals to plot.
            Defaults to None.
        max_insample_length (int, optional): Maximum number of train/insample
            observations to be plotted. Defaults to None.
        plot_anomalies (bool, optional): Plot anomalies for each prediction
            interval. Defaults to False.
        engine (str, optional): Library used to plot. 'plotly', 'plotly-resampler'
            or 'matplotlib'. Defaults to 'matplotlib'.
        palette (str, optional): Name of the matplotlib colormap to use for the
            plots. If None, uses the current style. Defaults to None.
        seed (int, optional): Seed used for the random number generator. Only
            used if plot_random is True. Defaults to 0.
        resampler_kwargs (dict, optional): Keyword arguments to be passed to
            plotly-resampler constructor. For further custumization ("show_dash")
            call the method, store the plotting object and add the extra arguments
            to its `show_dash` method. Defaults to None.
        ax (matplotlib axes, array of matplotlib axes or plotly Figure, optional):
            Object where plots will be added. Defaults to None.
    """
    df = ensure_time_dtype(df, time_col="ds")
    if forecasts_df is not None:
        forecasts_df = ensure_time_dtype(forecasts_df, time_col="ds")
        if any("anomaly" in col for col in forecasts_df.columns):
            df = None
            models = [
                col.split("-")[0]
                for col in forecasts_df.columns
                if col.endswith("-anomaly")
            ]
            forecasts_df = ufp.drop_columns(
                forecasts_df,
                [f"{model}-anomaly" for model in models],
            )
            lv_cols = [
                c.replace(f"{model}-lo-", "")
                for model in models
                for c in forecasts_df.columns
                if f"{model}-lo-" in c
            ]
            level = [float(c) if "." in c else int(c) for c in lv_cols]
            level = list(set(level))
            plot_anomalies = True
    return plot_series(
        df=df,
        forecasts_df=forecasts_df,
        ids=ids,
        plot_random=plot_random,
        max_ids=max_ids,
        models=models,
        level=level,
        max_insample_length=max_insample_length,
        plot_anomalies=plot_anomalies,
        engine=engine,
        resampler_kwargs=resampler_kwargs,
        palette=palette,
        seed=seed,
        id_col="unique_id",
        time_col="ds",
        target_col="y",
        ax=ax,
    )

TiRex

TiRex(
    repo_id: str = "NX-AI/TiRex",
    batch_size: int = 16,
    alias: str = "TiRex",
)

Bases: Forecaster

TiRex is a zero-shot time series forecasting model based on xLSTM, supporting both point and quantile predictions for long and short horizons. See the official repo for more details.

Parameters:

Name Type Description Default
repo_id str

The Hugging Face Hub model ID or local path to load the TiRex model from. Examples include "NX-AI/TiRex". Defaults to "NX-AI/TiRex". See the full list of models at Hugging Face.

'NX-AI/TiRex'
batch_size int

Batch size to use for inference. Defaults to 16. Adjust based on available memory and model size.

16
alias str

Name to use for the model in output DataFrames and logs. Defaults to "TiRex".

'TiRex'
Notes

Academic Reference:

Resources:

Technical Details:

  • The model is loaded onto the best available device (GPU if available, otherwise CPU).
  • On CPU, CUDA kernels are disabled automatically. See the CUDA kernels section for details.
  • For best performance, a CUDA-capable GPU with compute capability >= 8.0 is recommended.
  • The model is only available for Python >= 3.11.
Source code in timecopilot/models/foundation/tirex.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def __init__(
    self,
    repo_id: str = "NX-AI/TiRex",
    batch_size: int = 16,
    alias: str = "TiRex",
):
    """
    Args:
        repo_id (str, optional): The Hugging Face Hub model ID or local path to load
            the TiRex model from. Examples include "NX-AI/TiRex". Defaults to
            "NX-AI/TiRex". See the full list of models at
            [Hugging Face](https://huggingface.co/NX-AI).
        batch_size (int, optional): Batch size to use for inference. Defaults to 16.
            Adjust based on available memory and model size.
        alias (str, optional): Name to use for the model in output DataFrames
            and logs. Defaults to "TiRex".

    Notes:
        **Academic Reference:**

        - Paper: [TiRex: Zero-shot Time Series Forecasting with xLSTM](https://arxiv.org/abs/2505.23719)

        **Resources:**

        - GitHub: [NX-AI/tirex](https://github.com/NX-AI/tirex)
        - HuggingFace: [NX-AI Models](https://huggingface.co/NX-AI)

        **Technical Details:**

        - The model is loaded onto the best available device (GPU if available,
          otherwise CPU).
        - On CPU, CUDA kernels are disabled automatically. See the
          [CUDA kernels section](https://github.com/NX-AI/tirex#cuda-kernels)
          for details.
        - For best performance, a CUDA-capable GPU with compute capability >= 8.0
          is recommended.
        - The model is only available for Python >= 3.11.
    """
    self.repo_id = repo_id
    self.batch_size = batch_size
    self.alias = alias

forecast

forecast(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Generate forecasts for time series data using the model.

This method produces point forecasts and, optionally, prediction intervals or quantile forecasts. The input DataFrame can contain one or multiple time series in stacked (long) format.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). If provided, the returned DataFrame will include lower and upper interval columns for each specified level.

None
quantiles list[float]

List of quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. When provided, the output DataFrame will contain additional columns named in the format "model-q-{percentile}", where {percentile} = 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing forecast results. Includes:

- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.

For multi-series data, the output retains the same unique identifiers as the input DataFrame.

Source code in timecopilot/models/foundation/tirex.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def forecast(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """Generate forecasts for time series data using the model.

    This method produces point forecasts and, optionally, prediction
    intervals or quantile forecasts. The input DataFrame can contain one
    or multiple time series in stacked (long) format.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.org/
            pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for
            valid values. If not provided, the frequency will be inferred
            from the data.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). If provided, the returned
            DataFrame will include lower and upper interval columns for
            each specified level.
        quantiles (list[float], optional):
            List of quantiles to forecast, expressed as floats between 0
            and 1. Should not be used simultaneously with `level`. When
            provided, the output DataFrame will contain additional columns
            named in the format "model-q-{percentile}", where {percentile}
            = 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing forecast results. Includes:

                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.

            For multi-series data, the output retains the same unique
            identifiers as the input DataFrame.
    """
    freq = self._maybe_infer_freq(df, freq)
    qc = QuantileConverter(level=level, quantiles=quantiles)
    dataset = TimeSeriesDataset.from_df(df, batch_size=self.batch_size)
    fcst_df = dataset.make_future_dataframe(h=h, freq=freq)
    with self._get_model() as model:
        fcsts_mean_np, fcsts_quantiles_np = self._forecast(
            model,
            dataset,
            h,
            quantiles=qc.quantiles,
        )
    fcst_df[self.alias] = fcsts_mean_np.reshape(-1, 1)
    if qc.quantiles is not None and fcsts_quantiles_np is not None:
        for i, q in enumerate(qc.quantiles):
            fcst_df[f"{self.alias}-q-{int(q * 100)}"] = fcsts_quantiles_np[
                ..., i
            ].reshape(-1, 1)
        fcst_df = qc.maybe_convert_quantiles_to_level(
            fcst_df,
            models=[self.alias],
        )
    return fcst_df

cross_validation

cross_validation(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Perform cross-validation on time series data.

This method splits the time series into multiple training and testing windows and generates forecasts for each window. It enables evaluating forecast accuracy over different historical periods. Supports point forecasts and, optionally, prediction intervals or quantile forecasts.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict in each window.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. Defaults to 1.

1
step_size int

Step size between the start of consecutive windows. If None, it defaults to h.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). When specified, the output DataFrame includes lower and upper interval columns for each level.

None
quantiles list[float]

Quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. If provided, additional columns named "model-q-{percentile}" will appear in the output, where {percentile} is 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- "cutoff" column to indicate which window each forecast
  belongs to.
- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.
Source code in timecopilot/models/utils/forecaster.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def cross_validation(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """
    Perform cross-validation on time series data.

    This method splits the time series into multiple training and testing
    windows and generates forecasts for each window. It enables evaluating
    forecast accuracy over different historical periods. Supports point
    forecasts and, optionally, prediction intervals or quantile forecasts.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict in
            each window.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate. Defaults to 1.
        step_size (int, optional):
            Step size between the start of consecutive windows. If None, it
            defaults to `h`.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). When specified, the output
            DataFrame includes lower and upper interval columns for each
            level.
        quantiles (list[float], optional):
            Quantiles to forecast, expressed as floats between 0 and 1.
            Should not be used simultaneously with `level`. If provided,
            additional columns named "model-q-{percentile}" will appear in
            the output, where {percentile} is 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - "cutoff" column to indicate which window each forecast
                  belongs to.
                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    # mlforecast cv code
    results = []
    sort_idxs = maybe_compute_sort_indices(df, "unique_id", "ds")
    if sort_idxs is not None:
        df = take_rows(df, sort_idxs)
    splits = backtest_splits(
        df,
        n_windows=n_windows,
        h=h,
        id_col="unique_id",
        time_col="ds",
        freq=pd.tseries.frequencies.to_offset(freq),
        step_size=h if step_size is None else step_size,
    )
    for _, (cutoffs, train, valid) in tqdm(enumerate(splits)):
        if len(valid.columns) > 3:
            raise NotImplementedError(
                "Cross validation with exogenous variables is not yet supported."
            )
        y_pred = self.forecast(
            df=train,
            h=h,
            freq=freq,
            level=level,
            quantiles=quantiles,
        )
        y_pred = join(y_pred, cutoffs, on="unique_id", how="left")
        result = join(
            valid[["unique_id", "ds", "y"]],
            y_pred,
            on=["unique_id", "ds"],
        )
        if result.shape[0] < valid.shape[0]:
            raise ValueError(
                "Cross validation result produced less results than expected. "
                "Please verify that the frequency parameter (freq) "
                "matches your series' "
                "and that there aren't any missing periods."
            )
        results.append(result)
    out = vertical_concat(results)
    out = drop_index_if_pandas(out)
    first_out_cols = ["unique_id", "ds", "cutoff", "y"]
    remaining_cols = [c for c in out.columns if c not in first_out_cols]
    fcst_cv_df = out[first_out_cols + remaining_cols]
    return fcst_cv_df

detect_anomalies

detect_anomalies(
    df: DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> DataFrame

Detect anomalies in time-series using a cross-validated z-score test.

This method uses rolling-origin cross-validation to (1) produce adjusted (out-of-sample) predictions and (2) estimate the standard deviation of forecast errors. It then computes a per-point z-score, flags values outside a two-sided prediction interval (with confidence level), and returns a DataFrame with results.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to detect anomalies.

required
h int

Forecast horizon specifying how many future steps to predict. In each cross validation window. If not provided, the seasonality of the data (inferred from the frequency) is used.

None
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. If not provided, the maximum number of windows (computed by the shortest time series) is used. If provided, the number of windows is the minimum between the maximum number of windows (computed by the shortest time series) and the number of windows provided.

None
level int | float

Confidence levels for z-score, expressed as percentages (e.g. 80, 95). Default is 99.

99

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- model column to indicate the model.
- lower prediction interval.
- upper prediction interval.
- anomaly column to indicate if the value is an anomaly.
    an anomaly is defined as a value that is outside of the
    prediction interval (True or False).
Source code in timecopilot/models/utils/forecaster.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
def detect_anomalies(
    self,
    df: pd.DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> pd.DataFrame:
    """
    Detect anomalies in time-series using a cross-validated z-score test.

    This method uses rolling-origin cross-validation to (1) produce
    adjusted (out-of-sample) predictions and (2) estimate the
    standard deviation of forecast errors. It then computes a per-point z-score,
    flags values outside a two-sided prediction interval (with confidence `level`),
    and returns a DataFrame with results.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to detect anomalies.
        h (int, optional):
            Forecast horizon specifying how many future steps to predict.
            In each cross validation window. If not provided, the seasonality
            of the data (inferred from the frequency) is used.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate.
            If not provided, the maximum number of windows
            (computed by the shortest time series) is used.
            If provided, the number of windows is the minimum
            between the maximum number of windows
            (computed by the shortest time series)
            and the number of windows provided.
        level (int | float):
            Confidence levels for z-score, expressed as
            percentages (e.g. 80, 95). Default is 99.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - model column to indicate the model.
                - lower prediction interval.
                - upper prediction interval.
                - anomaly column to indicate if the value is an anomaly.
                    an anomaly is defined as a value that is outside of the
                    prediction interval (True or False).
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    if h is None:
        h = self._maybe_get_seasonality(freq)
    min_series_length = df.groupby("unique_id").size().min()
    # we require at least one observation before the first forecast
    max_possible_windows = (min_series_length - 1) // h
    if n_windows is None:
        _n_windows = max_possible_windows
    else:
        _n_windows = min(n_windows, max_possible_windows)
    if _n_windows < 1:
        raise ValueError(
            f"Cannot perform anomaly detection: series too short. "
            f"Minimum series length required: {h + 1}, "
            f"actual minimum length: {min_series_length}"
        )
    cv_results = self.cross_validation(
        df=df,
        h=h,
        freq=freq,
        n_windows=_n_windows,
        step_size=h,  # this is the default but who knows, anxiety
    )
    cv_results["residuals"] = cv_results["y"] - cv_results[self.alias]
    residual_stats = (
        cv_results.groupby("unique_id")["residuals"].std().reset_index()
    )
    residual_stats.columns = ["unique_id", "residual_std"]
    cv_results = cv_results.merge(residual_stats, on="unique_id", how="left")
    cv_results["z_score"] = cv_results["residuals"] / cv_results["residual_std"]
    alpha = 1 - level / 100
    critical_z = stats.norm.ppf(1 - alpha / 2)
    an_col = f"{self.alias}-anomaly"
    cv_results[an_col] = np.abs(cv_results["z_score"]) > critical_z
    lo_col = f"{self.alias}-lo-{int(level)}"
    hi_col = f"{self.alias}-hi-{int(level)}"
    margin = critical_z * cv_results["residual_std"]
    cv_results[lo_col] = cv_results[self.alias] - margin
    cv_results[hi_col] = cv_results[self.alias] + margin
    output_cols = [
        "unique_id",
        "ds",
        "cutoff",
        "y",
        self.alias,
        lo_col,
        hi_col,
        an_col,
    ]
    result = cv_results[output_cols].copy()
    result = drop_index_if_pandas(result)
    return result

plot staticmethod

plot(
    df: DataFrame | None = None,
    forecasts_df: DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: Axes | ndarray | Figure | None = None,
)

Plot forecasts and insample values.

Parameters:

Name Type Description Default
df DataFrame

DataFrame with columns [unique_id, ds, y]. Defaults to None.

None
forecasts_df DataFrame

DataFrame with columns [unique_id, ds] and models. Defaults to None.

None
ids list[str]

Time Series to plot. If None, time series are selected randomly. Defaults to None.

None
plot_random bool

Select time series to plot randomly. Defaults to True.

True
max_ids int

Maximum number of ids to plot. Defaults to 8.

8
models list[str]

Models to plot. Defaults to None.

None
level list[float]

Prediction intervals to plot. Defaults to None.

None
max_insample_length int

Maximum number of train/insample observations to be plotted. Defaults to None.

None
plot_anomalies bool

Plot anomalies for each prediction interval. Defaults to False.

False
engine str

Library used to plot. 'plotly', 'plotly-resampler' or 'matplotlib'. Defaults to 'matplotlib'.

'matplotlib'
palette str

Name of the matplotlib colormap to use for the plots. If None, uses the current style. Defaults to None.

None
seed int

Seed used for the random number generator. Only used if plot_random is True. Defaults to 0.

None
resampler_kwargs dict

Keyword arguments to be passed to plotly-resampler constructor. For further custumization ("show_dash") call the method, store the plotting object and add the extra arguments to its show_dash method. Defaults to None.

None
ax matplotlib axes, array of matplotlib axes or plotly Figure

Object where plots will be added. Defaults to None.

None
Source code in timecopilot/models/utils/forecaster.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
@staticmethod
def plot(
    df: pd.DataFrame | None = None,
    forecasts_df: pd.DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: plt.Axes | np.ndarray | plotly.graph_objects.Figure | None = None,
):
    """Plot forecasts and insample values.

    Args:
        df (pd.DataFrame, optional): DataFrame with columns
            [`unique_id`, `ds`, `y`]. Defaults to None.
        forecasts_df (pd.DataFrame, optional): DataFrame with
            columns [`unique_id`, `ds`] and models. Defaults to None.
        ids (list[str], optional): Time Series to plot. If None, time series
            are selected randomly. Defaults to None.
        plot_random (bool, optional): Select time series to plot randomly.
            Defaults to True.
        max_ids (int, optional): Maximum number of ids to plot. Defaults to 8.
        models (list[str], optional): Models to plot. Defaults to None.
        level (list[float], optional): Prediction intervals to plot.
            Defaults to None.
        max_insample_length (int, optional): Maximum number of train/insample
            observations to be plotted. Defaults to None.
        plot_anomalies (bool, optional): Plot anomalies for each prediction
            interval. Defaults to False.
        engine (str, optional): Library used to plot. 'plotly', 'plotly-resampler'
            or 'matplotlib'. Defaults to 'matplotlib'.
        palette (str, optional): Name of the matplotlib colormap to use for the
            plots. If None, uses the current style. Defaults to None.
        seed (int, optional): Seed used for the random number generator. Only
            used if plot_random is True. Defaults to 0.
        resampler_kwargs (dict, optional): Keyword arguments to be passed to
            plotly-resampler constructor. For further custumization ("show_dash")
            call the method, store the plotting object and add the extra arguments
            to its `show_dash` method. Defaults to None.
        ax (matplotlib axes, array of matplotlib axes or plotly Figure, optional):
            Object where plots will be added. Defaults to None.
    """
    df = ensure_time_dtype(df, time_col="ds")
    if forecasts_df is not None:
        forecasts_df = ensure_time_dtype(forecasts_df, time_col="ds")
        if any("anomaly" in col for col in forecasts_df.columns):
            df = None
            models = [
                col.split("-")[0]
                for col in forecasts_df.columns
                if col.endswith("-anomaly")
            ]
            forecasts_df = ufp.drop_columns(
                forecasts_df,
                [f"{model}-anomaly" for model in models],
            )
            lv_cols = [
                c.replace(f"{model}-lo-", "")
                for model in models
                for c in forecasts_df.columns
                if f"{model}-lo-" in c
            ]
            level = [float(c) if "." in c else int(c) for c in lv_cols]
            level = list(set(level))
            plot_anomalies = True
    return plot_series(
        df=df,
        forecasts_df=forecasts_df,
        ids=ids,
        plot_random=plot_random,
        max_ids=max_ids,
        models=models,
        level=level,
        max_insample_length=max_insample_length,
        plot_anomalies=plot_anomalies,
        engine=engine,
        resampler_kwargs=resampler_kwargs,
        palette=palette,
        seed=seed,
        id_col="unique_id",
        time_col="ds",
        target_col="y",
        ax=ax,
    )

Toto

Toto(
    repo_id: str = "Datadog/Toto-Open-Base-1.0",
    context_length: int = 4096,
    batch_size: int = 16,
    num_samples: int = 128,
    samples_per_batch: int = 8,
    alias: str = "Toto",
)

Bases: Forecaster

Toto is a foundation model for multivariate time series forecasting, optimized for observability and high-dimensional data. See the official repo for more details.

Parameters:

Name Type Description Default
repo_id str

The Hugging Face Hub model ID or local path to load the Toto model from. Examples include "Datadog/Toto-Open-Base-1.0". Defaults to "Datadog/Toto-Open-Base-1.0". See the full list of models at Hugging Face.

'Datadog/Toto-Open-Base-1.0'
context_length int

Maximum context length (input window size) for the model. Defaults to 4096. Should match the configuration of the pretrained checkpoint. See Toto docs for details.

4096
batch_size int

Batch size to use for inference. Defaults to 16. Adjust based on available memory and model size.

16
num_samples int

Number of samples for probabilistic forecasting. Controls the number of forecast samples drawn for uncertainty estimation. Defaults to 128.

128
samples_per_batch int

Number of samples processed per batch during inference. Controls memory usage. Defaults to 8.

8
alias str

Name to use for the model in output DataFrames and logs. Defaults to "Toto".

'Toto'
Notes

Academic Reference:

Resources:

Technical Details:

  • The model is loaded onto the best available device (GPU if available, otherwise CPU).
  • For best performance, a CUDA-capable GPU is recommended.
Source code in timecopilot/models/foundation/toto.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def __init__(
    self,
    repo_id: str = "Datadog/Toto-Open-Base-1.0",
    context_length: int = 4096,
    batch_size: int = 16,
    num_samples: int = 128,
    samples_per_batch: int = 8,
    alias: str = "Toto",
):
    """
    Args:
        repo_id (str, optional): The Hugging Face Hub model ID or local path to
            load the Toto model from. Examples include "Datadog/Toto-Open-Base-1.0".
            Defaults to "Datadog/Toto-Open-Base-1.0". See the full list of models
            at [Hugging Face](https://huggingface.co/Datadog).
        context_length (int, optional): Maximum context length (input window size)
            for the model. Defaults to 4096. Should match the configuration of the
            pretrained checkpoint. See [Toto docs](https://github.com/DataDog/toto#
            toto-model) for details.
        batch_size (int, optional): Batch size to use for inference. Defaults to 16.
            Adjust based on available memory and model size.
        num_samples (int, optional): Number of samples for probabilistic
            forecasting. Controls the number of forecast samples drawn for
            uncertainty estimation. Defaults to 128.
        samples_per_batch (int, optional): Number of samples processed per batch
            during inference. Controls memory usage. Defaults to 8.
        alias (str, optional): Name to use for the model in output DataFrames and
            logs. Defaults to "Toto".

    Notes:
        **Academic Reference:**

        - Paper: [Building a Foundation Model for Time Series](https://arxiv.org/abs/2505.14766)

        **Resources:**

        - GitHub: [DataDog/toto](https://github.com/DataDog/toto)
        - HuggingFace: [Datadog Models](https://huggingface.co/Datadog)

        **Technical Details:**

        - The model is loaded onto the best available device (GPU if available,
          otherwise CPU).
        - For best performance, a CUDA-capable GPU is recommended.
    """
    self.repo_id = repo_id
    self.context_length = context_length
    self.batch_size = batch_size
    # Number of samples for probabilistic forecasting
    self.num_samples = num_samples
    # Control memory usage during inference
    self.samples_per_batch = samples_per_batch
    self.alias = alias
    self.device = "cuda" if torch.cuda.is_available() else "cpu"

forecast

forecast(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Generate forecasts for time series data using the model.

This method produces point forecasts and, optionally, prediction intervals or quantile forecasts. The input DataFrame can contain one or multiple time series in stacked (long) format.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). If provided, the returned DataFrame will include lower and upper interval columns for each specified level.

None
quantiles list[float]

List of quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. When provided, the output DataFrame will contain additional columns named in the format "model-q-{percentile}", where {percentile} = 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing forecast results. Includes:

- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.

For multi-series data, the output retains the same unique identifiers as the input DataFrame.

Source code in timecopilot/models/foundation/toto.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
def forecast(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """Generate forecasts for time series data using the model.

    This method produces point forecasts and, optionally, prediction
    intervals or quantile forecasts. The input DataFrame can contain one
    or multiple time series in stacked (long) format.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.org/
            pandas-docs/stable/user_guide/timeseries.html#offset-aliases) for
            valid values. If not provided, the frequency will be inferred
            from the data.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). If provided, the returned
            DataFrame will include lower and upper interval columns for
            each specified level.
        quantiles (list[float], optional):
            List of quantiles to forecast, expressed as floats between 0
            and 1. Should not be used simultaneously with `level`. When
            provided, the output DataFrame will contain additional columns
            named in the format "model-q-{percentile}", where {percentile}
            = 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing forecast results. Includes:

                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.

            For multi-series data, the output retains the same unique
            identifiers as the input DataFrame.
    """
    freq = self._maybe_infer_freq(df, freq)
    qc = QuantileConverter(level=level, quantiles=quantiles)
    dataset = TimeSeriesDataset.from_df(df, batch_size=self.batch_size)
    fcst_df = dataset.make_future_dataframe(h=h, freq=freq)
    with self._get_model() as model:
        fcsts_mean_np, fcsts_quantiles_np = self._forecast(
            model,
            dataset,
            h,
            quantiles=qc.quantiles,
        )
    fcst_df[self.alias] = fcsts_mean_np.reshape(-1, 1)
    if qc.quantiles is not None and fcsts_quantiles_np is not None:
        for i, q in enumerate(qc.quantiles):
            fcst_df[f"{self.alias}-q-{int(q * 100)}"] = fcsts_quantiles_np[
                ..., i
            ].reshape(-1, 1)
        fcst_df = qc.maybe_convert_quantiles_to_level(
            fcst_df,
            models=[self.alias],
        )
    return fcst_df

cross_validation

cross_validation(
    df: DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> DataFrame

Perform cross-validation on time series data.

This method splits the time series into multiple training and testing windows and generates forecasts for each window. It enables evaluating forecast accuracy over different historical periods. Supports point forecasts and, optionally, prediction intervals or quantile forecasts.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to forecast. It must include as columns:

- "unique_id": an ID column to distinguish multiple series.
- "ds": a time column indicating timestamps or periods.
- "y": a target column with the observed values.
required
h int

Forecast horizon specifying how many future steps to predict in each window.

required
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. Defaults to 1.

1
step_size int

Step size between the start of consecutive windows. If None, it defaults to h.

None
level list[int | float]

Confidence levels for prediction intervals, expressed as percentages (e.g. [80, 95]). When specified, the output DataFrame includes lower and upper interval columns for each level.

None
quantiles list[float]

Quantiles to forecast, expressed as floats between 0 and 1. Should not be used simultaneously with level. If provided, additional columns named "model-q-{percentile}" will appear in the output, where {percentile} is 100 ร— quantile value.

None

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- "cutoff" column to indicate which window each forecast
  belongs to.
- point forecasts for each timestamp and series.
- prediction intervals if `level` is specified.
- quantile forecasts if `quantiles` is specified.
Source code in timecopilot/models/utils/forecaster.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
def cross_validation(
    self,
    df: pd.DataFrame,
    h: int,
    freq: str | None = None,
    n_windows: int = 1,
    step_size: int | None = None,
    level: list[int | float] | None = None,
    quantiles: list[float] | None = None,
) -> pd.DataFrame:
    """
    Perform cross-validation on time series data.

    This method splits the time series into multiple training and testing
    windows and generates forecasts for each window. It enables evaluating
    forecast accuracy over different historical periods. Supports point
    forecasts and, optionally, prediction intervals or quantile forecasts.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to forecast. It must
            include as columns:

                - "unique_id": an ID column to distinguish multiple series.
                - "ds": a time column indicating timestamps or periods.
                - "y": a target column with the observed values.

        h (int):
            Forecast horizon specifying how many future steps to predict in
            each window.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate. Defaults to 1.
        step_size (int, optional):
            Step size between the start of consecutive windows. If None, it
            defaults to `h`.
        level (list[int | float], optional):
            Confidence levels for prediction intervals, expressed as
            percentages (e.g. [80, 95]). When specified, the output
            DataFrame includes lower and upper interval columns for each
            level.
        quantiles (list[float], optional):
            Quantiles to forecast, expressed as floats between 0 and 1.
            Should not be used simultaneously with `level`. If provided,
            additional columns named "model-q-{percentile}" will appear in
            the output, where {percentile} is 100 ร— quantile value.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - "cutoff" column to indicate which window each forecast
                  belongs to.
                - point forecasts for each timestamp and series.
                - prediction intervals if `level` is specified.
                - quantile forecasts if `quantiles` is specified.
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    # mlforecast cv code
    results = []
    sort_idxs = maybe_compute_sort_indices(df, "unique_id", "ds")
    if sort_idxs is not None:
        df = take_rows(df, sort_idxs)
    splits = backtest_splits(
        df,
        n_windows=n_windows,
        h=h,
        id_col="unique_id",
        time_col="ds",
        freq=pd.tseries.frequencies.to_offset(freq),
        step_size=h if step_size is None else step_size,
    )
    for _, (cutoffs, train, valid) in tqdm(enumerate(splits)):
        if len(valid.columns) > 3:
            raise NotImplementedError(
                "Cross validation with exogenous variables is not yet supported."
            )
        y_pred = self.forecast(
            df=train,
            h=h,
            freq=freq,
            level=level,
            quantiles=quantiles,
        )
        y_pred = join(y_pred, cutoffs, on="unique_id", how="left")
        result = join(
            valid[["unique_id", "ds", "y"]],
            y_pred,
            on=["unique_id", "ds"],
        )
        if result.shape[0] < valid.shape[0]:
            raise ValueError(
                "Cross validation result produced less results than expected. "
                "Please verify that the frequency parameter (freq) "
                "matches your series' "
                "and that there aren't any missing periods."
            )
        results.append(result)
    out = vertical_concat(results)
    out = drop_index_if_pandas(out)
    first_out_cols = ["unique_id", "ds", "cutoff", "y"]
    remaining_cols = [c for c in out.columns if c not in first_out_cols]
    fcst_cv_df = out[first_out_cols + remaining_cols]
    return fcst_cv_df

detect_anomalies

detect_anomalies(
    df: DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> DataFrame

Detect anomalies in time-series using a cross-validated z-score test.

This method uses rolling-origin cross-validation to (1) produce adjusted (out-of-sample) predictions and (2) estimate the standard deviation of forecast errors. It then computes a per-point z-score, flags values outside a two-sided prediction interval (with confidence level), and returns a DataFrame with results.

Parameters:

Name Type Description Default
df DataFrame

DataFrame containing the time series to detect anomalies.

required
h int

Forecast horizon specifying how many future steps to predict. In each cross validation window. If not provided, the seasonality of the data (inferred from the frequency) is used.

None
freq str

Frequency of the time series (e.g. "D" for daily, "M" for monthly). See Pandas frequency aliases for valid values. If not provided, the frequency will be inferred from the data.

None
n_windows int

Number of cross-validation windows to generate. If not provided, the maximum number of windows (computed by the shortest time series) is used. If provided, the number of windows is the minimum between the maximum number of windows (computed by the shortest time series) and the number of windows provided.

None
level int | float

Confidence levels for z-score, expressed as percentages (e.g. 80, 95). Default is 99.

99

Returns:

Type Description
DataFrame

pd.DataFrame: DataFrame containing the forecasts for each cross-validation window. The output includes:

- "unique_id" column to indicate the series.
- "ds" column to indicate the timestamp.
- "y" column to indicate the target.
- model column to indicate the model.
- lower prediction interval.
- upper prediction interval.
- anomaly column to indicate if the value is an anomaly.
    an anomaly is defined as a value that is outside of the
    prediction interval (True or False).
Source code in timecopilot/models/utils/forecaster.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
def detect_anomalies(
    self,
    df: pd.DataFrame,
    h: int | None = None,
    freq: str | None = None,
    n_windows: int | None = None,
    level: int | float = 99,
) -> pd.DataFrame:
    """
    Detect anomalies in time-series using a cross-validated z-score test.

    This method uses rolling-origin cross-validation to (1) produce
    adjusted (out-of-sample) predictions and (2) estimate the
    standard deviation of forecast errors. It then computes a per-point z-score,
    flags values outside a two-sided prediction interval (with confidence `level`),
    and returns a DataFrame with results.

    Args:
        df (pd.DataFrame):
            DataFrame containing the time series to detect anomalies.
        h (int, optional):
            Forecast horizon specifying how many future steps to predict.
            In each cross validation window. If not provided, the seasonality
            of the data (inferred from the frequency) is used.
        freq (str, optional):
            Frequency of the time series (e.g. "D" for daily, "M" for
            monthly). See [Pandas frequency aliases](https://pandas.pydata.
            org/pandas-docs/stable/user_guide/timeseries.html#offset-aliases)
            for valid values. If not provided, the frequency will be inferred
            from the data.
        n_windows (int, optional):
            Number of cross-validation windows to generate.
            If not provided, the maximum number of windows
            (computed by the shortest time series) is used.
            If provided, the number of windows is the minimum
            between the maximum number of windows
            (computed by the shortest time series)
            and the number of windows provided.
        level (int | float):
            Confidence levels for z-score, expressed as
            percentages (e.g. 80, 95). Default is 99.

    Returns:
        pd.DataFrame:
            DataFrame containing the forecasts for each cross-validation
            window. The output includes:

                - "unique_id" column to indicate the series.
                - "ds" column to indicate the timestamp.
                - "y" column to indicate the target.
                - model column to indicate the model.
                - lower prediction interval.
                - upper prediction interval.
                - anomaly column to indicate if the value is an anomaly.
                    an anomaly is defined as a value that is outside of the
                    prediction interval (True or False).
    """
    freq = self._maybe_infer_freq(df, freq)
    df = maybe_convert_col_to_datetime(df, "ds")
    if h is None:
        h = self._maybe_get_seasonality(freq)
    min_series_length = df.groupby("unique_id").size().min()
    # we require at least one observation before the first forecast
    max_possible_windows = (min_series_length - 1) // h
    if n_windows is None:
        _n_windows = max_possible_windows
    else:
        _n_windows = min(n_windows, max_possible_windows)
    if _n_windows < 1:
        raise ValueError(
            f"Cannot perform anomaly detection: series too short. "
            f"Minimum series length required: {h + 1}, "
            f"actual minimum length: {min_series_length}"
        )
    cv_results = self.cross_validation(
        df=df,
        h=h,
        freq=freq,
        n_windows=_n_windows,
        step_size=h,  # this is the default but who knows, anxiety
    )
    cv_results["residuals"] = cv_results["y"] - cv_results[self.alias]
    residual_stats = (
        cv_results.groupby("unique_id")["residuals"].std().reset_index()
    )
    residual_stats.columns = ["unique_id", "residual_std"]
    cv_results = cv_results.merge(residual_stats, on="unique_id", how="left")
    cv_results["z_score"] = cv_results["residuals"] / cv_results["residual_std"]
    alpha = 1 - level / 100
    critical_z = stats.norm.ppf(1 - alpha / 2)
    an_col = f"{self.alias}-anomaly"
    cv_results[an_col] = np.abs(cv_results["z_score"]) > critical_z
    lo_col = f"{self.alias}-lo-{int(level)}"
    hi_col = f"{self.alias}-hi-{int(level)}"
    margin = critical_z * cv_results["residual_std"]
    cv_results[lo_col] = cv_results[self.alias] - margin
    cv_results[hi_col] = cv_results[self.alias] + margin
    output_cols = [
        "unique_id",
        "ds",
        "cutoff",
        "y",
        self.alias,
        lo_col,
        hi_col,
        an_col,
    ]
    result = cv_results[output_cols].copy()
    result = drop_index_if_pandas(result)
    return result

plot staticmethod

plot(
    df: DataFrame | None = None,
    forecasts_df: DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: Axes | ndarray | Figure | None = None,
)

Plot forecasts and insample values.

Parameters:

Name Type Description Default
df DataFrame

DataFrame with columns [unique_id, ds, y]. Defaults to None.

None
forecasts_df DataFrame

DataFrame with columns [unique_id, ds] and models. Defaults to None.

None
ids list[str]

Time Series to plot. If None, time series are selected randomly. Defaults to None.

None
plot_random bool

Select time series to plot randomly. Defaults to True.

True
max_ids int

Maximum number of ids to plot. Defaults to 8.

8
models list[str]

Models to plot. Defaults to None.

None
level list[float]

Prediction intervals to plot. Defaults to None.

None
max_insample_length int

Maximum number of train/insample observations to be plotted. Defaults to None.

None
plot_anomalies bool

Plot anomalies for each prediction interval. Defaults to False.

False
engine str

Library used to plot. 'plotly', 'plotly-resampler' or 'matplotlib'. Defaults to 'matplotlib'.

'matplotlib'
palette str

Name of the matplotlib colormap to use for the plots. If None, uses the current style. Defaults to None.

None
seed int

Seed used for the random number generator. Only used if plot_random is True. Defaults to 0.

None
resampler_kwargs dict

Keyword arguments to be passed to plotly-resampler constructor. For further custumization ("show_dash") call the method, store the plotting object and add the extra arguments to its show_dash method. Defaults to None.

None
ax matplotlib axes, array of matplotlib axes or plotly Figure

Object where plots will be added. Defaults to None.

None
Source code in timecopilot/models/utils/forecaster.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
@staticmethod
def plot(
    df: pd.DataFrame | None = None,
    forecasts_df: pd.DataFrame | None = None,
    ids: list[str] | None = None,
    plot_random: bool = True,
    max_ids: int | None = 8,
    models: list[str] | None = None,
    level: list[float] | None = None,
    max_insample_length: int | None = None,
    plot_anomalies: bool = False,
    engine: str = "matplotlib",
    palette: str | None = None,
    seed: int | None = None,
    resampler_kwargs: dict | None = None,
    ax: plt.Axes | np.ndarray | plotly.graph_objects.Figure | None = None,
):
    """Plot forecasts and insample values.

    Args:
        df (pd.DataFrame, optional): DataFrame with columns
            [`unique_id`, `ds`, `y`]. Defaults to None.
        forecasts_df (pd.DataFrame, optional): DataFrame with
            columns [`unique_id`, `ds`] and models. Defaults to None.
        ids (list[str], optional): Time Series to plot. If None, time series
            are selected randomly. Defaults to None.
        plot_random (bool, optional): Select time series to plot randomly.
            Defaults to True.
        max_ids (int, optional): Maximum number of ids to plot. Defaults to 8.
        models (list[str], optional): Models to plot. Defaults to None.
        level (list[float], optional): Prediction intervals to plot.
            Defaults to None.
        max_insample_length (int, optional): Maximum number of train/insample
            observations to be plotted. Defaults to None.
        plot_anomalies (bool, optional): Plot anomalies for each prediction
            interval. Defaults to False.
        engine (str, optional): Library used to plot. 'plotly', 'plotly-resampler'
            or 'matplotlib'. Defaults to 'matplotlib'.
        palette (str, optional): Name of the matplotlib colormap to use for the
            plots. If None, uses the current style. Defaults to None.
        seed (int, optional): Seed used for the random number generator. Only
            used if plot_random is True. Defaults to 0.
        resampler_kwargs (dict, optional): Keyword arguments to be passed to
            plotly-resampler constructor. For further custumization ("show_dash")
            call the method, store the plotting object and add the extra arguments
            to its `show_dash` method. Defaults to None.
        ax (matplotlib axes, array of matplotlib axes or plotly Figure, optional):
            Object where plots will be added. Defaults to None.
    """
    df = ensure_time_dtype(df, time_col="ds")
    if forecasts_df is not None:
        forecasts_df = ensure_time_dtype(forecasts_df, time_col="ds")
        if any("anomaly" in col for col in forecasts_df.columns):
            df = None
            models = [
                col.split("-")[0]
                for col in forecasts_df.columns
                if col.endswith("-anomaly")
            ]
            forecasts_df = ufp.drop_columns(
                forecasts_df,
                [f"{model}-anomaly" for model in models],
            )
            lv_cols = [
                c.replace(f"{model}-lo-", "")
                for model in models
                for c in forecasts_df.columns
                if f"{model}-lo-" in c
            ]
            level = [float(c) if "." in c else int(c) for c in lv_cols]
            level = list(set(level))
            plot_anomalies = True
    return plot_series(
        df=df,
        forecasts_df=forecasts_df,
        ids=ids,
        plot_random=plot_random,
        max_ids=max_ids,
        models=models,
        level=level,
        max_insample_length=max_insample_length,
        plot_anomalies=plot_anomalies,
        engine=engine,
        resampler_kwargs=resampler_kwargs,
        palette=palette,
        seed=seed,
        id_col="unique_id",
        time_col="ds",
        target_col="y",
        ax=ax,
    )