code: def get_canonical_anomaly_df(
      anomaly_df,
      freq,
      start_time_col=START_TIME_COL,
      end_time_col=END_TIME_COL,
  ):
      """Validates and merges overlapping anomaly periods in anomaly dataframe.
      Also standardizes column names.
  
      For example, consider the following input ``anomaly_df``:
       start_time       end_time
      "2020-01-01"    "2020-01-02"
      "2020-01-03"    "2020-01-05"
  
      For a daily dataset i.e. ``freq = "D"``, the end time "2020-01-02" and start time
      "2020-01-03" are consecutive. Hence, in the output the ``anomaly_df`` is converted to
      start_time       end_time
      "2020-01-01"    "2020-01-05"
  
      However, for an hourly dataset i.e. ``freq = "H"`` the end time "2020-01-02" and start time
      "2020-01-03" are not consecutive. Hence, the output is the same as the input ``anomaly_df``.
  
      Parameters
      ---------
      anomaly_df : `pandas.DataFrame`
          The dataframe that contains anomaly info.
          It should at least have
              - the anomaly start column ``start_time_col``
              - the anomaly end column ``end_time_col``
          Both are assumed to be inclusive of the start and end times.
      freq : `str`
          Frequency of the timeseries represented in ``anomaly_df``.
          This is used to determine if the timestamps in the ``anomaly_df`` are consecutive.
      start_time_col : `str` or None
          The column name containing anomaly start timestamps in ``anomaly_df``.
          If None, it is set to
          `~greykite.detection.detector.constants.START_TIME_COL`.
      end_time_col : `str` or None
          The column name containing anomaly end timestamps in ``anomaly_df``.
          If None, it is set to
          `~greykite.detection.detector.constants.END_TIME_COL`.
      Returns
      -------
      anomaly_df : `pandas.DataFrame`
          Standardized anomaly dataframe.
          It should have
              - the anomaly start column "start_time"
              `~greykite.detection.detector.constants.ANOMALY_START_TIME`.
              - the anomaly end column "end_time"
              `~greykite.detection.detector.constants.ANOMALY_END_TIME`.
          Both should be inclusive.
          The anomaly periods are non-overlapping and sorted from earliest to latest.
      """
      df = anomaly_df.copy()
      df[start_time_col] = pd.to_datetime(df[start_time_col])
      df[end_time_col] = pd.to_datetime(df[end_time_col])
      df = df.sort_values(by=[start_time_col]).reset_index(drop=True)
      row_num = df.shape[0] - 1
      for row in range(row_num):
          start_time = df[start_time_col][row]
          end_time = df[end_time_col][row]
          if start_time > end_time:
              raise ValueError(
                  f"Anomaly 'start_time' ({start_time}) is after the anomaly 'end_time' ({end_time})."
              )
          # Merges anomalies
          next_start_time = df[start_time_col][row + 1]
          next_end_time = df[end_time_col][row + 1]
          num_periods = (
              next_start_time.to_period(freq=freq)
              - end_time.to_period(freq=freq)
          ).n
          # Start times and end times are inclusive for anomaly df. Hence, the anomaly periods should
          # be merged if the number of periods between the anomalies are less than 1.
          # e.g. The anomaly periods ["2020-01-01", "2020-01-02"] and ["2020-01-03", "2020-01-04"]
          # should be merged into a single anomaly period ["2020-01-01", "2020-01-04"].
          if num_periods <= 1:
              df[start_time_col][row + 1] = start_time
          if next_end_time < end_time:
              df[end_time_col][row + 1] = end_time
      df = (
          df.drop_duplicates(subset=[start_time_col], keep="last")
          .rename(
              {
                  start_time_col: START_TIME_COL,
                  end_time_col: END_TIME_COL,
              },
              axis=1,
          )
          .reset_index(drop=True)
      )
      return df
  
file_path: greykite/greykite/detection/detector/ad_utils.py
imports: from functools import reduce
  import pandas
  from greykite.common.constants import ANOMALY_COL
  from greykite.common.constants import END_TIME_COL
  from greykite.common.constants import START_TIME_COL
  from greykite.common.constants import TIME_COL
  from greykite.common.features.outlier import ZScoreOutlierDetector
  from greykite.common.logging import LoggingLevelEnum
  from greykite.common.logging import log_message
  from greykite.detection.detector.constants import Z_SCORE_CUTOFF
label_desc: Use image node_id 5 for calling a global function with example usage: get_canonical_anomaly_df(anomaly_df, freq, start_time_col, end_time_col) and returns: df