Metadata-Version: 2.4
Name: tsfmecb
Version: 0.1.4
Summary: tsfm provides an easy interface to use TimeSeries Foundational Models.
Author-email: Ilias Aarab <ilias.aarab@ecb.europa.eu>
Requires-Python: >=3.12
Requires-Dist: accelerate>=1.10.1
Requires-Dist: chronos-forecasting>=2.0
Requires-Dist: gluonts[torch]
Requires-Dist: matplotlib>=3.8
Requires-Dist: numpy
Requires-Dist: pandas
Requires-Dist: scienceplots>=2.1.1
Requires-Dist: scipy>=1.11.4
Requires-Dist: statsmodels>=0.14.5
Requires-Dist: torch
Requires-Dist: transformers>=4.54.1
Requires-Dist: uni2ts>=2.0.0
Description-Content-Type: text/markdown

# UML Diagram - TSFM Codebase

## Overview

TSFM is a unified time series forecasting framework that supports multiple frequencies (monthly, quarterly, etc.) with automatic frequency inference. It provides a consistent interface across classical statistical models and modern foundation models.

## Key Features

- **Automatic Frequency Inference**: No need to specify data frequency - it's detected from the DataFrame's DatetimeIndex
- **Multiple Model Support**: Classical AR models and state-of-the-art foundation models (Moirai, Chronos)
- **Unified API**: Consistent interface across all models with `pred()` method
- **Frequency-Aware Metrics**: Horizons adapt to your data frequency (horizon=1 means next period, regardless of frequency)
- **Rich Outputs**: Built-in metrics (RMSFE, MAE, ME) and visualization with prediction intervals

```mermaid
classDiagram
    %% Exceptions
    class TSFMError {
        <<Exception>>
    }
    
    class InvalidInputError {
        <<Exception>>
        +__init__(message: str)
    }
    
    Exception <|-- TSFMError
    TSFMError <|-- InvalidInputError
    
    %% Output Classes
    class ForecastOutput {
        -df_preds: DataFrame
        -meta: dict[str, Any]
        +rmsfe: DataFrame
        +mae: DataFrame
        +me: DataFrame
        +metric(name: Literal) DataFrame
        +summary(digits: int) None
        +plot_actual_vs_pred(horizon: int, ax: Axes, start: Timestamp, end: Timestamp, return_ax: bool) Axes
        -_agg_mean(s: Series, name: str, post: Callable) DataFrame
        -_summary(digits: int) str
    }
    
    %% Base Model (Abstract)
    class Model {
        <<Abstract>>
        +registry: ClassVar[dict]
        +name: property
        +__init_subclass__(cls, name: str, **kwargs)
        +build(name: str, **kwargs) Model
        +get_backbone() Any*
        +_pred(df: DataFrame, y: str, X: list, ctx_len: int, horizon: int, oos_start: str)* DataFrame
        +pred(df: DataFrame, y: str, X: list, ctx_len: int, horizon: int, oos_start: str) ForecastOutput
    }
    
    ABC <|-- Model
    
    %% ARModel
    class ARModel {
        +get_backbone() None
        +_pred(df: DataFrame, y: str, X: list, ctx_len: int, horizon: int, oos_start: str) DataFrame
    }
    
    Model <|-- ARModel : name="armodel"
    ARModel ..> InvalidInputError : raises
    ARModel ..> ForecastOutput : creates
    
    %% Moirai Model
    class Moirai {
        +get_backbone() MoiraiModule
        +get_model(ctx_len: int, horizon: int, n_covariates: int) MoiraiForecast
        +_pred(df: DataFrame, y: str, X: list, ctx_len: int, horizon: int, oos_start: str) DataFrame
    }
    
    Model <|-- Moirai : name="moirai"
    Moirai ..> ForecastOutput : creates
    
    %% Moirai2 Model
    class Moirai2 {
        +get_backbone() Moirai2Module
        +get_model(ctx_len: int, horizon: int, n_covariates: int) Moirai2Forecast
        +_pred(df: DataFrame, y: str, X: list, ctx_len: int, horizon: int, oos_start: str) DataFrame
    }
    
    Model <|-- Moirai2 : name="moirai2"
    Moirai2 ..> ForecastOutput : creates
    
    %% Chronos Model
    class Chronos {
        +get_backbone() BaseChronosPipeline
        +_pred(df: DataFrame, y: str, X: list, ctx_len: int, horizon: int, oos_start: str) DataFrame
    }
    
    Model <|-- Chronos : name="chronos"
    Chronos ..> ForecastOutput : creates
    
    %% Chronos2 Model
    class Chronos2 {
        +get_backbone() Chronos2Pipeline
        +_pred(df: DataFrame, y: str, X: list, ctx_len: int, horizon: int, oos_start: str) DataFrame
    }
    
    Model <|-- Chronos2 : name="chronos2"
    Chronos2 ..> ForecastOutput : creates
    
    %% Data Module Functions
    class DataModule {
        <<module: data.py>>
        +infer_freq(df: DataFrame) str
        +generator(n: int, phi: float, beta0: float, beta1: float, sigma: float, trend: float, season_period: int, season_ampl: float, seed: int) DataFrame
        +split_is_oos(df: DataFrame, test_frac: float) tuple[DataFrame, DataFrame]
    }
    
    %% ARModel Helper Functions
    class ARModelHelpers {
        <<module: armodel.py>>
        +create_lags(var: Series, lags: int) DataFrame
        +get_design_matrix(ys: Series, y_lags: int, horizon: int) tuple[Series, DataFrame]
        +fit(ys: Series, y_lags: int, horizon: int) RegressionResultsWrapper
        +pred(ys: Series, y_lags: int, horizon: int, oos_start: str) Series
        +build_oos_panel(ys: Series, y_lags: int, horizon: int, oos_start: str, freq: str) DataFrame
        -_to_period_end(idx: Index, freq: str) DatetimeIndex
    }
    
    ARModel ..> ARModelHelpers : uses
    
    %% Moirai Helper Functions
    class MoiraiHelpers {
        <<module: moirai.py>>
        +make_fc_df(forecasts: list, y_true_series: Series, freq: str) DataFrame
        +prepare_data(df: DataFrame, y: str, X: list, horizon: int, oos_start: str, freq: str) TestData
        +MODEL_ID: str
    }
    
    Moirai ..> MoiraiHelpers : uses
    
    %% Moirai2 Helper Functions
    class Moirai2Helpers {
        <<module: moirai2.py>>
        +make_fc_df(forecasts: list, y_true_series: Series, freq: str) DataFrame
        +prepare_data(df: DataFrame, y: str, X: list, horizon: int, oos_start: str, freq: str) TestData
        +MODEL_ID: str
    }
    
    Moirai2 ..> Moirai2Helpers : uses
    
    %% Chronos Helper Functions
    class ChronosHelpers {
        <<module: chronos.py>>
        +prepare_data(df: DataFrame, y: str, X: list, ctx_len: int, oos_start: str, freq: str) DataFrame
        +make_fc_df(forecasts: DataFrame, y_true: DataFrame, horizon: int, freq: str) DataFrame
        +BOLT_MODEL_ID: str
    }
    
    Chronos ..> ChronosHelpers : uses
    
    %% Chronos2 Helper Functions
    class Chronos2Helpers {
        <<module: chronos2.py>>
        +prepare_data(df: DataFrame, y: str, X: list, ctx_len: int, oos_start: str, freq: str) DataFrame
        +make_fc_df(forecasts: DataFrame, y_true: DataFrame, horizon: int, freq: str) DataFrame
        +MODEL_ID: str
    }
    
    Chronos2 ..> Chronos2Helpers : uses
    
    %% Output Helper Functions
    class OutputHelpers {
        <<module: outputs.py>>
        +get_horizon_groupby(df: DataFrame, freq: str) Index
    }
    
    ForecastOutput ..> OutputHelpers : uses
    
    %% Relationships with external libraries
    Model ..> ForecastOutput : returns
    Model ..> DataModule : uses infer_freq
    
    %% Package level
    class TSFMPackage {
        <<module: __init__.py>>
        +ARModel
        +Model
        +Moirai
        +Moirai2
        +Chronos
        +Chronos2
        +generator
    }
    
    TSFMPackage ..> ARModel : exports
    TSFMPackage ..> Model : exports
    TSFMPackage ..> Moirai : exports
    TSFMPackage ..> Moirai2 : exports
    TSFMPackage ..> Chronos : exports
    TSFMPackage ..> Chronos2 : exports
    TSFMPackage ..> DataModule : exports generator

    %% Notes
    note for Model "Registry pattern:\nSubclasses auto-register\nby name parameter\nAuto-infers frequency from data"
    
    note for ForecastOutput "Contains forecast results\nwith MultiIndex[cutoff, oos_date]\nFrequency-aware metrics\nand visualization"
    
    note for ARModel "Autoregressive model\nusing statsmodels OLS\nwith HAC standard errors\nNo covariate support"
    
    note for Moirai "Pretrained foundation model\nfrom Salesforce\nmodel: moirai-1.1-R-small\nSupports covariates"
    
    note for Moirai2 "Pretrained foundation model\nfrom Salesforce\nmodel: moirai-2.0-R-small\nuses uni2ts library\nSupports covariates"
    
    note for Chronos "Amazon Chronos-Bolt model\nchronos-bolt-small\nNo covariate support\nFrequency-aware"
    
    note for Chronos2 "Amazon Chronos 2.0 model\namazon/chronos-2\nSupports covariates\nFrequency-aware"
    
    note for DataModule "infer_freq:\nAuto-detects frequency\nfrom DatetimeIndex\nSupports M, Q, W, D, etc."
```

## Class Descriptions

### Core Classes

1. **Model (Abstract Base Class)**
   - Registry pattern for dynamic model creation
   - All models inherit from this class
   - Defines interface: `get_backbone()` (abstract), `_pred()` (abstract), and `pred()` (concrete)
   - Registry allows building models by name: `Model.build(name="armodel")`
   - Automatically infers frequency from DataFrame and passes to ForecastOutput

2. **ARModel**
   - Autoregressive time series model using classical statistical methods
   - Uses statsmodels for OLS regression with HAC standard errors
   - Does not support covariates (raises `InvalidInputError`)
   - Implements expanding window forecasting
   - Frequency-aware: automatically adapts to monthly, quarterly, or other frequencies

3. **Moirai**
   - Wrapper for Salesforce's Moirai 1.1 foundation model (moirai-1.1-R-small)
   - Supports covariates via `past_feat_dynamic_real`
   - Uses GluonTS for data preparation
   - Generates 1,000 samples for probabilistic forecasting with quantile outputs
   - Frequency-aware with automatic normalization of timestamps

4. **Moirai2**
   - Wrapper for Salesforce's Moirai 2.0 foundation model (moirai-2.0-R-small)
   - Supports covariates
   - Uses uni2ts library for inference
   - Quantile-based forecasting (median as point forecast)
   - Frequency-aware with automatic normalization of timestamps

5. **Chronos**
   - Amazon Chronos-Bolt model (chronos-bolt-small)
   - Fast inference with quantile predictions
   - Does not support covariates
   - Frequency-aware with dynamic offset calculations
   - Returns quantile predictions (0.1 through 0.9)

6. **Chronos2**
   - Amazon Chronos 2.0 model (amazon/chronos-2)
   - Supports covariates via DataFrame columns
   - Uses `predict_df()` interface for batch predictions
   - Frequency-aware with dynamic offset calculations
   - Returns quantile predictions and point forecasts

### Data Structures

7. **ForecastOutput**
   - Dataclass containing forecast results
   - MultiIndex DataFrame: (cutoff, oos_date)
   - Cached properties for metrics: RMSFE, MAE, ME
   - Frequency-aware metrics (stored in `meta['freq']`)
   - Methods for summary statistics and plotting
   - `plot_actual_vs_pred()`: Visualize forecasts with prediction intervals
   - Horizon parameter adapts to data frequency (horizon=1 = next period)

### Exceptions

8. **TSFMError**
   - Base exception for the package

9. **InvalidInputError**
   - Raised when input validation fails (e.g., covariates not supported)

### Utility Modules

10. **DataModule (data.py)**
    - `infer_freq()`: Automatically detects frequency from DataFrame's DatetimeIndex
    - `generator()`: Simulates time series with AR, trend, seasonality
    - `split_is_oos()`: Splits data into train/test

11. **Helper Functions**
    - **ARModel helpers**: lag creation, design matrix, OLS fitting, frequency-aware period alignment
    - **Moirai helpers**: forecast formatting with frequency parameter, GluonTS data preparation
    - **Moirai2 helpers**: forecast formatting with frequency parameter, uni2ts data preparation
    - **Chronos helpers**: rolling window data preparation, frequency-aware offset handling
    - **Chronos2 helpers**: DataFrame-based data preparation, frequency-aware offset handling
    - **Output helpers**: horizon grouping with frequency awareness

## Architecture Patterns

- **Registry Pattern**: Models self-register via `__init_subclass__` enabling dynamic instantiation
- **Template Method**: Base `Model.pred()` calls abstract `_pred()` and handles common logic
- **Dataclass**: `ForecastOutput` with cached properties for lazy metric computation
- **Facade**: Simple interface (`pred()`) over complex forecasting logic
- **Frequency Inference**: Automatic detection and propagation of time series frequency throughout the pipeline

## Frequency Support

All models automatically detect and adapt to your data's frequency:

- **Monthly (M)**: Standard month-end dates
- **Quarterly (Q)**: Quarter-end dates  
- **Weekly (W)**: Week-end dates
- **Daily (D)**: Daily dates
- **Custom**: Any pandas frequency string

The frequency is:
1. Inferred from the DataFrame's DatetimeIndex using `pd.infer_freq()`
2. Passed through the prediction pipeline
3. Stored in `ForecastOutput.meta['freq']`
4. Used for horizon calculations, metrics, and plotting

## Usage Examples

### Basic Usage - Monthly Data

```python
from tsfm import Model, generator

# Generate monthly data (frequency auto-detected)
df = generator()

# Build and use any model
mdl = Model.build(name="moirai2")
yhs = mdl.pred(df, y="y", ctx_len=12, horizon=6, oos_start="2005-01-31")

# View metrics
yhs.summary()

# Plot forecasts (horizon=1 means next month)
yhs.plot_actual_vs_pred(horizon=1)
```

### Quarterly Data

```python
# Resample to quarterly (frequency auto-detected as 'Q')
df_quarterly = df.resample('Q').mean()

# Use the same interface - frequency is handled automatically
mdl = Model.build(name="chronos2")
yhs = mdl.pred(df_quarterly, y="y", ctx_len=4, horizon=2, oos_start="2005Q1")

# horizon=1 now means next quarter (not next 3 months!)
yhs.plot_actual_vs_pred(horizon=1)
```

### Available Models

```python
# Classical statistical model
mdl = Model.build(name="armodel")

# Foundation models from Salesforce
mdl = Model.build(name="moirai")   # Moirai 1.1
mdl = Model.build(name="moirai2")  # Moirai 2.0

# Foundation models from Amazon
mdl = Model.build(name="chronos")   # Chronos-Bolt
mdl = Model.build(name="chronos2")  # Chronos 2.0
```

### Accessing Metrics

```python
# Individual metrics
rmsfe = yhs.rmsfe  # Root Mean Squared Forecast Error by horizon
mae = yhs.mae      # Mean Absolute Error by horizon
me = yhs.me        # Mean Error by horizon

# Or use the metric method
yhs.metric("mae")

# Access raw predictions
yhs.df_preds  # MultiIndex DataFrame with y_true, y_pred, quantiles
```
