code: def test_greykite_detector_hourly_f1(
      hourly_data, forecast_config_info_hourly
  ):
      """Tests ``GreykiteDetector`` with F1 score as reward on hourly data."""
      df_train = hourly_data["df_train"]
      df_test = hourly_data["df_test"]
      df = hourly_data["df"]
  
      forecast_config = forecast_config_info_hourly
      ad_config = ADConfig(
          volatility_features_list=[["dow"], ["hour"]],
          coverage_grid=[
              0.5,
              0.55,
              0.6,
              0.65,
              0.7,
              0.75,
              0.9,
              0.95,
              0.99,
              0.999,
          ],
          target_anomaly_percent=None,
          variance_scaling=False,
      )
  
      train_data = DetectorData(df=df_train)
  
      def reward_func(data):
          return calc_f1(y_true=data.y_true, y_pred=data.y_pred)
  
      reward = Reward(reward_func)
  
      detector = GreykiteDetector(
          forecast_config=forecast_config,
          ad_config=ad_config,
          reward=reward,
      )
  
      detector.fit(data=train_data)
  
      # Checks optimal parameter.
      assert detector.fit_info["param"] == {
          "coverage": 0.99,
          "volatility_features": ["dow"],
      }
      # Checks parameter grid.
      param_obj_list = detector.fit_info["param_obj_list"]
      param_eval_df = pd.DataFrame.from_records(param_obj_list)
      assert list(param_eval_df.columns) == [
          "coverage",
          "volatility_features",
          "obj_value",
      ]
  
      param_eval_df["volatility_features"] = param_eval_df[
          "volatility_features"
      ].map(str)
      fig = px.line(
          param_eval_df,
          x="coverage",
          y="obj_value",
          color="volatility_features",
          title="'GreykiteDetector' result of parameter search: f1, hourly data",
      )
      assert fig is not None
      if FIG_SHOW:
          fig.show()
  
      test_data = DetectorData(df=df_test, y_true=df_test[ANOMALY_COL])
  
      test_data = detector.predict(test_data)
      test_obj_value = detector.reward.apply(test_data)
      assert test_obj_value == pytest.approx(0.70, 0.01)
  
      test_recall = calc_recall(
          y_true=test_data.y_true, y_pred=test_data.y_pred
      )
  
      test_precision = calc_precision(
          y_true=test_data.y_true, y_pred=test_data.y_pred
      )
  
      assert test_recall == pytest.approx(1.00, 0.001)
      assert test_precision == pytest.approx(0.545, 0.001)
  
      fit_data = detector.fit_info["best_calc_result"].data
      fit_df = fit_data.pred_df
      pred_df = test_data.pred_df
  
      # Checks if we get the expected columns in the fit / prediction data.
      assert list(pred_df.columns) == DETECTOR_PREDICT_COLS + [
          ANOMALY_COL
      ]
      assert list(fit_df.columns) == DETECTOR_PREDICT_COLS + [
          ANOMALY_COL
      ]
      fit_pred_df = pd.concat([fit_df, pred_df], axis=0).reset_index(
          drop=True
      )
      fit_pred_df[ANOMALY_COL] = df[ANOMALY_COL]
  
      fig = plt_compare_series_annotations(
          df=fit_pred_df,
          x_col=TIME_COL,
          actual_col=ACTUAL_COL,
          actual_label_col=ANOMALY_COL,
          forecast_label_col=PREDICTED_ANOMALY_COL,
          keep_cols=[PREDICTED_LOWER_COL, PREDICTED_UPPER_COL],
          forecast_col=PREDICTED_COL,
          standardize_col=None,
          title="test_greykite_detector_hourly_f1",
      )
  
      fig.add_vline(
          x=fit_df[TIME_COL].max(),
          line_width=1,
          line_dash="dash",
          line_color="green",
      )
  
      fig.add_annotation(
          x=fit_df[TIME_COL].max(),
          y=fit_pred_df[ACTUAL_COL].max(),
          text="end of training",
      )
      assert fig is not None
      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 6 for calling a global function with example usage: test_greykite_detector_hourly_f1(hourly_data, forecast_config_info_hourly) and returns: calc_f1