Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
beb8ead
scripts to extract tests
deven367 Jul 8, 2025
b029356
cleaned tests for `utils`
deven367 Jul 9, 2025
fd5407d
find `nbs` recursively
deven367 Jul 9, 2025
bc66a3a
extracted tests
deven367 Jul 9, 2025
44b4091
Merge branch 'main' into rm-nbdev
deven367 Jul 9, 2025
66b81e9
cleaned tests for `feature_engineering`
deven367 Jul 10, 2025
ff41ab4
cleaned tests for `ets`
deven367 Jul 10, 2025
14a4150
cleaned tests for `tbats`
deven367 Jul 11, 2025
6c91661
cleaned tests for `theta`
deven367 Jul 11, 2025
92490ce
partially cleaned tests for core
deven367 Jul 11, 2025
f129571
tests for `garch`
deven367 Jul 14, 2025
ac77624
cleaned tests for `core`
deven367 Jul 14, 2025
6dbc426
rename and clean
deven367 Jul 14, 2025
ac193f1
rename and clean
deven367 Jul 14, 2025
ec509c4
workflow for `pytest`
deven367 Jul 14, 2025
89e0a86
clone submodule to install from source
deven367 Jul 14, 2025
fc40da4
format tests for `mstl`
deven367 Jul 14, 2025
a2e8716
Merge branch 'main' into rm-nbdev
deven367 Jul 15, 2025
80c7ecf
add missing imports
deven367 Jul 15, 2025
def6dd1
rename and clean
deven367 Jul 15, 2025
c34ba8b
add missing imports
deven367 Jul 15, 2025
30035ba
Merge branch 'rm-nbdev' of https://github.com/Nixtla/statsforecast in…
deven367 Jul 15, 2025
c851e02
cleaned tests for `models`
deven367 Jul 17, 2025
2137a17
rename and clean tests for `prophet` adapter
deven367 Jul 18, 2025
2cb050a
clean remaining tests for ARIMA
deven367 Jul 18, 2025
4127531
clean remaining tests for CES
deven367 Jul 18, 2025
c12f37b
clean tests for `core`
deven367 Jul 18, 2025
51a3569
clean tests for `mfles`
deven367 Jul 18, 2025
c250124
clean tests for `mstl`
deven367 Jul 18, 2025
1a0ce31
clean tests for `tbats`
deven367 Jul 18, 2025
1a17072
missing import?
deven367 Jul 18, 2025
2f53a04
pytest mark skip tests for `ray`
deven367 Jul 24, 2025
9537370
skip test on py3.12+
deven367 Jul 24, 2025
dbfca17
conditional import
deven367 Jul 24, 2025
92c718f
skip tests in distributed_fugue
deven367 Jul 24, 2025
9827047
pinning pyarrow for `PyExtensionType` error
deven367 Jul 25, 2025
623179d
add tests for `garch` vs `arch`
deven367 Jul 30, 2025
f8bd26b
rm redundant print statements
deven367 Jul 30, 2025
18df258
clean `scripts` folder
deven367 Jul 31, 2025
949ba20
remove `initstate`
deven367 Aug 5, 2025
b6ddd57
merge test
deven367 Aug 12, 2025
52ed641
move common functionality to `conftest`
deven367 Aug 12, 2025
b686d91
move common logic to fixtures
deven367 Aug 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: pytest

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
all-tests:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps:
- name: Clone repo
uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
git submodule status
git submodule update --init --recursive
pip install uv && uv pip install --system ".[all]"

- name: Run pytest
run: pytest tests --ignore tests/test_distributed_fugue.py
618 changes: 618 additions & 0 deletions scripts/cli.ipynb

Large diffs are not rendered by default.

95 changes: 95 additions & 0 deletions scripts/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# AUTOGENERATED! DO NOT EDIT! File to edit: cli.ipynb.

# %% auto 0
__all__ = ['tst_flags', 'to_skip', 'mapper', 'print_execs', 'print_hide', 'other_tests', 'get_markdown', 'get_code',
'extract_dir', 'no_dir_and_dir', 'get_all_tests', 'print_dir_in_nb']

# %% cli.ipynb 1
from functools import partial

from fastcore.script import call_parse
from nbdev.processors import NBProcessor

# %% cli.ipynb 5
tst_flags = 'datasets distributed matplotlib polars pyarrow scipy'.split()
to_skip = [
'showdoc',
'load_ext',
'from nbdev'
]


def print_execs(cell):
if 'exec' in cell.source: print(cell.source)

def print_hide(cell):
if 'hide' in cell.directives_: print(cell.source)

def other_tests(cell):
if len(cell.directives_) == 0:
print(cell.source)

def get_markdown(cell):
if cell.cell_type == "markdown":
print(cell.source)

def get_code(cell):
if cell.cell_type == "code":
print(cell.source)

def extract_dir(cell, dirs):
for directive in dirs.split(','):
if directive in cell.directives_:
print(cell.source)

def no_dir_and_dir(cell, dir):
if len(cell.directives_) == 0:
print(cell.source)

if dir in cell.directives_:
print(cell.source)

def get_all_tests(cell):
if cell.cell_type == "code":

if len(cell.directives_) == 0:
print(cell.source)


elif any(x in tst_flags + ['hide'] for x in cell.directives_):
if not (x in cell.source for x in to_skip):
print(cell.source)


# %% cli.ipynb 9
mapper = {
'print_execs': print_execs,
'print_hide': print_hide,
'other_tests': other_tests,
'get_markdown': get_markdown,
'extract_dir': extract_dir,
'no_dir_and_dir': no_dir_and_dir,
'get_all_tests':get_all_tests
}

# %% cli.ipynb 10
@call_parse
def print_dir_in_nb(nb_path:str,
dir:str=None,
dir_name:str=None,
):
if dir_name not in mapper.keys():
raise ValueError(f'Choose processor from the the following: {mapper.keys()}')

if dir_name == 'extract_dir':
processor = NBProcessor(nb_path, partial(extract_dir, dir=dir))
processor.process()
return
elif dir_name == 'no_dir_and_dir':
processor = NBProcessor(nb_path, partial(no_dir_and_dir, dir=dir))
processor.process()
return

processor = NBProcessor(nb_path, mapper[dir_name])
processor.process()

2 changes: 2 additions & 0 deletions scripts/cvt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from nbdev.export import nb_export
nb_export('cli.ipynb', lib_path='.', name='cli')
40 changes: 40 additions & 0 deletions scripts/extract_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash

echo Python: $(which python)
source /Users/deven367/miniforge3/bin/activate
conda activate nixtla

echo Activated env: $(which python)


mkdir -p ../tests

tst_flags='datasets distributed matplotlib polars pyarrow scipy'

nbs=$(find ../nbs/src -type f -iname '*.ipynb')
# echo "Available notebooks: $nbs"


# this approach3 that was discussed on Slack
# mkdir -p ../tests3
# for flag in $tst_flags; do
# # echo "Extracting $flag"

# for nb in $nbs; do
# # get name of notebook without extension
# nb_name=$(basename "$nb" .ipynb)

# # echo "Processing notebook: $nb"
# # print_dir_in_nb "$nb" --dir_name no_dir_and_dir --dir "$flag" >> "../tests/test_$flag_$nb_name.py"
# print_dir_in_nb "$nb" --dir_name no_dir_and_dir --dir "$flag" >> "../tests3/test_${flag}_$nb_name.py"
# done
# done

for nb in $nbs; do
# get name of notebook without extension
nb_name=$(basename "$nb" .ipynb)

# echo "Processing notebook: $nb"
# print_dir_in_nb "$nb" --dir_name no_dir_and_dir --dir "$flag" >> "../tests/test_$flag_$nb_name.py"
python cli.py "$nb" --dir_name get_all_tests2 >> "../tests/test_$nb_name.py"
done
2 changes: 1 addition & 1 deletion settings.ini
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ray_requirements = fugue[ray]>=0.8.1 protobuf>=3.15.3,<4.0.0 numpy<2 pandas<2.2
dask_requirements = dask<=2024.12.1 fugue[dask]>=0.8.1
spark_requirements = fugue[spark]>=0.8.1
plotly_requirements = plotly plotly-resampler
dev_requirements = black datasetsforecast fastcore<=1.7.29 fire nbdev==2.3.25 nbformat nbdev_plotly pandas[plot] polars[numpy] pre-commit prophet pyarrow pybind11 pytest scikit-learn setuptools<70 supersmoother yfinance
dev_requirements = black datasetsforecast fastcore<=1.7.29 fire nbdev==2.3.25 nbformat nbdev_plotly pandas[plot] polars[numpy] pre-commit prophet pyarrow<21.0.0 pybind11 pytest scikit-learn setuptools<70 supersmoother yfinance
nbs_path = nbs
doc_path = _docs
recursive = True
Expand Down
18 changes: 18 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pandas as pd
import pytest
from statsforecast.utils import AirPassengers as ap


@pytest.fixture(scope="module")
def sample_data_prophet():
"""Load sample data for Prophet testing."""
df = pd.read_csv(
"https://raw.githubusercontent.com/facebook/prophet/main/examples/example_wp_log_peyton_manning.csv"
)
return df


@pytest.fixture
def air_passengers():
"""AirPassengers dataset."""
return ap
132 changes: 132 additions & 0 deletions tests/test_adapters_prophet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import warnings

import pandas as pd
import pytest
from statsforecast.adapters.prophet import Prophet

warnings.simplefilter(action="ignore", category=FutureWarning)


@pytest.fixture
Comment thread
deven367 marked this conversation as resolved.
Outdated
def prophet_model():
"""Create a Prophet model instance."""
return Prophet(daily_seasonality=False)


@pytest.fixture
def holidays_data():
"""Create sample holidays data."""
playoffs = pd.DataFrame(
{
"holiday": "playoff",
"ds": pd.to_datetime(
[
"2008-01-13",
"2009-01-03",
"2010-01-16",
"2010-01-24",
"2010-02-07",
"2011-01-08",
"2013-01-12",
"2014-01-12",
"2014-01-19",
"2014-02-02",
"2015-01-11",
"2016-01-17",
"2016-01-24",
"2016-02-07",
]
),
"lower_window": 0,
"upper_window": 1,
}
)
superbowls = pd.DataFrame(
{
"holiday": "superbowl",
"ds": pd.to_datetime(["2010-02-07", "2014-02-02", "2016-02-07"]),
"lower_window": 0,
"upper_window": 1,
}
)
return pd.concat((playoffs, superbowls))


def test_prophet_initialization():
"""Test Prophet model initialization."""
model = Prophet(daily_seasonality=False)
assert model is not None
assert hasattr(model, "fit")
assert hasattr(model, "predict")


def test_prophet_fit(sample_data_prophet, prophet_model):
"""Test Prophet model fitting."""
prophet_model.fit(sample_data_prophet)
# Check that the model has been fitted by verifying it has the necessary attributes
assert hasattr(prophet_model, "history")
assert prophet_model.history is not None


def test_prophet_predict(sample_data_prophet, prophet_model):
"""Test Prophet model prediction."""
prophet_model.fit(sample_data_prophet)
future = prophet_model.make_future_dataframe(365)
forecast = prophet_model.predict(future)

assert forecast is not None
assert isinstance(forecast, pd.DataFrame)
assert len(forecast) > len(sample_data_prophet)


def test_prophet_make_future_dataframe(sample_data_prophet, prophet_model):
"""Test making future dataframe."""
prophet_model.fit(sample_data_prophet)
future = prophet_model.make_future_dataframe(365)

assert isinstance(future, pd.DataFrame)
assert len(future) == len(sample_data_prophet) + 365
assert "ds" in future.columns


def test_prophet_plot(sample_data_prophet, prophet_model):
"""Test Prophet plotting functionality."""
prophet_model.fit(sample_data_prophet)
future = prophet_model.make_future_dataframe(365)
forecast = prophet_model.predict(future)

# Test that plot method exists and can be called
fig = prophet_model.plot(forecast)
assert fig is not None


def test_prophet_with_holidays(sample_data_prophet, holidays_data):
"""Test Prophet model with holidays."""
model = Prophet(holidays=holidays_data, daily_seasonality=False)
model.fit(sample_data_prophet)
future = model.make_future_dataframe(365)
forecast = model.predict(future)

assert forecast is not None
assert isinstance(forecast, pd.DataFrame)


def test_prophet_forecast_components(sample_data_prophet, prophet_model):
"""Test Prophet forecast components."""
prophet_model.fit(sample_data_prophet)
future = prophet_model.make_future_dataframe(10)
forecast = prophet_model.predict(future)

# Check that the forecast contains expected columns
expected_columns = ["yhat", "yhat_lower", "yhat_upper", "trend"]
for col in expected_columns:
assert col in forecast.columns, f"Expected column '{col}' not found in forecast"


def test_prophet_empty_dataframe():
"""Test Prophet behavior with empty dataframe."""
model = Prophet(daily_seasonality=False)
empty_df = pd.DataFrame(columns=["ds", "y"])

with pytest.raises(Exception): # Prophet should raise an error with empty data
model.fit(empty_df)
Loading
Loading