class_docstr: This module estimates the impact of holidays and their neighboring days
  given a raw holiday dataframe ``holiday_df``, and a time series containing
  the observed values to construct the baselines.
  It groups events with similar effects to several groups using kernel density estimation (KDE)
  and generates the grouped events in a dictionary of dataframes that is recognizable by
  `~greykite.algo.forecast.silverkite.forecast_silverkite.SilverkiteForecast`.
  
  Parameters
  ----------
  df : `pandas.DataFrame`
      Input time series that contains ``time_col`` and ``value_col``.
      The values will be used to construct baselines to estimate the holiday impact.
  time_col : `str`
      Name of the time column in ``df``.
  value_col : `str`
      Name of the value column in ``df``.
  holiday_df : `pandas.DataFrame`
      Input holiday dataframe that contains the dates and names of the holidays.
  holiday_date_col : `str`
      Name of the holiday date column in ``holiday_df``.
  holiday_name_col : `str`
      Name of the holiday name column in ``holiday_df``.
  holiday_impact_pre_num_days: `int`, default 0
      Default number of days before the holiday that will be modeled for holiday effect if the given holiday
      is not specified in ``holiday_impact_dict``.
  holiday_impact_post_num_days: `int`, default 0
      Default number of days after the holiday that will be modeled for holiday effect if the given holiday
      is not specified in ``holiday_impact_dict``.
  holiday_impact_dict : `Dict` [`str`, Any] or None, default None
      A dictionary containing the neighboring impacting days of a certain holiday. This overrides the
      default ``pre_num`` and ``post_num`` for each holiday specified here.
      The key is the name of the holiday matching those in the provided ``holiday_df``.
      The value is a tuple of two values indicating the number of neighboring days
      before and after the holiday. For example, a valid dictionary may look like:
  
          .. code-block:: python
  
              holiday_impact_dict = {
                  "Christmas Day": [3, 3],
                  "Memorial Day": [0, 0]
              }
  
  get_suffix_func : Callable or `str` or None, default "wd_we"
      A function that generates a suffix (usually a time feature e.g. "_WD" for weekday,
      "_WE" for weekend) given an input date.
      This can be used to estimate the interaction between floating holidays
      and on which day they are getting observed.
      We currently support two defaults:
  
          - "wd_we" to generate suffixes based on whether the day falls on weekday or weekend.
          - "dow_grouped" to generate three categories: ["_WD", "_Sat", "_Sun"].
  
      If None, no suffix is added.
  
  Attributes
  ----------
  expanded_holiday_df : `pandas.DataFrame`
      An expansion of ``holiday_df`` after adding the neighboring dates provided in
      ``holiday_impact_dict`` and the suffix generated by ``get_suffix_func``.
      For example, if ``"Christmas Day": [3, 3]`` and "wd_we" are used, events
      such as "Christmas Day_WD_plus_1_WE" or "Christmas Day_WD_minus_3_WD" will be generated
      for a Christmas that falls on Friday.
  baseline_offsets : `Tuple`[`int`] or None
      The offsets in days to calculate baselines for a given holiday.
      By default, the same days of the week before and after are used.
  use_relative_score : `bool` or None
      Whether to use relative or absolute score when estimating the holiday impact.
  clustering_method : `str` or None
      Clustering method used to group the holidays.
      Since we are doing 1-D clustering, current supported methods include
      (1) "kde" for kernel density estimation, and (2) "kmeans" for k-means clustering.
  bandwidth : `float` or None
      The bandwidth used in the kernel density estimation.
      Higher bandwidth results in less clusters.
      If None, it is automatically inferred with the ``bandwidth_multiplier`` factor.
  bandwidth_multiplier : `float` or None
      Multiplier to be multiplied to the kernel density estimation's default parameter calculated from
      `here<https://en.wikipedia.org/wiki/Kernel_density_estimation#A_rule-of-thumb_bandwidth_estimator>_`.
      This multiplier has been found useful in adjusting the default bandwidth parameter in many cases.
      Only used when ``bandwidth`` is not specified.
  kde : `KernelDensity` or None
      The `KernelDensity` object if ``clustering_method == "kde"``.
  n_clusters : `int` or None
      Number of clusters in the k-means algorithm.
  kmeans : `KMeans` or None
      The `KMeans` object if ``clustering_method == "kmeans"``.
  include_diagnostics : `bool` or None
      Whether to include ``kmeans_diagnostics`` and ``kmeans_plot`` in the output ``result_dict``.
  result_dict : `Dict`[`str`, Any] or None
      A dictionary that stores the scores and clustering results, with the following keys.
  
          - "holiday_inferrer": the `~greykite.algo.common.holiday_inferrer.HolidayInferrer`
              instance used for calculating the scores.
          - "score_result_original": a dictionary with keys being the names of all holiday events
              after expansion (i.e. the keys in ``expanded_holiday_df``), values being a list of scores
              of all dates corresponding to this event.
          - "score_result_avg_original": a dictionary with the same key as in
              ``result_dict["score_result_original"]``.
              But the values are the average scores of each event across all occurrences.
          - "score_result": same as ``result_dict["score_result_original"]``, but after removing
              holidays with inconsistent / negligible scores.
          - "score_result_avg": same as ``result_dict["score_result_original"]``, but after removing
              holidays with inconsistent / negligible scores.
          - "daily_event_df_dict_with_score": a dictionary of dataframes.
              Key is the group name ``"holiday_group_{k}"``.
              Value is a dataframe of all holiday events in this group, containing 4 columns:
              "date" (``EVENT_DF_DATE_COL``), "event_name" (``EVENT_DF_LABEL_COL``), "original_name", "avg_score".
          - "daily_event_df_dict": a dictionary of dataframes that is ready to use in `SilverkiteForecast`.
              Contains 2 keys: ``EVENT_DF_DATE_COL`` and ``EVENT_DF_LABEL_COL``.
          - "kde_cutoffs": a list of `float`, the cutoffs returned by the kernel density clustering.
          - "kde_res": a dataframe that contains "score" and "density" from the kernel density estimation.
          - "kde_plot": a plot of the kernel density estimation.
          - "kmeans_diagnostics": a dataframe containing metrics for different number of clusters.
              Columns are:
  
                  - "k": number of clusters;
                  - "wsse": within-cluster sum of squared error (lower is better);
                  - "sil_score": Silhouette coefficient, a value between [-1, 1] that describes
                      the separation of clusters (higher is better).
  
              Only generated when ``include_diagnostics`` is True. See `group_holidays` for details.
          - "kmeans_plot": a plot visualizing how the diagnostic metrics change over K.
              Only generated when ``include_diagnostics`` is True. See `group_holidays` for details.
class_docstr_tok: ["This","module","estimates","the","impact","of","holidays","and","their","neighboring","days","given","a","raw","holiday","dataframe","``","holiday_df","``",",","and","a","time","series","containing","the","observed","values","to","construct","the","baselines",".","It","groups","events","with","similar","effects","to","several","groups","using","kernel","density","estimation","(","KDE",")","and","generates","the","grouped","events","in","a","dictionary","of","dataframes","that","is","recognizable","by","`","~greykite.algo.forecast.silverkite.forecast_silverkite.SilverkiteForecast","`",".","Parameters","--","--","--","--","--","df",":","`","pandas.DataFrame","`","Input","time","series","that","contains","``","time_col","``","and","``","value_col","``",".","The","values","will","be","used","to","construct","baselines","to","estimate","the","holiday","impact",".","time_col",":","`","str","`","Name","of","the","time","column","in","``","df","``",".","value_col",":","`","str","`","Name","of","the","value","column","in","``","df","``",".","holiday_df",":","`","pandas.DataFrame","`","Input","holiday","dataframe","that","contains","the","dates","and","names","of","the","holidays",".","holiday_date_col",":","`","str","`","Name","of","the","holiday","date","column","in","``","holiday_df","``",".","holiday_name_col",":","`","str","`","Name","of","the","holiday","name","column","in","``","holiday_df","``",".","holiday_impact_pre_num_days",":","`","int","`",",","default","0","Default","number","of","days","before","the","holiday","that","will","be","modeled","for","holiday","effect","if","the","given","holiday","is","not","specified","in","``","holiday_impact_dict","``",".","holiday_impact_post_num_days",":","`","int","`",",","default","0","Default","number","of","days","after","the","holiday","that","will","be","modeled","for","holiday","effect","if","the","given","holiday","is","not","specified","in","``","holiday_impact_dict","``",".","holiday_impact_dict",":","`","Dict","`","[","`","str","`",",","Any","]","or","None",",","default","None","A","dictionary","containing","the","neighboring","impacting","days","of","a","certain","holiday",".","This","overrides","the","default","``","pre_num","``","and","``","post_num","``","for","each","holiday","specified","here",".","The","key","is","the","name","of","the","holiday","matching","those","in","the","provided","``","holiday_df","``",".","The","value","is","a","tuple","of","two","values","indicating","the","number","of","neighboring","days","before","and","after","the","holiday",".","For","example",",","a","valid","dictionary","may","look","like",":","..","code-block",":",":","python","holiday_impact_dict","=","{","``","Christmas","Day","''",":","[","3",",","3","]",",","``","Memorial","Day","''",":","[","0",",","0","]","}","get_suffix_func",":","Callable","or","`","str","`","or","None",",","default","``","wd_we","''","A","function","that","generates","a","suffix","(","usually","a","time","feature","e.g",".","``","_WD","''","for","weekday",",","``","_WE","''","for","weekend",")","given","an","input","date",".","This","can","be","used","to","estimate","the","interaction","between","floating","holidays","and","on","which","day","they","are","getting","observed",".","We","currently","support","two","defaults",":","-","``","wd_we","''","to","generate","suffixes","based","on","whether","the","day","falls","on","weekday","or","weekend",".","-","``","dow_grouped","''","to","generate","three","categories",":","[","``","_WD","''",",","``","_Sat","''",",","``","_Sun","''","]",".","If","None",",","no","suffix","is","added",".","Attributes","--","--","--","--","--","expanded_holiday_df",":","`","pandas.DataFrame","`","An","expansion","of","``","holiday_df","``","after","adding","the","neighboring","dates","provided","in","``","holiday_impact_dict","``","and","the","suffix","generated","by","``","get_suffix_func","``",".","For","example",",","if","``","``","Christmas","Day","''",":","[","3",",","3","]","``","and","``","wd_we","''","are","used",",","events","such","as","``","Christmas","Day_WD_plus_1_WE","''","or","``","Christmas","Day_WD_minus_3_WD","''","will","be","generated","for","a","Christmas","that","falls","on","Friday",".","baseline_offsets",":","`","Tuple","`","[","`","int","`","]","or","None","The","offsets","in","days","to","calculate","baselines","for","a","given","holiday",".","By","default",",","the","same","days","of","the","week","before","and","after","are","used",".","use_relative_score",":","`","bool","`","or","None","Whether","to","use","relative","or","absolute","score","when","estimating","the","holiday","impact",".","clustering_method",":","`","str","`","or","None","Clustering","method","used","to","group","the","holidays",".","Since","we","are","doing","1-D","clustering",",","current","supported","methods","include","(","1",")","``","kde","''","for","kernel","density","estimation",",","and","(","2",")","``","kmeans","''","for","k-means","clustering",".","bandwidth",":","`","float","`","or","None","The","bandwidth","used","in","the","kernel","density","estimation",".","Higher","bandwidth","results","in","less","clusters",".","If","None",",","it","is","automatically","inferred","with","the","``","bandwidth_multiplier","``","factor",".","bandwidth_multiplier",":","`","float","`","or","None","Multiplier","to","be","multiplied","to","the","kernel","density","estimation","'s","default","parameter","calculated","from","`","here","<","https",":","\/\/en.wikipedia.org\/wiki\/Kernel_density_estimation","#","A_rule-of-thumb_bandwidth_estimator",">","_","`",".","This","multiplier","has","been","found","useful","in","adjusting","the","default","bandwidth","parameter","in","many","cases",".","Only","used","when","``","bandwidth","``","is","not","specified",".","kde",":","`","KernelDensity","`","or","None","The","`","KernelDensity","`","object","if","``","clustering_method","==","``","kde","''","``",".","n_clusters",":","`","int","`","or","None","Number","of","clusters","in","the","k-means","algorithm",".","kmeans",":","`","KMeans","`","or","None","The","`","KMeans","`","object","if","``","clustering_method","==","``","kmeans","''","``",".","include_diagnostics",":","`","bool","`","or","None","Whether","to","include","``","kmeans_diagnostics","``","and","``","kmeans_plot","``","in","the","output","``","result_dict","``",".","result_dict",":","`","Dict","`","[","`","str","`",",","Any","]","or","None","A","dictionary","that","stores","the","scores","and","clustering","results",",","with","the","following","keys",".","-","``","holiday_inferrer","''",":","the","`","~greykite.algo.common.holiday_inferrer.HolidayInferrer","`","instance","used","for","calculating","the","scores",".","-","``","score_result_original","''",":","a","dictionary","with","keys","being","the","names","of","all","holiday","events","after","expansion","(","i.e",".","the","keys","in","``","expanded_holiday_df","``",")",",","values","being","a","list","of","scores","of","all","dates","corresponding","to","this","event",".","-","``","score_result_avg_original","''",":","a","dictionary","with","the","same","key","as","in","``","result_dict","[","``","score_result_original","''","]","``",".","But","the","values","are","the","average","scores","of","each","event","across","all","occurrences",".","-","``","score_result","''",":","same","as","``","result_dict","[","``","score_result_original","''","]","``",",","but","after","removing","holidays","with","inconsistent","\/","negligible","scores",".","-","``","score_result_avg","''",":","same","as","``","result_dict","[","``","score_result_original","''","]","``",",","but","after","removing","holidays","with","inconsistent","\/","negligible","scores",".","-","``","daily_event_df_dict_with_score","''",":","a","dictionary","of","dataframes",".","Key","is","the","group","name","``","``","holiday_group_","{","k","}","''","``",".","Value","is","a","dataframe","of","all","holiday","events","in","this","group",",","containing","4","columns",":","``","date","''","(","``","EVENT_DF_DATE_COL","``",")",",","``","event_name","''","(","``","EVENT_DF_LABEL_COL","``",")",",","``","original_name","''",",","``","avg_score","''",".","-","``","daily_event_df_dict","''",":","a","dictionary","of","dataframes","that","is","ready","to","use","in","`","SilverkiteForecast","`",".","Contains","2","keys",":","``","EVENT_DF_DATE_COL","``","and","``","EVENT_DF_LABEL_COL","``",".","-","``","kde_cutoffs","''",":","a","list","of","`","float","`",",","the","cutoffs","returned","by","the","kernel","density","clustering",".","-","``","kde_res","''",":","a","dataframe","that","contains","``","score","''","and","``","density","''","from","the","kernel","density","estimation",".","-","``","kde_plot","''",":","a","plot","of","the","kernel","density","estimation",".","-","``","kmeans_diagnostics","''",":","a","dataframe","containing","metrics","for","different","number","of","clusters",".","Columns","are",":","-","``","k","''",":","number","of","clusters",";","-","``","wsse","''",":","within-cluster","sum","of","squared","error","(","lower","is","better",")",";","-","``","sil_score","''",":","Silhouette","coefficient",",","a","value","between","[","-1",",","1","]","that","describes","the","separation","of","clusters","(","higher","is","better",")",".","Only","generated","when","``","include_diagnostics","``","is","True",".","See","`","group_holidays","`","for","details",".","-","``","kmeans_plot","''",":","a","plot","visualizing","how","the","diagnostic","metrics","change","over","K.","Only","generated","when","``","include_diagnostics","``","is","True",".","See","`","group_holidays","`","for","details","."]
docstr: Estimates the impact of holidays and their neighboring days and
  groups events with similar effects to several groups using kernel density estimation (KDE).
  Then generates the grouped events and stores the results in ``self.result_dict``.
  
  Parameters
  ----------
  baseline_offsets : `Tuple`[`int`], default (-7, 7)
      The offsets in days to calculate baselines for a given holiday.
      By default, the same days of the week before and after are used.
  use_relative_score : `bool`, default True
      Whether to use relative or absolute score when estimating the holiday impact.
  min_n_days : `int`, default 1
      Minimal number of occurrences for a holiday event to be kept before grouping.
  min_same_sign_ratio : `float`, default 0.66
      Threshold of the ratio of the same-sign scores for an event's occurrences.
      For example, if an event has two occurrences, they both need to have positive or negative
      scores for the ratio to achieve 0.66.
      Similarly, if an event has 3 occurrences, at least 2 of them must have the same directional impact.
      This parameter is intended to rule out holidays that have indefinite effects.
  min_abs_avg_score : `float`, default 0.03
      The minimal average score of an event (across all its occurrences) to be kept
      before grouping.
      When ``use_relative_score = True``, 0.03 means the effect must be greater than 3%.
  clustering_method : `str`, default "kmeans"
      Clustering method used to group the holidays.
      Since we are doing 1-D clustering, current supported methods include
      (1) "kde" for kernel density estimation, and (2) "kmeans" for k-means clustering.
  bandwidth : `float` or None, default None
      The bandwidth used in the kernel density estimation.
      Higher bandwidth results in less clusters.
      If None, it is automatically inferred with the ``bandwidth_multiplier`` factor.
      Only used when ``clustering_method == "kde"``.
  bandwidth_multiplier : `float` or None, default 0.2
      Multiplier to be multiplied to the kernel density estimation's default parameter calculated from
      `here<https://en.wikipedia.org/wiki/Kernel_density_estimation#A_rule-of-thumb_bandwidth_estimator>_`.
      This multiplier has been found useful in adjusting the default bandwidth parameter in many cases.
      Only used when ``bandwidth`` is not specified and ``clustering_method == "kde"``.
  n_clusters : `int` or None, default 5
      Number of clusters in the k-means algorithm.
      Only used when ``clustering_method == "kmeans"``.
  include_diagnostics : `bool`, default False
      Whether to include ``kmeans_diagnostics`` and ``kmeans_plot`` in the output ``result_dict``.
  
  Returns
  -------
      Saves the results in the ``result_dict`` attribute.
docstr_tok: ["Estimates","the","impact","of","holidays","and","their","neighboring","days","and","groups","events","with","similar","effects","to","several","groups","using","kernel","density","estimation","(","KDE",")",".","Then","generates","the","grouped","events","and","stores","the","results","in","``","self.result_dict","``",".","Parameters","--","--","--","--","--","baseline_offsets",":","`","Tuple","`","[","`","int","`","]",",","default","(","-7",",","7",")","The","offsets","in","days","to","calculate","baselines","for","a","given","holiday",".","By","default",",","the","same","days","of","the","week","before","and","after","are","used",".","use_relative_score",":","`","bool","`",",","default","True","Whether","to","use","relative","or","absolute","score","when","estimating","the","holiday","impact",".","min_n_days",":","`","int","`",",","default","1","Minimal","number","of","occurrences","for","a","holiday","event","to","be","kept","before","grouping",".","min_same_sign_ratio",":","`","float","`",",","default","0.66","Threshold","of","the","ratio","of","the","same-sign","scores","for","an","event","'s","occurrences",".","For","example",",","if","an","event","has","two","occurrences",",","they","both","need","to","have","positive","or","negative","scores","for","the","ratio","to","achieve","0.66",".","Similarly",",","if","an","event","has","3","occurrences",",","at","least","2","of","them","must","have","the","same","directional","impact",".","This","parameter","is","intended","to","rule","out","holidays","that","have","indefinite","effects",".","min_abs_avg_score",":","`","float","`",",","default","0.03","The","minimal","average","score","of","an","event","(","across","all","its","occurrences",")","to","be","kept","before","grouping",".","When","``","use_relative_score","=","True","``",",","0.03","means","the","effect","must","be","greater","than","3","%",".","clustering_method",":","`","str","`",",","default","``","kmeans","''","Clustering","method","used","to","group","the","holidays",".","Since","we","are","doing","1-D","clustering",",","current","supported","methods","include","(","1",")","``","kde","''","for","kernel","density","estimation",",","and","(","2",")","``","kmeans","''","for","k-means","clustering",".","bandwidth",":","`","float","`","or","None",",","default","None","The","bandwidth","used","in","the","kernel","density","estimation",".","Higher","bandwidth","results","in","less","clusters",".","If","None",",","it","is","automatically","inferred","with","the","``","bandwidth_multiplier","``","factor",".","Only","used","when","``","clustering_method","==","``","kde","''","``",".","bandwidth_multiplier",":","`","float","`","or","None",",","default","0.2","Multiplier","to","be","multiplied","to","the","kernel","density","estimation","'s","default","parameter","calculated","from","`","here","<","https",":","\/\/en.wikipedia.org\/wiki\/Kernel_density_estimation","#","A_rule-of-thumb_bandwidth_estimator",">","_","`",".","This","multiplier","has","been","found","useful","in","adjusting","the","default","bandwidth","parameter","in","many","cases",".","Only","used","when","``","bandwidth","``","is","not","specified","and","``","clustering_method","==","``","kde","''","``",".","n_clusters",":","`","int","`","or","None",",","default","5","Number","of","clusters","in","the","k-means","algorithm",".","Only","used","when","``","clustering_method","==","``","kmeans","''","``",".","include_diagnostics",":","`","bool","`",",","default","False","Whether","to","include","``","kmeans_diagnostics","``","and","``","kmeans_plot","``","in","the","output","``","result_dict","``",".","Returns","--","--","--","-","Saves","the","results","in","the","``","result_dict","``","attribute","."]
code: def group_holidays(
      self,
      baseline_offsets: Tuple[int, int] = (-7, 7),
      use_relative_score: bool = True,
      min_n_days: int = 1,
      min_same_sign_ratio: float = 0.66,
      min_abs_avg_score: float = 0.03,
      clustering_method: str = "kmeans",
      bandwidth: Optional[float] = None,
      bandwidth_multiplier: Optional[float] = 0.2,
      n_clusters: Optional[int] = 5,
      include_diagnostics: bool = False,
  ) -> None:
      """Estimates the impact of holidays and their neighboring days and
      groups events with similar effects to several groups using kernel density estimation (KDE).
      Then generates the grouped events and stores the results in ``self.result_dict``.
  
      Parameters
      ----------
      baseline_offsets : `Tuple`[`int`], default (-7, 7)
          The offsets in days to calculate baselines for a given holiday.
          By default, the same days of the week before and after are used.
      use_relative_score : `bool`, default True
          Whether to use relative or absolute score when estimating the holiday impact.
      min_n_days : `int`, default 1
          Minimal number of occurrences for a holiday event to be kept before grouping.
      min_same_sign_ratio : `float`, default 0.66
          Threshold of the ratio of the same-sign scores for an event's occurrences.
          For example, if an event has two occurrences, they both need to have positive or negative
          scores for the ratio to achieve 0.66.
          Similarly, if an event has 3 occurrences, at least 2 of them must have the same directional impact.
          This parameter is intended to rule out holidays that have indefinite effects.
      min_abs_avg_score : `float`, default 0.03
          The minimal average score of an event (across all its occurrences) to be kept
          before grouping.
          When ``use_relative_score = True``, 0.03 means the effect must be greater than 3%.
      clustering_method : `str`, default "kmeans"
          Clustering method used to group the holidays.
          Since we are doing 1-D clustering, current supported methods include
          (1) "kde" for kernel density estimation, and (2) "kmeans" for k-means clustering.
      bandwidth : `float` or None, default None
          The bandwidth used in the kernel density estimation.
          Higher bandwidth results in less clusters.
          If None, it is automatically inferred with the ``bandwidth_multiplier`` factor.
          Only used when ``clustering_method == "kde"``.
      bandwidth_multiplier : `float` or None, default 0.2
          Multiplier to be multiplied to the kernel density estimation's default parameter calculated from
          `here<https://en.wikipedia.org/wiki/Kernel_density_estimation#A_rule-of-thumb_bandwidth_estimator>_`.
          This multiplier has been found useful in adjusting the default bandwidth parameter in many cases.
          Only used when ``bandwidth`` is not specified and ``clustering_method == "kde"``.
      n_clusters : `int` or None, default 5
          Number of clusters in the k-means algorithm.
          Only used when ``clustering_method == "kmeans"``.
      include_diagnostics : `bool`, default False
          Whether to include ``kmeans_diagnostics`` and ``kmeans_plot`` in the output ``result_dict``.
  
      Returns
      -------
          Saves the results in the ``result_dict`` attribute.
      """
      # Parameters should have already been set during initialization.
      # If new values are provided, they will override the original values.
      self.baseline_offsets = baseline_offsets
      self.use_relative_score = use_relative_score
      self.clustering_method = clustering_method
  
      # Runs baselines to get scores on holiday events.
      self.result_dict = self.get_holiday_scores(
          baseline_offsets=baseline_offsets,
          use_relative_score=use_relative_score,
          min_n_days=min_n_days,
          min_same_sign_ratio=min_same_sign_ratio,
          min_abs_avg_score=min_abs_avg_score,
      )
      # Extracts results and prepares data for kernel density estimation.
      score_result_avg = self.result_dict["score_result_avg"]
      scores_df_original = (
          pd.DataFrame(score_result_avg, index=["avg_score"])
          .transpose()
          .reset_index(drop=False)
          .rename(columns={"index": "event_name"})
      )
      # In rare cases some holiday events may fall on exactly the same day, hence the same score.
      # We drop duplicated average scores before clustering, for example,
      # Halloween (10/31) plus 1 always has the same scores as All Saints Day (11/1).
      scores_df = (
          scores_df_original.drop_duplicates("avg_score")
          .sort_values("avg_score")
          .reset_index(drop=True)
      )
      scores_x = np.array(scores_df["avg_score"]).reshape(-1, 1)
  
      # The following parameters are set to None unless the clustering method is called to populate them.
      kde_res = None
      kde_plot = None
      kde_cutoffs = None
      kmeans_diagnostics = None
      kmeans_plot = None
  
      if clustering_method.lower() == "kde":
          if bandwidth is None and bandwidth_multiplier is None:
              raise ValueError(
                  f"At least one of `bandwidth` or `bandwidth_multiplier` must be provided!"
              )
          if bandwidth is None:
              # Automatically infers the best `bandwidth`.
              std = scores_x.std()
              iqr = np.percentile(scores_x, 75) - np.percentile(
                  scores_x, 25
              )
              sigma = min(std, iqr / 1.34)
              bandwidth = (
                  0.9
                  * sigma
                  * (len(scores_x) ** (-1 / 5))
                  * bandwidth_multiplier
              )
  
          kde = KernelDensity(
              kernel="gaussian", bandwidth=bandwidth
          ).fit(scores_x)
          y = np.exp(kde.score_samples(scores_x))
          kde_res = pd.DataFrame(
              {"score": scores_df["avg_score"], "density": y.tolist()}
          )
          kde_res = kde_res.sort_values("score").reset_index(drop=True)
          kde_plot = plot_multivariate(
              df=kde_res,
              x_col="score",
              title="Kernel density of the holiday scores",
              xlabel="Holiday impact",
              ylabel="Kernel density",
          )
  
          # Performs holiday clustering based on the kernel densities.
          scores = kde_res["score"].to_list()
          densities = kde_res["density"].to_list()
          # Find the cutoffs such that scores <= each cutoff are grouped together.
          kde_cutoffs = []
          for i, x in enumerate(densities):
              if (
                  0 < i < len(densities) - 1
                  and x < densities[i - 1]
                  and x < densities[i + 1]
              ):
                  kde_cutoffs.append(scores[i])
  
          # The group around 0 may contain mixed signs, hence we manually add 0 as a cutoff.
          # This might introduce an extra group with no scores - we will remove it later.
          # Note that there are no scores smaller than `min_abs_avg_score`.
          kde_cutoffs = sorted(kde_cutoffs + [0])
  
          # Constructs `daily_event_df_dict` for all groups.
          daily_event_df_dict_raw = {
              f"holiday_group_{i}": pd.DataFrame(
                  {
                      EVENT_DF_DATE_COL: [],
                      EVENT_DF_LABEL_COL: [],
                      "original_name": [],
                      "avg_score": [],
                  }
              )
              for i in range(len(kde_cutoffs) + 1)
          }
  
          for key, value in score_result_avg.items():
              # Gets group assignment.
              for i, cutoff in enumerate(sorted(kde_cutoffs)):
                  if value <= cutoff:
                      break
              else:  # If `value` > the largest cutoff, assigns it to the last group.
                  i += 1
              # Now, `i` is the group this holiday belongs to.
  
              # Gets the dates for each event.
              # Since holiday inferrer automatically adds "+0" to all events, here we remove it.
              key_without_plus_minus = "_".join(key.split("_")[:-1])
              idx = (
                  self.expanded_holiday_df[HOLIDAY_NAME_COL]
                  == key_without_plus_minus
              )
              dates = self.expanded_holiday_df.loc[
                  idx, HOLIDAY_DATE_COL
              ]
  
              # Creates `event_df`.
              group_name = f"holiday_group_{i}"
              event_df = pd.DataFrame(
                  {
                      EVENT_DF_DATE_COL: dates,
                      EVENT_DF_LABEL_COL: group_name,
                      "original_name": key_without_plus_minus,
                      "avg_score": value,
                  }
              )
              daily_event_df_dict_raw[
                  group_name
              ] = daily_event_df_dict_raw[group_name].append(
                  event_df, ignore_index=True
              )
          # Removes potential empty groups.
          daily_event_df_dict = {}
          new_idx = 0
          for i in range(len(daily_event_df_dict_raw)):
              event_df = daily_event_df_dict_raw[f"holiday_group_{i}"]
              if len(event_df) > 0:
                  daily_event_df_dict[
                      f"holiday_group_{new_idx}"
                  ] = event_df.sort_values(
                      by=["avg_score", EVENT_DF_DATE_COL]
                  ).reset_index(
                      drop=True
                  )
                  new_idx += 1
          del daily_event_df_dict_raw
          # Overrides the attributes in the end.
          self.bandwidth = bandwidth
          self.bandwidth_multiplier = bandwidth_multiplier
          self.kde = kde
  
      elif clustering_method.lower() == "kmeans":
          # Runs K-means ++ to generate group assignments.
          kmeans = KMeans(
              n_clusters=n_clusters, random_state=0, init="k-means++"
          ).fit(scores_x)
          # Predicts on the original `scores_df` since two different events may fall on the
          # same dates where the score was calculated, but they may have different dates in the future.
          predicted_labels = kmeans.predict(
              np.array(scores_df_original["avg_score"]).reshape(-1, 1)
          )
          scores_df_with_label = scores_df_original.copy()
          scores_df_with_label["labels"] = list(predicted_labels)
          # Since `predicted_labels` is not ordered, we sort the groups according to the average score.
          group_rank_df = (
              scores_df_with_label.groupby(by="labels")["avg_score"]
              .agg(np.nanmean)
              .reset_index()
          )
          group_rank_df["group_id"] = (
              group_rank_df["avg_score"]
              .rank(method="dense", ascending=True)
              .astype(int)
          ) - 1  # Group indices start from 0.
          # Merges back to the original scores dataframe.
          scores_df_with_label = scores_df_with_label.merge(
              group_rank_df[["labels", "group_id"]],
              on="labels",
              how="left",
          )
  
          daily_event_df_dict = {
              f"holiday_group_{i}": pd.DataFrame(
                  {
                      EVENT_DF_DATE_COL: [],
                      EVENT_DF_LABEL_COL: [],
                      "original_name": [],
                      "avg_score": [],
                  }
              )
              for i in range(n_clusters)
          }
          for key, value in score_result_avg.items():
              # Gets group assignment.
              group_id = scores_df_with_label.loc[
                  scores_df_with_label["event_name"] == key, "group_id"
              ].values[0]
              group_name = f"holiday_group_{group_id}"
  
              # Gets the dates for each event.
              # Since holiday inferrer automatically adds "+0" to all events, here we remove it.
              key_without_plus_minus = "_".join(key.split("_")[:-1])
              idx = (
                  self.expanded_holiday_df[HOLIDAY_NAME_COL]
                  == key_without_plus_minus
              )
              dates = self.expanded_holiday_df.loc[
                  idx, HOLIDAY_DATE_COL
              ]
  
              # Creates `event_df`.
              event_df = pd.DataFrame(
                  {
                      EVENT_DF_DATE_COL: dates,
                      EVENT_DF_LABEL_COL: group_name,
                      "original_name": key_without_plus_minus,
                      "avg_score": value,
                  }
              )
              daily_event_df_dict[group_name] = pd.concat(
                  [daily_event_df_dict[group_name], event_df]
              )
          # Sorts the output dataframes.
          for i in range(len(daily_event_df_dict)):
              event_df = daily_event_df_dict[f"holiday_group_{i}"]
              daily_event_df_dict[
                  f"holiday_group_{i}"
              ] = event_df.sort_values(
                  by=["avg_score", EVENT_DF_DATE_COL]
              ).reset_index(
                  drop=True
              )
  
          # Generates diagnostics for K means to help choose the optimal K (`n_clusters`).
          if include_diagnostics:
              kmeans_diagnostics = {
                  "k": [],
                  "wsse": [],
                  "sil_score": [],
              }
              for candidate_k in range(
                  2, min(len(scores_df) // 2, 20) + 1
              ):
                  tmp_model = KMeans(n_clusters=candidate_k).fit(
                      scores_x
                  )
                  # Gets Silhouette score.
                  kmeans_diagnostics["k"].append(candidate_k)
                  kmeans_diagnostics["sil_score"].append(
                      silhouette_score(
                          X=scores_x,
                          labels=tmp_model.labels_,
                          metric="euclidean",
                      )
                  )
                  # Gets within-cluster sum of squared errors.
                  centroids = tmp_model.cluster_centers_
                  pred_clusters = tmp_model.predict(scores_x)
                  curr_sse = 0
                  for i in range(len(scores_x)):
                      curr_center = centroids[pred_clusters[i]]
                      curr_sse += (scores_x[i, 0] - curr_center[0]) ** 2
                  kmeans_diagnostics["wsse"].append(curr_sse)
              kmeans_diagnostics = pd.DataFrame(kmeans_diagnostics)
              kmeans_plot = plot_multivariate(
                  df=kmeans_diagnostics,
                  x_col="k",
                  xlabel="n_clusters",
                  title="K-means diagnostics<br>"
                  "(1) Within-cluster SSE: lower is better<br>"
                  "(2) Silhouette scores: higher is better",
              )
  
          # Overrides the attributes in the end.
          self.n_clusters = n_clusters
          self.kmeans = kmeans
          self.include_diagnostics = include_diagnostics
      else:
          raise NotImplementedError(
              f"`clustering_method` {clustering_method} is not supported! "
              f'Must be one of "kde" (kernel density estimation) or '
              f'"kmeans" (k-means).'
          )
  
      # Cleans up and removes duplicate dates.
      new_daily_event_df_dict = {
          key: (
              df[[EVENT_DF_DATE_COL, EVENT_DF_LABEL_COL]]
              .drop_duplicates(EVENT_DF_DATE_COL)
              .reset_index(drop=True)
          )
          for key, df in daily_event_df_dict.items()
      }
      self.result_dict.update(
          {
              "daily_event_df_dict_with_score": daily_event_df_dict,
              "daily_event_df_dict": new_daily_event_df_dict,
              "kde_cutoffs": kde_cutoffs,
              "kde_res": kde_res,
              "kde_plot": kde_plot,
              "kmeans_diagnostics": kmeans_diagnostics,
              "kmeans_plot": kmeans_plot,
          }
      )
  
imports: from datetime import timedelta
  from typing import Any
  from typing import Callable
  from typing import Dict
  from typing import List
  from typing import Optional
  from typing import Tuple
  from typing import Union
  import numpy
  import pandas
  from IPython.display import display
  from sklearn.cluster import KMeans
  from sklearn.metrics import silhouette_score
  from sklearn.neighbors import KernelDensity
  from greykite.algo.common.holiday_inferrer import HolidayInferrer
  from greykite.algo.common.holiday_utils import HOLIDAY_DATE_COL
  from greykite.algo.common.holiday_utils import HOLIDAY_NAME_COL
  from greykite.algo.common.holiday_utils import get_dow_grouped_suffix
  from greykite.algo.common.holiday_utils import get_weekday_weekend_suffix
  from greykite.common.constants import EVENT_DF_DATE_COL
  from greykite.common.constants import EVENT_DF_LABEL_COL
  from greykite.common.logging import LoggingLevelEnum
  from greykite.common.logging import log_message
  from greykite.common.viz.timeseries_plotting import plot_multivariate
label_desc: Use image node_id 2 for calling the HolidayGrouper obj's underlying member method code with example usage: obj.group_holidays(baseline_offsets, use_relative_score, min_n_days, min_same_sign_ratio, min_abs_avg_score, clustering_method, bandwidth, bandwidth_multiplier, n_clusters, include_diagnostics) without return types