Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend unit tests #2640

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 47 additions & 0 deletions python/prophet/tests/test_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from prophet import Prophet, diagnostics

import logging

@pytest.fixture(scope="module")
def ts_short(daily_univariate_ts):
Expand Down Expand Up @@ -401,3 +402,49 @@ def test_prophet_copy_custom(self, data, backend):
assert (changepoints == m2.changepoints).all()
assert "custom" in m2.seasonalities
assert "binary_feature" in m2.extra_regressors


def test_cross_validation_dask_import_error(self, ts_short, backend, monkeypatch):
m = Prophet(stan_backend=backend)
m.fit(ts_short)

# Simulate dask not being installed
import sys
if 'dask' in sys.modules:
del sys.modules['dask']

with pytest.raises(ImportError, match="parallel='dask' requires the optional dependency dask."):
diagnostics.cross_validation(m, horizon="4 days", parallel="dask")


def test_cross_validation_min_cutoff_value_error(self, ts_short, backend):
m = Prophet(stan_backend=backend)
m.fit(ts_short)
cutoffs = [ts_short['ds'].min(), pd.Timestamp("2012-07-01")]
with pytest.raises(ValueError, match="Minimum cutoff value is not strictly greater than min date in history"):
diagnostics.cross_validation(m, horizon="4 days", cutoffs=cutoffs)


def test_cross_validation_initial_window_warning(self, ts_short, backend, caplog):
m = Prophet(stan_backend=backend)
m.add_seasonality(name="monthly", period=30.5, fourier_order=5)
m.fit(ts_short)
with caplog.at_level(logging.WARNING):
diagnostics.cross_validation(
m, horizon="4 days", period="10 days", initial="20 days"
)
assert any("Seasonality has period of" in message for message in caplog.messages)


def test_cross_validation_max_cutoff_value_error(self, ts_short, backend):
m = Prophet(stan_backend=backend)
m.fit(ts_short)
cutoffs = [pd.Timestamp("2012-12-31")]
with pytest.raises(ValueError, match="Maximum cutoff value is greater than end date minus horizon"):
diagnostics.cross_validation(m, horizon="4 days", cutoffs=cutoffs)


def test_cross_validation_unfit_model_exception(self):
m = Prophet()
with pytest.raises(Exception, match="Model has not been fit"):
diagnostics.cross_validation(m, horizon="4 days")
44 changes: 44 additions & 0 deletions python/prophet/tests/test_prophet.py
Original file line number Diff line number Diff line change
Expand Up @@ -982,3 +982,47 @@ def test_sampling_warm_start(self, daily_univariate_ts, backend):
daily_univariate_ts.iloc[:510], init=warm_start_params(m), show_progress=False
)
assert m2.params["delta"].shape == (200, 25)


def test_invalid_scaling_parameter(self, daily_univariate_ts, backend):
with pytest.raises(ValueError, match="scaling must be one of 'absmax' or 'minmax'"):
Prophet(scaling="invalid", stan_backend=backend).fit(daily_univariate_ts)


def test_holidays_missing_ds_column(self, daily_univariate_ts, backend):
holidays = pd.DataFrame({
"holiday": ["xmas"]
})
with pytest.raises(ValueError, match='holidays must be a DataFrame with "ds" and "holiday" columns.'):
Prophet(holidays=holidays, stan_backend=backend).fit(daily_univariate_ts)


def test_holidays_upper_window_less_than_zero(self, daily_univariate_ts, backend):
holidays = pd.DataFrame({
"ds": pd.to_datetime(["2016-12-25"]),
"holiday": ["xmas"],
"lower_window": [-1],
"upper_window": [-2]
})
with pytest.raises(ValueError, match="Holiday upper_window should be >= 0"):
Prophet(holidays=holidays, stan_backend=backend).fit(daily_univariate_ts)


def test_holidays_lower_window_greater_than_zero(self, daily_univariate_ts, backend):
holidays = pd.DataFrame({
"ds": pd.to_datetime(["2016-12-25"]),
"holiday": ["xmas"],
"lower_window": [1],
"upper_window": [0]
})
with pytest.raises(ValueError, match="Holiday lower_window should be <= 0"):
Prophet(holidays=holidays, stan_backend=backend).fit(daily_univariate_ts)


def test_holidays_with_nan(self, daily_univariate_ts, backend):
holidays = pd.DataFrame({
"ds": pd.to_datetime(["2016-12-25", None]),
"holiday": ["xmas", "xmas"]
})
with pytest.raises(ValueError, match="Found a NaN in holidays dataframe."):
Prophet(holidays=holidays, stan_backend=backend).fit(daily_univariate_ts)
20 changes: 20 additions & 0 deletions python/prophet/tests/test_serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,23 @@ def test_backwards_compatibility(self):
future = m.make_future_dataframe(10)
fcst = m.predict(future)
assert fcst["yhat"].values[-1] == pytest.approx(pred_val)


def test_fit_kwargs_conversion(self, daily_univariate_ts, backend):
from prophet.serialize import model_to_dict
m = Prophet(stan_backend=backend)
df = daily_univariate_ts.head(daily_univariate_ts.shape[0] - 30)
m.fit(df)
m.fit_kwargs['init'] = {'param1': np.array([1.0, 2.0]), 'param2': np.float64(3.0)}

model_dict = model_to_dict(m)

assert model_dict['fit_kwargs']['init']['param1'] == [1.0, 2.0]
assert model_dict['fit_kwargs']['init']['param2'] == 3.0


def test_model_to_dict_unfitted_model(self):
from prophet.serialize import model_to_dict
m = Prophet()
with pytest.raises(ValueError, match="This can only be used to serialize models that have already been fit."):
model_to_dict(m)
35 changes: 35 additions & 0 deletions python/prophet/tests/test_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from prophet import Prophet
from prophet.utilities import regressor_coefficients

from prophet.utilities import warm_start_params

class TestUtilities:
def test_regressor_coefficients(self, daily_univariate_ts, backend):
Expand All @@ -25,3 +26,37 @@ def test_regressor_coefficients(self, daily_univariate_ts, backend):
# No MCMC sampling, so lower and upper should be the same as mean
assert np.array_equal(coefs["coef_lower"].values, coefs["coef"].values)
assert np.array_equal(coefs["coef_upper"].values, coefs["coef"].values)

def test_warm_start_params_with_mcmc(self, daily_univariate_ts, backend):
m = Prophet(stan_backend=backend, mcmc_samples=10)
df = daily_univariate_ts.copy()
np.random.seed(123)
df["regr1"] = np.random.normal(size=df.shape[0])
m.add_regressor("regr1", mode="additive")
m.fit(df)

params = warm_start_params(m)
assert isinstance(params, dict)
assert "k" in params
assert "m" in params
assert "sigma_obs" in params
assert "delta" in params
assert "beta" in params


def test_warm_start_params_no_mcmc(self, daily_univariate_ts, backend):
m = Prophet(stan_backend=backend, mcmc_samples=0)
df = daily_univariate_ts.copy()
np.random.seed(123)
df["regr1"] = np.random.normal(size=df.shape[0])
m.add_regressor("regr1", mode="additive")
m.fit(df)

params = warm_start_params(m)
assert isinstance(params, dict)
assert "k" in params
assert "m" in params
assert "sigma_obs" in params
assert "delta" in params
assert "beta" in params