code: def test_greykite_detector_hourly_anomaly_pickup():
      """Tests that anomaly data is picked up properly by the ``GreykiteDetector``.
  
      Anomalies are injected to the training data and the anomaly info is passed to
      ``GreykiteDetector`` via `anomaly_df`.
  
      We check that the anomaly info is picked up by the detector during training
      and the future forecasts are unaffected by the anomaly values.
      """
      metadata = MetadataParam(freq="H")
  
      evaluation_period = EvaluationPeriodParam(
          test_horizon=0, cv_max_splits=0
      )
  
      # This forecast configs includes the median of past three weeks as an important predictor.
      # Therefore, if that median is off due to anomalies, the prediction can be off.
      # In this test, we prove that by labeling those points and passing that information to the model
      # via `anomaly_df`, we can avoid the model to fit to those values.
      model_components = ModelComponentsParam(
          growth={"growth_term": "linear"},
          hyperparameter_override={
              "input__response__null__impute_algorithm": "ts_interpolate",
              "input__response__null__impute_params": {
                  "orders": [168, 336, 504]
              },
          },
          seasonality={
              "monthly_seasonality": 2,
              "yearly_seasonality": 7,
          },
          events={
              "holiday_lookup_countries": ["US"],
              "holiday_pre_num_days": 2,
              "holiday_post_num_days": 2,
          },
          autoregression={
              "autoreg_dict": {
                  "agg_lag_dict": {
                      "orders_list": [[168, 336, 504]],
                      "agg_func": "median",
                  }
              }
          },
          custom={
              "fit_algorithm_dict": {"fit_algorithm": "ridge"},
              "min_admissible_value": 0,
              "normalize_method": "zero_to_one",
              "extra_pred_cols": [
                  "is_event:is_weekend:C(hour)",
                  "dow_hr",
                  "y_avglag_168_336_504*dow_hr",
                  "y_avglag_168_336_504*sin1_ct1_yearly",
                  "y_avglag_168_336_504*cos1_ct1_yearly",
                  "y_avglag_168_336_504*sin1_tom_monthly",
                  "y_avglag_168_336_504*cos1_tom_monthly",
                  "us_dst*dow_hr",
              ],
          },
      )
  
      forecast_config = ForecastConfig(
          model_template="SILVERKITE_EMPTY",
          metadata_param=metadata,
          coverage=0.95,
          evaluation_period_param=evaluation_period,
          forecast_horizon=1,
          model_components_param=model_components,
      )
  
      ad_config = ADConfig(
          target_anomaly_percent=0.35,
          volatility_features_list=[["hour"]],
          coverage_grid=[0.996],
          sape_grid=[5.0, 10.0, 15.0, 20.0, 30.0, 40.0, 50.0, 60.0],
          variance_scaling=True,
      )
  
      input_data = generate_df_for_tests(
          freq="H",
          train_start_date=datetime.datetime(2020, 1, 1),
          intercept=50,
          train_frac=0.99,
          periods=24 * 60,
      )
      df_train = input_data["train_df"]
      df_test = input_data["test_df"]
  
      # Injects 3 anomalies at the same time of the week (0 values).
      # Note that if model does not have access to these anomaly labels,
      # the prediction can be off.
      df_train.loc[[24 * 28, 24 * 35, 24 * 42], "y"] = 0
      # Passes the anomaly info to the `detector` via `anomaly_df`.
      anomaly_df = pd.DataFrame(
          {
              START_TIME_COL: [
                  "2020-01-29 00:00:00",
                  "2020-02-05 00:00:00",
                  "2020-02-12 00:00:00",
              ],
              END_TIME_COL: [
                  "2020-01-29 00:00:00",
                  "2020-02-05 00:00:00",
                  "2020-02-12 00:00:00",
              ],
          }
      )
  
      fig = plot_lines_markers(
          df=df_train, x_col=TIME_COL, line_cols=["y"]
      )
      fig.layout.update(title="Generation of daily anomalous data")
      fig.update_yaxes()
      assert fig is not None
      if FIG_SHOW:
          fig.show()
  
      # Trains `GreykiteDetector`.
      train_data = DetectorData(df=df_train, anomaly_df=anomaly_df)
      detector = GreykiteDetector(
          forecast_config=forecast_config,
          ad_config=ad_config,
          reward=None,
      )
      detector.fit(data=train_data)
      # We expect the anomalies to be picked up by the `detector` during training.
      if FIG_SHOW:
          detector.plot(phase="train")
  
      # Predicts on test data.
      test_data = DetectorData(df=df_test)
      detector.predict(test_data)
      fig = detector.plot()
      assert fig is not None
      # Adds a vertical line at the next hourly datapoint.
      # The forecast at this data point should not be close to 0.
      # This is because even though the median aggregated lag across the previous 3 weeks is 0,
      # the anomaly labels help the model not fit those values.
      fig.add_vline(x="2020-02-19 00:00:00", line_dash="dash")
      if FIG_SHOW:
          fig.show()
  
imports: import datetime
  import numpy
  import pandas
  import plotly.express
  import pytest
  from greykite.common.constants import ACTUAL_COL
  from greykite.common.constants import ANOMALY_COL
  from greykite.common.constants import END_TIME_COL
  from greykite.common.constants import PREDICTED_ANOMALY_COL
  from greykite.common.constants import PREDICTED_COL
  from greykite.common.constants import PREDICTED_LOWER_COL
  from greykite.common.constants import PREDICTED_UPPER_COL
  from greykite.common.constants import START_TIME_COL
  from greykite.common.constants import TIME_COL
  from greykite.common.constants import VALUE_COL
  from greykite.common.testing_utils import generate_df_for_tests
  from greykite.common.testing_utils_anomalies import contaminate_df_with_anomalies
  from greykite.common.viz.timeseries_annotate import plot_anomalies_over_forecast_vs_actual
  from greykite.common.viz.timeseries_annotate import plot_lines_markers
  from greykite.common.viz.timeseries_annotate import plt_compare_series_annotations
  from greykite.detection.common.ad_evaluation import f1_score
  from greykite.detection.common.ad_evaluation import precision_score
  from greykite.detection.common.ad_evaluation import recall_score
  from greykite.detection.detector.ad_utils import partial_return
  from greykite.detection.detector.config import F1
  from greykite.detection.detector.config import ADConfig
  from greykite.detection.detector.data import DetectorData
  from greykite.detection.detector.greykite import DETECTOR_PREDICT_COLS
  from greykite.detection.detector.greykite import GreykiteDetector
  from greykite.detection.detector.reward import Reward
  from greykite.framework.templates.autogen.forecast_config import EvaluationPeriodParam
  from greykite.framework.templates.autogen.forecast_config import ForecastConfig
  from greykite.framework.templates.autogen.forecast_config import MetadataParam
  from greykite.framework.templates.autogen.forecast_config import ModelComponentsParam
label_desc: Use image node_id 13 for calling a global function with example usage: test_greykite_detector_hourly_anomaly_pickup() without return types