Metadata-Version: 2.4
Name: hybridts
Version: 0.3.0
Summary: Hybrid time series forecasting library combining Prophet with gradient boosting models
Author-email: Davi Franco <alcanfordavi@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/davifrancamaciel/hybridts
Project-URL: Repository, https://github.com/davifrancamaciel/hybridts
Project-URL: Documentation, https://github.com/davifrancamaciel/hybridts#readme
Project-URL: Bug Tracker, https://github.com/davifrancamaciel/hybridts/issues
Project-URL: Changelog, https://github.com/davifrancamaciel/hybridts/blob/main/CHANGELOG.md
Keywords: time-series,forecasting,prophet,xgboost,lightgbm,hybrid-models,machine-learning,ensemble,prediction
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pandas>=2.0.0
Requires-Dist: numpy>=1.24.0
Requires-Dist: scikit-learn>=1.3.0
Requires-Dist: prophet>=1.1.0
Requires-Dist: xgboost>=2.0.0
Requires-Dist: lightgbm>=4.0.0
Requires-Dist: sktime>=0.13.0
Requires-Dist: holidays>=0.34
Requires-Dist: python-dateutil>=2.8.0
Requires-Dist: loguru>=0.7.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=3.0.0; extra == "dev"
Requires-Dist: black>=22.0.0; extra == "dev"
Requires-Dist: isort>=5.10.0; extra == "dev"
Requires-Dist: flake8>=4.0.0; extra == "dev"
Requires-Dist: mypy>=0.950; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx>=4.5.0; extra == "docs"
Requires-Dist: sphinx-rtd-theme>=1.0.0; extra == "docs"
Provides-Extra: all
Requires-Dist: hybridts[dev,docs]; extra == "all"
Dynamic: license-file

# HybridTS

![HybridTS Logo](docs/logo_v1.png)

Hybrid Time Series Forecasting for Python

Combines Prophet's trend and seasonality detection with gradient boosting (XGBoost / LightGBM) to correct residuals — delivering more accurate forecasts than either model alone.

[![Python Version](https://img.shields.io/badge/python-3.8%2B-blue)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
[![Version](https://img.shields.io/badge/version-0.3.0-orange)](https://github.com/DaviAlcanfor/hybridts)
[![Tests](https://github.com/DaviAlcanfor/hybridts/actions/workflows/tests.yml/badge.svg)](https://github.com/DaviAlcanfor/hybridts/actions/workflows/tests.yml)

---

## How It Works

1. **Prophet** fits trend, seasonality, and holiday effects
2. **XGBoost / LightGBM** learns from the residuals using engineered features
3. **Final forecast** = Prophet baseline + ML residual correction

---

## Installation

```bash
pip install hybridts
```

---

## Quick Start

```python
from hybridts import HybridForecaster, ProphetModel, XGBoostTuner
import pandas as pd

prophet = ProphetModel()
xgb = XGBoostTuner()

forecaster = HybridForecaster(primary_model=prophet, secondary_model=xgb)

df = pd.read_csv("data.csv", parse_dates=["ds"])  # columns: ds, y
forecaster.fit(df)
forecast = forecaster.predict(horizon=30)
print(forecast)
```

---

## Data Format

A pandas DataFrame with exactly two columns:

| Column | Type | Description |
|--------|------|-------------|
| `ds` | datetime | Date of observation |
| `y` | float | Value to forecast |

---

## Core API

### `HybridForecaster`

```python
HybridForecaster(
    primary_model,           # ProphetModel instance
    secondary_model,         # XGBoostTuner or LightGBMTuner instance
    test_size=30,            # holdout size for evaluate()
    paydays_set=None,        # set of payday Timestamps (auto-generated if None)
    holidays_country="BR",   # country code for auto-generated holidays
    holidays_state=None,     # state/subdivision code
)
```

#### `fit(df, holidays=<auto>, features=<auto>)`

Trains both models. By default, holidays and features are auto-generated from the data.

```python
forecaster.fit(df)                        # auto holidays + auto features
forecaster.fit(df, holidays=None)         # no holidays passed to Prophet
forecaster.fit(df, holidays=my_holidays)  # custom holidays DataFrame
forecaster.fit(df, features=None)         # secondary model trains without exogenous features
```

#### `predict(horizon, features=<auto>)`

Returns a DataFrame with `horizon` rows:

| Column                  | Description            |
| ----------------------- | ---------------------- |
| `data`                  | Forecast date          |
| `forecast_primary_base` | Prophet baseline       |
| `residual_correction`   | ML adjustment          |
| `forecast_final`        | Final hybrid forecast  |

#### `evaluate(df, test_size=None)`

Evaluates on a holdout set without data leakage. Returns `(metrics, y_true, y_pred)`.

```python
metrics, y_true, y_pred = forecaster.evaluate(df)

# Access full metrics reports after evaluate()
forecaster.metrics_report_          # ForecastMetrics for the hybrid forecast
forecaster.primary_metrics_report_  # ForecastMetrics for the primary model alone
```

#### `evaluate_and_fit(df, test_size=None)`

Evaluates on holdout, then retrains on the full dataset.

```python
forecaster, metrics = forecaster.evaluate_and_fit(df)
```

---

## Models

### `ProphetModel`

Wraps Prophet with automated hyperparameter tuning via cross-validation.

```python
ProphetModel(
    param_grid={"changepoint_prior_scale": [0.05, 0.1], ...},  # grid search params
    cv_params={"initial": "300 days", "period": "30 days", "horizon": "30 days"},
    yearly_seasonality=True,
    weekly_seasonality=True,
    daily_seasonality=False,
    static_params={...},   # used by fit_static() — skips CV
)
```

| Method                     | Description                                          |
| -------------------------- | ---------------------------------------------------- |
| `fit(df, holidays)`        | Tunes hyperparameters via CV, then fits on full data |
| `fit_static(df, holidays)` | Fits with `static_params`, no CV                     |
| `predict(df)`              | Returns Prophet forecast DataFrame                   |

### `XGBoostTuner`

Wraps XGBoost via sktime's `make_reduction` with grid search and expanding window CV.

```python
XGBoostTuner(
    test_size=30,
    param_grid={"window_length": [21], "estimator__max_depth": [5, 7], ...},
    static_params={"n_estimators": 200, "max_depth": 5, ...},
    cv_initial_window=270,
    cv_step_length=30,
    window_length=21,
    fh=30,
    strategy="recursive",
    regressor_params={"random_state": 42},
)
```

### `LightGBMTuner`

Same interface as `XGBoostTuner`, using LightGBM as the estimator.

```python
LightGBMTuner(
    lgbm_regressor_params={"strategy": "recursive", "n_estimators": 200, ...},
    test_size=30,
    initial_window=270,
    step_length=30,
    window_length=21,
    param_grid={"window_length": [21, 28], ...},
)
```

Both models expose `fit()`, `fit_static()`, and `predict()`.

---

## Feature Engineering

```python
from hybridts import create_features, get_brazilian_paydays, create_holidays_prophet

# Generate Brazilian payday dates
paydays = get_brazilian_paydays(start_year=2022, end_year=2025)

# Create exogenous features
features = create_features(
    df_dates=df[["ds"]],
    paydays_set=paydays,       # optional — all payday features zeroed if None
    min_year=2022,
    max_year=2025,
    holidays_country="BR",
    holidays_state="SP",
)
```

Generated features:

| Feature                  | Description                    |
| ------------------------ | ------------------------------ |
| `is_weekend`             | 1 if Saturday or Sunday        |
| `is_month_start`         | 1 if day <= 9                  |
| `is_month_end`           | 1 if last day of month         |
| `day_of_week`            | 0 (Mon) to 6 (Sun)             |
| `day_of_month`           | 1-31                           |
| `is_payday`              | 1 if date is a payday          |
| `is_adiantamento`        | 1 if salary advance day        |
| `sextou_com_dinheiro`    | 1 if Friday and payday         |
| `dias_desde_pagamento`   | Days since last payday         |
| `is_holiday`             | 1 if public holiday            |
| `is_holiday_eve`         | 1 if day before holiday        |
| `is_post_holiday`        | 1 if day after holiday         |

---

## Metrics

After `evaluate()`, two `ForecastMetrics` reports are available on the forecaster instance.

```python
from hybridts.src.metrics import ForecastMetrics

report = forecaster.metrics_report_

report.mae        # Mean Absolute Error
report.mse        # Mean Squared Error
report.rmse       # Root Mean Squared Error
report.mape       # Mean Absolute Percentage Error (%)
report.smape      # Symmetric MAPE (%)
report.r_squared  # Coefficient of determination
report.bias       # Mean signed error (+ = underestimation, - = overestimation)

print(report.summary())
# Forecast Metrics Summary:
# MAE: 142.3401
# MSE: 32401.2000
# ...

report.all_metrics()  # returns a dict with all metrics
```

`ForecastMetrics` can also be used standalone on any `y_true` / `y_pred` pair:

```python
report = ForecastMetrics(y_true, y_pred)
print(report.summary())
```

---

## Logging

HybridTS uses [loguru](https://github.com/Delgan/loguru) with logging disabled by default (library-safe). To enable:

```python
from loguru import logger
logger.enable("hybridts")
```

---

## Project Structure

```text
hybridts/
├── __init__.py                  # public API
└── src/
    ├── features/
    │   ├── data_processor.py    # TimeSeriesProcessor
    │   ├── engineering.py       # create_features
    │   └── holidays.py          # create_holidays_prophet, get_brazilian_paydays
    ├── models/
    │   ├── primary/
    │   │   └── prophet.py       # ProphetModel
    │   └── secondary/
    │       ├── xgboost.py       # XGBoostTuner
    │       └── lightgbm.py      # LightGBMTuner
    ├── metrics/
    │   └── forecast.py          # ForecastMetrics
    ├── pipeline/
    │   └── pipeline.py          # HybridForecaster
    └── exception/
        ├── model_exception.py   # ModelTrainingException, ModelPredictionException
        └── dataframe_exception.py
```

---

## License

MIT — see [LICENSE](LICENSE)

---

**Davi Franco** — [GitHub](https://github.com/DaviAlcanfor)
