예제 #1
0
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)
예제 #2
0
def test_apply_template_decorator():
    data = generate_df_for_tests(freq="D", periods=10)
    df = data["df"]
    template = ProphetTemplate()
    with pytest.raises(
            ValueError,
            match=
            "ProphetTemplate only supports config.model_template='PROPHET', found 'UNKNOWN'"
    ):
        template.apply_template_for_pipeline_params(
            df=df, config=ForecastConfig(model_template="UNKNOWN"))
예제 #3
0
def test_prophet_template_default():
    """Tests prophet_template with default values, for limited data"""
    # prepares input data
    num_days = 10
    data = generate_df_for_tests(freq="D",
                                 periods=num_days,
                                 train_start_date="2018-01-01")
    df = data["df"]
    template = ProphetTemplate()
    config = ForecastConfig(model_template="PROPHET")
    params = template.apply_template_for_pipeline_params(df=df, config=config)
    # not modified
    assert config == ForecastConfig(model_template="PROPHET")
    # checks result
    metric = EvaluationMetricEnum.MeanAbsolutePercentError
    pipeline = params.pop("pipeline", None)
    expected_params = dict(
        df=df,
        time_col=cst.TIME_COL,
        value_col=cst.VALUE_COL,
        date_format=None,
        freq=None,
        train_end_date=None,
        anomaly_info=None,
        # model
        regressor_cols=None,
        lagged_regressor_cols=None,
        estimator=None,
        hyperparameter_grid=template.hyperparameter_grid,
        hyperparameter_budget=None,
        n_jobs=COMPUTATION_N_JOBS,
        verbose=1,
        # forecast
        forecast_horizon=None,
        coverage=None,
        test_horizon=None,
        periods_between_train_test=None,
        agg_periods=None,
        agg_func=None,
        # evaluation
        score_func=metric.name,
        score_func_greater_is_better=metric.get_metric_greater_is_better(),
        cv_report_metrics=CV_REPORT_METRICS_ALL,
        null_model_params=None,
        relative_error_tolerance=None,
        # CV
        cv_horizon=None,
        cv_min_train_periods=None,
        cv_expanding_window=True,
        cv_periods_between_splits=None,
        cv_periods_between_train_test=None,
        cv_max_splits=3)
    assert_basic_pipeline_equal(pipeline, template.pipeline)
    assert_equal(params, expected_params)
예제 #4
0
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])
예제 #5
0
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)
예제 #6
0
def default_holidays():
    """Default holidays by country params"""
    start_year = 2015
    end_year = 2030
    holiday_pre_num_days = [2]
    holiday_post_num_days = [2]
    expected_holidays = ProphetTemplate().get_prophet_holidays(
        year_list=list(range(start_year - 1, end_year +
                             2)),  # covers one more year on both sides.
        countries="auto",
        lower_window=-holiday_pre_num_days[0],
        upper_window=holiday_post_num_days[0])
    return expected_holidays
예제 #7
0
def test_property():
    """Tests properties"""
    assert ProphetTemplate().allow_model_template_list is False
    assert ProphetTemplate().allow_model_components_param_list is False

    template = ProphetTemplate()
    assert template.DEFAULT_MODEL_TEMPLATE == "PROPHET"
    assert isinstance(template.estimator, ProphetEstimator)
    assert template.estimator.coverage == 0.80
    assert template.apply_forecast_config_defaults(
    ).model_template == "PROPHET"

    estimator = ProphetEstimator(coverage=0.99)
    template = ProphetTemplate(estimator=estimator)
    assert template.estimator is estimator
예제 #8
0
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)
예제 #9
0
def test_prophet_template_custom():
    """Tests prophet_template with custom values, with long range input"""
    # prepares input data
    data = generate_df_with_reg_for_tests(freq="H",
                                          periods=300 * 24,
                                          remove_extra_cols=True,
                                          mask_test_actuals=True)
    df = data["df"]
    time_col = "some_time_col"
    value_col = "some_value_col"
    df.rename({
        cst.TIME_COL: time_col,
        cst.VALUE_COL: value_col
    },
              axis=1,
              inplace=True)
    # prepares params and calls template
    metric = EvaluationMetricEnum.MeanAbsoluteError
    # anomaly adjustment adds 10.0 to every record
    adjustment_size = 10.0
    anomaly_df = pd.DataFrame({
        cst.START_DATE_COL: [df[time_col].min()],
        cst.END_DATE_COL: [df[time_col].max()],
        cst.ADJUSTMENT_DELTA_COL: [adjustment_size],
        cst.METRIC_COL: [value_col]
    })
    anomaly_info = {
        "value_col": cst.VALUE_COL,
        "anomaly_df": anomaly_df,
        "start_date_col": cst.START_DATE_COL,
        "end_date_col": cst.END_DATE_COL,
        "adjustment_delta_col": cst.ADJUSTMENT_DELTA_COL,
        "filter_by_dict": {
            cst.METRIC_COL: cst.VALUE_COL
        },
        "adjustment_method": "add"
    }
    metadata = MetadataParam(
        time_col=time_col,
        value_col=value_col,
        freq="H",
        date_format="%Y-%m-%d-%H",
        train_end_date=datetime.datetime(2019, 7, 1),
        anomaly_info=anomaly_info,
    )
    evaluation_metric = EvaluationMetricParam(
        cv_selection_metric=metric.name,
        cv_report_metrics=[
            EvaluationMetricEnum.MedianAbsolutePercentError.name
        ],
        agg_periods=24,
        agg_func=np.max,
        null_model_params={
            "strategy": "quantile",
            "constant": None,
            "quantile": 0.8
        },
        relative_error_tolerance=0.01)
    evaluation_period = EvaluationPeriodParam(test_horizon=1,
                                              periods_between_train_test=2,
                                              cv_horizon=3,
                                              cv_min_train_periods=4,
                                              cv_expanding_window=True,
                                              cv_periods_between_splits=5,
                                              cv_periods_between_train_test=6,
                                              cv_max_splits=7)
    model_components = ModelComponentsParam(
        seasonality={
            "yearly_seasonality": [True],
            "weekly_seasonality": [False],
            "daily_seasonality": [4],
            "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
                }
            }]
        },
        growth={"growth_term": "linear"},
        events={
            "holiday_lookup_countries":
            ["UnitedStates", "UnitedKingdom", "India"],
            "holiday_pre_num_days": [2],
            "holiday_post_num_days": [3],
            "holidays_prior_scale": [5.0]
        },
        regressors={
            "add_regressor_dict": [{
                "regressor1": {
                    "prior_scale": 10.0,
                    "mode": 'additive'
                },
                "regressor2": {
                    "prior_scale": 20.0,
                    "mode": 'multiplicative'
                },
            }]
        },
        changepoints={
            "changepoint_prior_scale": [0.05],
            "changepoints": [None],
            "n_changepoints": [50],
            "changepoint_range": [0.9]
        },
        uncertainty={
            "mcmc_samples": [500],
            "uncertainty_samples": [2000]
        },
        hyperparameter_override={
            "input__response__null__impute_algorithm":
            "ts_interpolate",
            "input__response__null__impute_params": {
                "orders": [7, 14]
            },
            "input__regressors_numeric__normalize__normalize_algorithm":
            "RobustScaler",
        })
    computation = ComputationParam(hyperparameter_budget=10,
                                   n_jobs=None,
                                   verbose=1)
    forecast_horizon = 20
    coverage = 0.7
    config = ForecastConfig(model_template=ModelTemplateEnum.PROPHET.name,
                            metadata_param=metadata,
                            forecast_horizon=forecast_horizon,
                            coverage=coverage,
                            evaluation_metric_param=evaluation_metric,
                            evaluation_period_param=evaluation_period,
                            model_components_param=model_components,
                            computation_param=computation)
    template = ProphetTemplate()
    params = template.apply_template_for_pipeline_params(df=df, config=config)
    pipeline = params.pop("pipeline", None)
    # Adding start_year and end_year based on the input df
    model_components.events["start_year"] = df[time_col].min().year
    model_components.events["end_year"] = df[time_col].max().year
    expected_params = dict(
        df=df,
        time_col=time_col,
        value_col=value_col,
        date_format=metadata.date_format,
        freq=metadata.freq,
        train_end_date=metadata.train_end_date,
        anomaly_info=metadata.anomaly_info,
        # model
        regressor_cols=template.regressor_cols,
        estimator=None,
        hyperparameter_grid=template.hyperparameter_grid,
        hyperparameter_budget=computation.hyperparameter_budget,
        n_jobs=computation.n_jobs,
        verbose=computation.verbose,
        # forecast
        forecast_horizon=forecast_horizon,
        coverage=coverage,
        test_horizon=evaluation_period.test_horizon,
        periods_between_train_test=evaluation_period.
        periods_between_train_test,
        agg_periods=evaluation_metric.agg_periods,
        agg_func=evaluation_metric.agg_func,
        # evaluation
        score_func=metric.name,
        score_func_greater_is_better=metric.get_metric_greater_is_better(),
        cv_report_metrics=evaluation_metric.cv_report_metrics,
        null_model_params=evaluation_metric.null_model_params,
        relative_error_tolerance=evaluation_metric.relative_error_tolerance,
        # CV
        cv_horizon=evaluation_period.cv_horizon,
        cv_min_train_periods=evaluation_period.cv_min_train_periods,
        cv_expanding_window=evaluation_period.cv_expanding_window,
        cv_periods_between_splits=evaluation_period.cv_periods_between_splits,
        cv_periods_between_train_test=evaluation_period.
        cv_periods_between_train_test,
        cv_max_splits=evaluation_period.cv_max_splits)
    assert_basic_pipeline_equal(pipeline, template.pipeline)
    assert_equal(params, expected_params)
예제 #10
0
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)
예제 #11
0
def test_get_prophet_holidays():
    """Tests get_prophet_holidays"""
    year_list = list(range(2014, 2030 + 2))
    holiday_lookup_countries = [
        "UnitedStates", "UnitedKingdom", "India", "France", "China"
    ]

    # Default holidays from get_prophet_holidays
    actual_holidays = ProphetTemplate().get_prophet_holidays(
        year_list=year_list)

    # Ensures all given countries' holidays are captured in the given `year_list`.
    # Loops through all country level holidays and confirm they are available in actual_holidays.
    # Suppresses the warnings such as "We only support Diwali and Holi holidays from 2010 to 2025"
    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        for ctry in holiday_lookup_countries:
            expected_ctry_holidays = make_holidays_df(year_list=year_list,
                                                      country=ctry)
            # sort df and reset index to ensure assert_frame_equal works well. Without it, assert throws an error.
            expected_ctry_holidays = expected_ctry_holidays.sort_values(
                by=["ds"]).reset_index(drop=True)

            actual = actual_holidays[["ds", "holiday"]]  # All actual holidays
            actual_ctry_holidays = actual.merge(  # Ensure country-level holidays are a subset of actual holidays
                expected_ctry_holidays,
                on=["ds", "holiday"],
                how="inner",
                validate="1:1")  # Ensures 1:1 mapping
            actual_ctry_holidays = actual_ctry_holidays.sort_values(
                by=["ds"]).reset_index(drop=True)
            assert_frame_equal(expected_ctry_holidays, actual_ctry_holidays)

    # there are no duplicates at date and holiday level in the final holidays df
    actual_holidays_rows = actual_holidays.shape[0]
    unique_date_holiday = actual_holidays["ds"].astype(
        str) + " " + actual_holidays["holiday"]
    unique_date_holiday_combinations = pd.unique(unique_date_holiday).shape[0]
    assert unique_date_holiday_combinations == actual_holidays_rows

    # Tests custom params
    lower_window = -1
    upper_window = 4
    countries = ["UnitedKingdom", "Australia"]
    actual_holidays = ProphetTemplate().get_prophet_holidays(
        countries=countries,
        year_list=year_list,
        lower_window=lower_window,
        upper_window=upper_window)
    assert "Australia Day (Observed)" in actual_holidays["holiday"].values
    assert "Chinese New Year" not in actual_holidays["holiday"].values

    # all of the expected columns are available in the output
    actual_columns = list(actual_holidays.columns)
    expected_columns = ["ds", "holiday", "lower_window", "upper_window"]
    assert actual_columns == expected_columns

    # lower_window and upper_window are accurately assigned
    assert actual_holidays["lower_window"].unique() == lower_window
    assert actual_holidays["upper_window"].unique() == upper_window

    # no countries
    actual_holidays = ProphetTemplate().get_prophet_holidays(
        countries=[],
        year_list=year_list,
        lower_window=lower_window,
        upper_window=upper_window)
    assert actual_holidays is None
    actual_holidays = ProphetTemplate().get_prophet_holidays(
        countries=None,
        year_list=year_list,
        lower_window=lower_window,
        upper_window=upper_window)
    assert actual_holidays is None

    # single country
    with pytest.raises(ValueError,
                       match="`countries` should be a list, found Australia"):
        ProphetTemplate().get_prophet_holidays(countries="Australia",
                                               year_list=year_list,
                                               lower_window=lower_window,
                                               upper_window=upper_window)
예제 #12
0
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)
예제 #13
0
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)
예제 #14
0
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)
예제 #15
0
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()
예제 #16
0
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