code: def plot_event_periods_multi(
      periods_df,
      start_time_col=START_TIME_COL,
      end_time_col=END_TIME_COL,
      freq=None,
      grouping_col=None,
      min_timestamp=None,
      max_timestamp=None,
      new_cols_tag="_is_anomaly",
      title="anomaly periods",
  ):
      """For a dataframe (``periods_df``) with rows denoting start and end of the periods,
      it plots the periods. If there extra segmentation is given (``grouping_col``) then
      the periods in each segment/slice will be plotted separately on top of each other so that
      their overlap can be seen easily.
  
      Parameters
      ----------
      periods_df : `pandas.DataFrame`
          Data frame with ``start_time_col`` and ``end_time_col`` and optionally
          ``grouping_col`` if passed.
      start_time_col : `str`, default START_TIME_COL
          The column denoting the start of a period. The type can be any type
          admissable as `pandas.to_datetime`.
      end_time_col : `str`, default END_TIME_COL
          The column denoting the start of a period. The type can be any type
          admissable as `pandas.to_datetime`.
      freq : `str` or None, default None
          Frequency of the generated time grid which is used to plot the horizontal
          line segments (points).
  
          If None, we use hourly as default (freq = "H") which will be accurate
          for timestamps which are rounded up to an hour. For finer timestamps
          user should specify higher frequencies e.g. "min" for minutely. Also for
          daily, weekly, monthly data, user can use a lower frequency to make the plot
          size on disk smaller.
      grouping_col : `str` or None, default None
          A column which specifies the slicing.
          Each slice's event / anomaly period will be plotted with a specific color
          for that slice. Each segment will appear at a different height of the y-axis
          and its periods would be annotated at that height.
      min_timestamp : `str` or None, default None
          A string denoting the starting point (time) the x axis.
          If None, the minimum of ``start_time_col`` will be used.
      max_timestamp : `str` or None, default None
          A string denoting the end point (time) for the x axis.
          If None, the maximum of ``end_time_col`` will be used.
      title : `str` or None, default None
          Plot title. If None, default is based on axis labels.
      new_cols_tag : `str`, default "_is_anomaly"
          The tag used in the column names for each group.
          The column name has this format: f"{group}{new_cols_tag}".
          For example if a group is "impressions", with the default value
          of this argument the added column name is "impressions_is_anomaly"
  
      Returns
      -------
      result : `dict`
  
          - "fig" : `plotly.graph_objects.Figure`
              Interactive plotly graph of periods given for each group.
          - "labels_df" : `pandas.DataFrame`
              A dataframe which includes timestamps as one column (TIME_COL) and one
              dummy string column for each group.
              The values of the new columns are None, except for the time periods specified
              in each corresponding group.
          - "groups" : `list` [`str`]
              The distinct values seen in ``df[grouping_col]`` which are used for
              slicing of data.
          - "new_cols" : `list` [`str`]
              The new columns generated and added to ``labels_df``.
              Each column corresponds to one slice of the data as specified in
              ``grouping_col``.
          - "ts" : `list` [`pandas._libs.tslibs.timestamps.Timestamp`]
              A time-grid generated by ``pandas.date_range``
          - "min_timestamp" : `str` or None, default None
              A string denoting the starting point (time) the x axis.
          - "max_timestamp" : `str` or None, default None
              A string denoting the end point (time) for the x axis.
          - "marker_colors" : `list` [`str`]
              A list of strings denoting the colors used for various slices.
  
      """
      periods_df = periods_df.copy()
      if min_timestamp is None:
          min_timestamp = periods_df[start_time_col].min()
      if max_timestamp is None:
          max_timestamp = periods_df[end_time_col].max()
  
      periods_df = periods_df[
          (periods_df[start_time_col] >= min_timestamp)
          & (periods_df[end_time_col] <= max_timestamp)
      ].reset_index(drop=True)
  
      if freq is None:
          freq = "H"
  
      ts = pd.date_range(
          start=min_timestamp, end=max_timestamp, freq=freq
      )
      labels_df = pd.DataFrame({TIME_COL: ts})
      # Converting the time to standard string format in order to use plotly safely
      labels_df[TIME_COL] = labels_df[TIME_COL].dt.strftime(
          "%Y-%m-%d %H:%M:%S"
      )
  
      def add_periods_dummy_column_for_one_group(
          labels_df, periods_df, new_col, label
      ):
          """This function will add a dummy column for the time periods of each group to the ``labels_df``.
          Parameters
          ----------
          labels_df : `pandas.DataFrame`
              A data frame which at least inludes a timestamp column (TIME_COL).
          periods_df : `pandas.DataFrame`
              Data frame with ``start_time_col`` and ``end_time_col``.
          new_col : `str`
              The column name for the new dummy column to be added.
          label : `str`
              The label to be used in the new column when an event / anomaly is
              happening. Other values will be None.
  
  
          Returns
          -------
          labels_df : `pandas.DataFrame`
              A dataframe which has one extra column as compared with the input ``labels_df``.
              This extra column is a dummy string column for the input ``group``.
          """
          for i, row in periods_df.iterrows():
              t1 = row[start_time_col]
              t2 = row[end_time_col]
              if t2 < t1:
                  raise ValueError(
                      f"End Time: {t2} cannot be before Start Time: {t1}, in ``periods_df``."
                  )
              bool_index = (ts >= t1) & (ts <= t2)
              labels_df.loc[bool_index, new_col] = label
  
          return labels_df
  
      new_cols = []
      # If there is no grouping column we add a grouping column with only one value ("metric")
      if grouping_col is None:
          grouping_col = "metric"
          periods_df["metric"] = "metric"
  
      groups = set(periods_df[grouping_col].values)
  
      marker_colors = get_distinct_colors(len(groups), opacity=0.8)
  
      for group in groups:
          new_col = f"{group}{new_cols_tag}"
          new_cols.append(new_col)
          periods_df_group = periods_df.loc[
              periods_df[grouping_col] == group
          ]
          labels_df = add_periods_dummy_column_for_one_group(
              labels_df=labels_df,
              periods_df=periods_df_group,
              new_col=new_col,
              label=group,
          )
  
      # Plotting line segments for each period.
      # The line segments will have the same color for the same group.
      # Each group will be occupying one horizontal level in the plot.
      # The groups are stacked vertically in the plot so that the user can
      # scan and compare the periods across groups.
      fig = plot_lines_markers(
          df=labels_df,
          x_col=TIME_COL,
          line_cols=None,
          marker_cols=new_cols,
          line_colors=None,
          marker_colors=marker_colors,
      )
  
      # Specify the y axis range
      fig.update_yaxes(range=[-1, len(groups)])
  
      # We add rectangles for each event period.
      # The rectangle colors for each group will be the same and consistent with
      # the line segments generated before for the same group.
      # The rectangles span all the way through the y-axis so that the user can
      # inspect the intersections between various groups better.
      shapes = []
      for i, group in enumerate(groups):
          ind = periods_df[grouping_col] == group
          periods_df_group = periods_df.loc[ind]
  
          fillcolor = marker_colors[i]
          for j, row in periods_df_group.iterrows():
              x0 = row[start_time_col]
              x1 = row[end_time_col]
              y0 = -1
              y1 = len(groups)
  
              # Specify the corners of the rectangles
              shape = dict(
                  type="rect",
                  x0=x0,
                  y0=y0,
                  x1=x1,
                  y1=y1,
                  fillcolor=fillcolor,
                  opacity=0.6,
                  line_width=2,
                  line_color=fillcolor,
                  layer="below",
              )
  
              shapes.append(shape)
  
      fig.update_layout(
          shapes=shapes,
          title=title,
          plot_bgcolor="rgba(233, 233, 233, 0.3)",
      )  # light grey (lighter than default background)
  
      return {
          "fig": fig,
          "labels_df": labels_df,
          "groups": groups,
          "new_cols": new_cols,
          "ts": ts,
          "min_timestamp": min_timestamp,
          "max_timestamp": max_timestamp,
          "marker_colors": marker_colors,
          "periods_df": periods_df,
      }
  
file_path: greykite/greykite/common/viz/timeseries_annotate.py
imports: import pandas
  import plotly.graph_objects
  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 START_TIME_COL
  from greykite.common.constants import TIME_COL
  from greykite.common.features.adjust_anomalous_data import label_anomalies_multi_metric
  from greykite.common.viz.colors_utils import get_distinct_colors
  from greykite.common.viz.timeseries_plotting import plot_forecast_vs_actual
label_desc: Use image node_id 4 for calling a global function with example usage: plot_event_periods_multi(periods_df, start_time_col, end_time_col, freq, grouping_col, min_timestamp, max_timestamp, new_cols_tag, title) and returns: dict, labels_df