def test_prophet_hyperparameter_grid_exception(): """Tests prophet_hyperparameter_grid exceptions""" # unknown argument with pytest.raises(ValueError, match=r"Unexpected key\(s\) found"): model_components = ModelComponentsParam( seasonality={"unknown_seasonality": ["additive"]}) template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = model_components template.get_hyperparameter_grid() # regressor must be specified under `add_regressor_dict`, not directly with pytest.raises(ValueError, match=r"Unexpected key\(s\) found"): model_components = ModelComponentsParam( regressors={ "regressor1": { "prior_scale": 10, "standardize": True, "mode": "additive" }, "regressor2": { "prior_scale": 15, "standardize": False, "mode": "additive" }, "regressor3": {} }) template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = model_components template.get_hyperparameter_grid()
def test_prophet_hyperparameter_grid_seasonality_growth(default_holidays): """Tests get_hyperparameter_grid for basic seasonality, growth and other default params""" seasonality = {"yearly_seasonality": [True], "weekly_seasonality": [False]} growth = {"growth_term": ["linear"]} model_components = ModelComponentsParam(seasonality=seasonality, growth=growth) template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = model_components hyperparameter_grid = template.get_hyperparameter_grid() # Expected Values expected_holidays = default_holidays expected_grid = { "estimator__growth": ["linear"], "estimator__seasonality_mode": ["additive"], "estimator__seasonality_prior_scale": [10.0], "estimator__yearly_seasonality": [True], "estimator__weekly_seasonality": [False], "estimator__daily_seasonality": ["auto"], "estimator__add_seasonality_dict": [None], "estimator__holidays": [expected_holidays], "estimator__holidays_prior_scale": [10.0], "estimator__changepoint_prior_scale": [0.05], "estimator__changepoints": [None], "estimator__n_changepoints": [25], "estimator__changepoint_range": [0.8], "estimator__mcmc_samples": [0], "estimator__uncertainty_samples": [1000], "estimator__add_regressor_dict": [None] } # Assertions assert_equal(actual=hyperparameter_grid, expected=expected_grid)
def test_prophet_hyperparameter_grid_default(): """Tests get_hyperparameter_grid and apply_prophet_model_components_defaults""" template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() # both model_components, time_properties are None hyperparameter_grid = template.get_hyperparameter_grid() expected_holidays = template.get_prophet_holidays(year_list=list( range(2015 - 1, 2030 + 2)), countries="auto", lower_window=-2, upper_window=2) expected_grid = { "estimator__growth": ["linear"], "estimator__seasonality_mode": ["additive"], "estimator__seasonality_prior_scale": [10.0], "estimator__yearly_seasonality": ["auto"], "estimator__weekly_seasonality": ["auto"], "estimator__daily_seasonality": ["auto"], "estimator__add_seasonality_dict": [None], "estimator__holidays": [expected_holidays], "estimator__holidays_prior_scale": [10.0], "estimator__changepoint_prior_scale": [0.05], "estimator__changepoints": [None], "estimator__n_changepoints": [25], "estimator__changepoint_range": [0.8], "estimator__mcmc_samples": [0], "estimator__uncertainty_samples": [1000], "estimator__add_regressor_dict": [None] } assert_equal(actual=hyperparameter_grid, expected=expected_grid)
def test_prophet_hyperparameter_grid_events(): """Tests get_prophet_hyperparameter_grid for selected Countries" holidays""" # holiday params start_year = 2018 end_year = 2022 holiday_pre_num_days = [1] holiday_post_num_days = [1] holiday_lookup_countries = ["UnitedStates", "China", "India"] holidays_prior_scale = [5.0, 10.0, 15.0] events = { "holiday_lookup_countries": holiday_lookup_countries, "holiday_pre_num_days": holiday_pre_num_days, "holiday_post_num_days": holiday_post_num_days, "start_year": start_year, "end_year": end_year, "holidays_prior_scale": holidays_prior_scale } model_components = ModelComponentsParam(events=events) template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = model_components hyperparameter_grid = template.get_hyperparameter_grid() # Expected Values # Holidays df, based on given holidays params expected_holidays = template.get_prophet_holidays( year_list=list(range(start_year - 1, end_year + 2)), countries=holiday_lookup_countries, lower_window=-holiday_pre_num_days[0], upper_window=holiday_post_num_days[0]) expected_grid = { "estimator__growth": ["linear"], "estimator__seasonality_mode": ["additive"], "estimator__seasonality_prior_scale": [10.0], "estimator__yearly_seasonality": ["auto"], "estimator__weekly_seasonality": ["auto"], "estimator__daily_seasonality": ["auto"], "estimator__add_seasonality_dict": [None], "estimator__holidays": [expected_holidays], "estimator__holidays_prior_scale": [5.0, 10.0, 15.0], "estimator__changepoint_prior_scale": [0.05], "estimator__changepoints": [None], "estimator__n_changepoints": [25], "estimator__changepoint_range": [0.8], "estimator__mcmc_samples": [0], "estimator__uncertainty_samples": [1000], "estimator__add_regressor_dict": [None] } # Assertions assert_equal(actual=hyperparameter_grid, expected=expected_grid)
def test_hyperparameter_override(default_holidays): """Tests the hyperparameter_override functionality. Use hyperparameter_override to override parameters and create multiple sets of grids. """ model_components = ModelComponentsParam( seasonality={ "yearly_seasonality": [True, False], "weekly_seasonality": False, }, hyperparameter_override=[{ "input__response__null__max_frac": 0.1, "estimator__yearly_seasonality": True, "estimator__growth": ["logistic"], }, {}]) template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = model_components hyperparameter_grid = template.get_hyperparameter_grid() expected_grid = { "estimator__growth": ["linear"], "estimator__seasonality_mode": ["additive"], "estimator__seasonality_prior_scale": [10.0], "estimator__yearly_seasonality": [True, False], "estimator__weekly_seasonality": [False], "estimator__daily_seasonality": ["auto"], "estimator__add_seasonality_dict": [None], "estimator__holidays": [default_holidays], "estimator__holidays_prior_scale": [10.0], "estimator__changepoint_prior_scale": [0.05], "estimator__changepoints": [None], "estimator__n_changepoints": [25], "estimator__changepoint_range": [0.8], "estimator__mcmc_samples": [0], "estimator__uncertainty_samples": [1000], "estimator__add_regressor_dict": [None] } updated_grid = expected_grid.copy() updated_grid["input__response__null__max_frac"] = [0.1] updated_grid["estimator__yearly_seasonality"] = [True] updated_grid["estimator__growth"] = ["logistic"] assert_equal(hyperparameter_grid, [updated_grid, expected_grid])
def test_prophet_hyperparameter_grid_auto_list(default_holidays): """Tests `get_prophet_hyperparameter_grid` automatic list conversion via `dictionaries_values_to_lists`. Holidays are tested separately because they are not directly passed to ProphetEstimator.""" growth = {"growth_term": "linear"} seasonality = { "seasonality_mode": "multiplicative", "seasonality_prior_scale": 10.0, "yearly_seasonality": False, "weekly_seasonality": False, "daily_seasonality": True, "add_seasonality_dict": { "yearly": { "period": 365.25, "fourier_order": 20, "prior_scale": 20.0 }, "quarterly": { "period": 365.25 / 4, "fourier_order": 15 }, "weekly": { "period": 7, "fourier_order": 35, "prior_scale": 30.0 } } } changepoints = { "changepoint_prior_scale": 0.05, "changepoints": ["2018-10-11", "2018-11-11", "2019-01-17"], "n_changepoints": 25, "changepoint_range": 0.8 } uncertainty = {"mcmc_samples": 0, "uncertainty_samples": 1000} regressors = { "add_regressor_dict": { "reg_col1": { "prior_scale": 10.0, "standardize": False, "mode": "additive" }, "reg_col2": { "prior_scale": 20.0, "standardize": True, "mode": "multiplicative" } } } model_components = ModelComponentsParam(growth=growth, seasonality=seasonality, changepoints=changepoints, regressors=regressors, uncertainty=uncertainty) template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = model_components hyperparameter_grid = template.get_hyperparameter_grid() # Expected Values expected_grid = { "estimator__growth": ["linear"], "estimator__seasonality_mode": ["multiplicative"], "estimator__seasonality_prior_scale": [10.0], "estimator__yearly_seasonality": [False], "estimator__weekly_seasonality": [False], "estimator__daily_seasonality": [True], "estimator__add_seasonality_dict": [seasonality["add_seasonality_dict"]], "estimator__holidays": [default_holidays], "estimator__holidays_prior_scale": [10.0], "estimator__changepoint_prior_scale": [0.05], "estimator__changepoints": [["2018-10-11", "2018-11-11", "2019-01-17"]], "estimator__n_changepoints": [25], "estimator__changepoint_range": [0.8], "estimator__mcmc_samples": [0], "estimator__uncertainty_samples": [1000], "estimator__add_regressor_dict": [{ "reg_col1": { "prior_scale": 10.0, "standardize": False, "mode": "additive" }, "reg_col2": { "prior_scale": 20.0, "standardize": True, "mode": "multiplicative" } }] } assert_equal(actual=hyperparameter_grid, expected=expected_grid)
def test_prophet_hyperparameter_grid_changepoints_uncertainty_custom( default_holidays): """Tests get_prophet_hyperparameter_grid for selected changepoints, regressor, and uncertainty""" changepoints = { "changepoint_prior_scale": [0.05, 1.0, 5.0, 10.0, 15.0], "changepoints": [None, ["2018-10-11", "2018-11-11", "2019-01-17"]], "n_changepoints": [25, 50, 100], "changepoint_range": [0.8, 0.9] } uncertainty = { "mcmc_samples": [0, 1000], "uncertainty_samples": [1000, 2000] } regressors = { "add_regressor_dict": [{ "reg_col1": { "prior_scale": 10.0, "standardize": False, "mode": "additive" }, "reg_col2": { "prior_scale": 20.0, "standardize": True, "mode": "multiplicative" } }, { "reg_col1": { "prior_scale": 20.0, "standardize": True, "mode": "additive" }, "reg_col2": { "prior_scale": 40.0, "standardize": False, "mode": "multiplicative" } }] } model_components = ModelComponentsParam(changepoints=changepoints, regressors=regressors, uncertainty=uncertainty) template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = model_components hyperparameter_grid = template.get_hyperparameter_grid() # Expected Values expected_holidays = default_holidays expected_grid = { "estimator__growth": ["linear"], "estimator__seasonality_mode": ["additive"], "estimator__seasonality_prior_scale": [10.0], "estimator__yearly_seasonality": ["auto"], "estimator__weekly_seasonality": ["auto"], "estimator__daily_seasonality": ["auto"], "estimator__add_seasonality_dict": [None], "estimator__holidays": [expected_holidays], "estimator__holidays_prior_scale": [10.0], "estimator__changepoint_prior_scale": [0.05, 1.0, 5.0, 10.0, 15.0], "estimator__changepoints": [None, ["2018-10-11", "2018-11-11", "2019-01-17"]], "estimator__n_changepoints": [25, 50, 100], "estimator__changepoint_range": [0.8, 0.9], "estimator__mcmc_samples": [0, 1000], "estimator__uncertainty_samples": [1000, 2000], "estimator__add_regressor_dict": [{ "reg_col1": { "prior_scale": 10.0, "standardize": False, "mode": "additive" }, "reg_col2": { "prior_scale": 20.0, "standardize": True, "mode": "multiplicative" } }, { "reg_col1": { "prior_scale": 20.0, "standardize": True, "mode": "additive" }, "reg_col2": { "prior_scale": 40.0, "standardize": False, "mode": "multiplicative" } }] } assert_equal(actual=hyperparameter_grid, expected=expected_grid)
def test_prophet_hyperparameter_grid_custom_seasonality(default_holidays): """Tests get_prophet_hyperparameter_grid for custom seasonality params, other params being defaults""" seasonality = { "seasonality_mode": ["additive", "multiplicative"], "seasonality_prior_scale": [5.0, 10.0, 15.0], "yearly_seasonality": [True, False], "weekly_seasonality": [True, False], "daily_seasonality": [True, False], "add_seasonality_dict": [{ "yearly": { "period": 365.25, "fourier_order": 20, "prior_scale": 20.0 }, "quarterly": { "period": 365.25 / 4, "fourier_order": 15 }, "weekly": { "period": 7, "fourier_order": 35, "prior_scale": 30.0 } }, { "yearly": { "period": 365.25, "fourier_order": 10, "prior_scale": 20.0 }, "quarterly": { "period": 365.25 / 4, "fourier_order": 3 }, "weekly": { "period": 7, "fourier_order": 5, "prior_scale": 20.0 } }, { "yearly": { "period": 365.25, "fourier_order": 10, "prior_scale": 30.0 }, "quarterly": { "period": 365.25 / 4, "fourier_order": 5 }, "weekly": { "period": 7, "fourier_order": 15, "prior_scale": 20.0 } }, { "yearly": { "period": 365.25, "fourier_order": 15, "prior_scale": 20.0 }, "quarterly": { "period": 365.25 / 4, "fourier_order": 10 }, "weekly": { "period": 7, "fourier_order": 25, "prior_scale": 20.0 } }] } model_components = ModelComponentsParam(seasonality=seasonality) template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = model_components hyperparameter_grid = template.get_hyperparameter_grid() # Expected Values expected_holidays = default_holidays expected_grid = { "estimator__growth": ["linear"], "estimator__seasonality_mode": ["additive", "multiplicative"], "estimator__seasonality_prior_scale": [5.0, 10.0, 15.0], "estimator__yearly_seasonality": [True, False], "estimator__weekly_seasonality": [True, False], "estimator__daily_seasonality": [True, False], "estimator__add_seasonality_dict": [{ "yearly": { "period": 365.25, "fourier_order": 20, "prior_scale": 20.0 }, "quarterly": { "period": 365.25 / 4, "fourier_order": 15 }, "weekly": { "period": 7, "fourier_order": 35, "prior_scale": 30.0 } }, { "yearly": { "period": 365.25, "fourier_order": 10, "prior_scale": 20.0 }, "quarterly": { "period": 365.25 / 4, "fourier_order": 3 }, "weekly": { "period": 7, "fourier_order": 5, "prior_scale": 20.0 } }, { "yearly": { "period": 365.25, "fourier_order": 10, "prior_scale": 30.0 }, "quarterly": { "period": 365.25 / 4, "fourier_order": 5 }, "weekly": { "period": 7, "fourier_order": 15, "prior_scale": 20.0 } }, { "yearly": { "period": 365.25, "fourier_order": 15, "prior_scale": 20.0 }, "quarterly": { "period": 365.25 / 4, "fourier_order": 10 }, "weekly": { "period": 7, "fourier_order": 25, "prior_scale": 20.0 } }], "estimator__holidays": [expected_holidays], "estimator__holidays_prior_scale": [10.0], "estimator__changepoint_prior_scale": [0.05], "estimator__changepoints": [None], "estimator__n_changepoints": [25], "estimator__changepoint_range": [0.8], "estimator__mcmc_samples": [0], "estimator__uncertainty_samples": [1000], "estimator__add_regressor_dict": [None] } # Assertions assert_equal(actual=hyperparameter_grid, expected=expected_grid)
def test_prophet_hyperparameter_grid_warn(): """Tests get_prophet_hyperparameter_grid warnings""" # holiday params start_year = 2018 end_year = 2022 holiday_pre_num_days = [1, 2, 3, 4] holiday_post_num_days = [1, 2, 3, 4] holiday_lookup_countries = [["UnitedStates"], ["UnitedStates", "China", "India"]] holidays_prior_scale = [5.0, 10.0, 15.0] events = { "holiday_lookup_countries": holiday_lookup_countries, "holiday_pre_num_days": holiday_pre_num_days, "holiday_post_num_days": holiday_post_num_days, "start_year": start_year, "end_year": end_year, "holidays_prior_scale": holidays_prior_scale } model_components = ModelComponentsParam(events=events) with pytest.warns(Warning) as record: template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = model_components template.get_hyperparameter_grid() assert f"`events['holiday_pre_num_days']` list has more than 1 element. "\ f"We currently support only 1 element. "\ f"Using 1." in record[0].message.args[0] assert f"`events['holiday_post_num_days']` list has more than 1 element. " \ f"We currently support only 1 element. " \ f"Using 1." in record[1].message.args[0] # Extra spaces for holidays to align with actual warning in the function, because of an extra tab. assert f"`events['holiday_lookup_countries']` contains multiple options. "\ f"We currently support only 1 option. Using ['UnitedStates']." in record[2].message.args[0] # other invalid holiday_lookup_countries with pytest.warns(Warning) as record: events["holiday_pre_num_days"] = [1] events["holiday_post_num_days"] = [0] events["holiday_lookup_countries"] = ["auto", None] model_components = ModelComponentsParam(events=events) template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = model_components template.get_hyperparameter_grid() assert f"`events['holiday_lookup_countries']` contains multiple options. " \ f"We currently support only 1 option. Using auto." in record[0].message.args[0] # no warning if only one list of holiday_lookup_countries is provided with pytest.warns(None): events["holiday_pre_num_days"] = [1] events["holiday_post_num_days"] = [0] events["holiday_lookup_countries"] = [[ "UnitedStates", "China", "UnitedKingdom", "India" ]] template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = ModelComponentsParam( events=events) hyp1 = template.get_hyperparameter_grid() events["holiday_lookup_countries"] = [ "UnitedStates", "China", "UnitedKingdom", "India" ] template = ProphetTemplate() template.config = template.apply_forecast_config_defaults() template.config.model_components_param = ModelComponentsParam( events=events) hyp2 = template.get_hyperparameter_grid() assert_equal(hyp1, hyp2)
def test_get_regressor_cols(): """Tests get_regressor_names""" # `add_regressor_dict` is a list of dict template = ProphetTemplate() model_components = ModelComponentsParam( regressors={ "add_regressor_dict": [{ "regressor1": { "prior_scale": 10, "standardize": True, "mode": "additive" }, "regressor2": { "prior_scale": 15, "standardize": False, "mode": "additive" }, "regressor3": {} }, None, { "regressor1": { "prior_scale": 10, "standardize": True, "mode": "additive" }, "regressor4": { "prior_scale": 15, "standardize": False, "mode": "additive" }, "regressor5": {} }] }) template.config = ForecastConfig(model_components_param=model_components) assert set(template.get_regressor_cols()) == { "regressor1", "regressor2", "regressor3", "regressor4", "regressor5" } # `add_regressor_dict` is a single dict model_components = ModelComponentsParam( regressors={ "add_regressor_dict": { "regressor1": { "prior_scale": 10, "standardize": True, "mode": "additive" }, "regressor2": { "prior_scale": 15, "standardize": False, "mode": "additive" }, "regressor3": {} } }) template.config = ForecastConfig(model_components_param=model_components) assert set(template.get_regressor_cols()) == { "regressor1", "regressor2", "regressor3" } # no regressors model_components = ModelComponentsParam() template.config = ForecastConfig(model_components_param=model_components) assert template.get_regressor_cols() is None model_components = ModelComponentsParam(regressors={}) template.config = ForecastConfig(model_components_param=model_components) assert template.get_regressor_cols() is None model_components = ModelComponentsParam( regressors={"add_regressor_dict": []}) template.config = ForecastConfig(model_components_param=model_components) assert template.get_regressor_cols() is None model_components = ModelComponentsParam( regressors={"add_regressor_dict": [{}, None]}) template.config = ForecastConfig(model_components_param=model_components) assert template.get_regressor_cols() is None