class_docstr: A class to infer appropriate Fourier series orders in different
  seasonality components.
  
  The method allows users to:
  
      - optionally remove the trend with different methods.
        Available methods are in `TrendAdjustMethodEnum`.
      - optionally do an aggregation.
      - fits the seasonality component with different Fourier series orders.
      - calculates the AIC/BIC of the fits.
      - choose the most appropriate order with AIC or BIC and an optional tolerance.
      - plot the investigations.
  
  Attributes
  ----------
  df : `pandas.DataFrame` or None
      The input timeseries.
  time_col : `str` or None
      The column name for timestamps in ``df``.
  value_col : `str` or None
      The column name for values in ``df``.
  fourier_series_orders : `dict` or None
      The inferred Fourier series orders.
      The keys are the seasonality component names.
      The values are the inferred best orders according to the config.
  df_features : `pandas.DataFrame` or None
      The cached dataframe with time features.
      Building this df is slow for large dataset.
      We cache it the first time we build it for subsequent uses.
docstr: Fits the seasonality with Fourier series and find the best order according to a criterion.
  
  Parameters
  ----------
  df : `pandas.DataFrame`
      The input time series after trend adjustment.
  time_col : `str`
      The column name for timestamps in ``df``.
  value_col : `str`
      The column name for values in ``df``.
  seas_name : `str`
      The name for the seasonality component.
  col_name : `str`
      The column name for the column used to build Fourier series.
      Must be in ``df`` or can be generated by
      `~greykite.common.features.timeseries_features.build_time_features_df`.
      See `~greykite.common.features.timeseries_features.fourier_series_multi_func`.
  period : `float`
      The period corresponding to ``col_name``.
      See `~greykite.common.features.timeseries_features.fourier_series_multi_func`.
  max_order : `int`
      The maximum Fourier series order to fit.
  fit_algorithm : `str`, default "ridge"
      The algorithm used to fit the seasonality.
      Supported algorithms are "linear", "ridge" and "sgd".
  plotting : `bool`, default False
      Whether to generate plots.
      If True, the returned dictionary will have plot via the "fig" key.
      Can turn this off to speed up the process.
  tolerance : `float`, default 0.0
      A tolerance on the criterion to allow a smaller order.
      For example, if AIC's minimum is 100 and ``tolerance`` is 0.1,
      then the function will find the smallest order that has AIC less than or equal to 110.
  offset : `int`, default 0
      The offset to be added to the best orders.
  
  Returns
  -------
  result : `dict`
      A result dictionary with the following keys:
  
          - "seas_name": the seasonality name.
          - "orders": the Fourier series orders fitted.
          - "aics": the fitted AICs.
          - "bics": the fitted BICs.
          - "best_aic_order": the order corresponding to the best feasible AIC.
          - "best_bic_order": the order corresponding to the best feasible BIC.
          - "fig": the diagnostic figure.
docstr_tok: ["Fits","the","seasonality","with","Fourier","series","and","find","the","best","order","according","to","a","criterion",".","Parameters","--","--","--","--","--","df",":","`","pandas.DataFrame","`","The","input","time","series","after","trend","adjustment",".","time_col",":","`","str","`","The","column","name","for","timestamps","in","``","df","``",".","value_col",":","`","str","`","The","column","name","for","values","in","``","df","``",".","seas_name",":","`","str","`","The","name","for","the","seasonality","component",".","col_name",":","`","str","`","The","column","name","for","the","column","used","to","build","Fourier","series",".","Must","be","in","``","df","``","or","can","be","generated","by","`","~greykite.common.features.timeseries_features.build_time_features_df","`",".","See","`","~greykite.common.features.timeseries_features.fourier_series_multi_func","`",".","period",":","`","float","`","The","period","corresponding","to","``","col_name","``",".","See","`","~greykite.common.features.timeseries_features.fourier_series_multi_func","`",".","max_order",":","`","int","`","The","maximum","Fourier","series","order","to","fit",".","fit_algorithm",":","`","str","`",",","default","``","ridge","''","The","algorithm","used","to","fit","the","seasonality",".","Supported","algorithms","are","``","linear","''",",","``","ridge","''","and","``","sgd","''",".","plotting",":","`","bool","`",",","default","False","Whether","to","generate","plots",".","If","True",",","the","returned","dictionary","will","have","plot","via","the","``","fig","''","key",".","Can","turn","this","off","to","speed","up","the","process",".","tolerance",":","`","float","`",",","default","0.0","A","tolerance","on","the","criterion","to","allow","a","smaller","order",".","For","example",",","if","AIC","'s","minimum","is","100","and","``","tolerance","``","is","0.1",",","then","the","function","will","find","the","smallest","order","that","has","AIC","less","than","or","equal","to","110.","offset",":","`","int","`",",","default","0","The","offset","to","be","added","to","the","best","orders",".","Returns","--","--","--","-","result",":","`","dict","`","A","result","dictionary","with","the","following","keys",":","-","``","seas_name","''",":","the","seasonality","name",".","-","``","orders","''",":","the","Fourier","series","orders","fitted",".","-","``","aics","''",":","the","fitted","AICs",".","-","``","bics","''",":","the","fitted","BICs",".","-","``","best_aic_order","''",":","the","order","corresponding","to","the","best","feasible","AIC",".","-","``","best_bic_order","''",":","the","order","corresponding","to","the","best","feasible","BIC",".","-","``","fig","''",":","the","diagnostic","figure","."]
code: def _gets_seasonality_fitting_metrics(
      self,
      df: pd.DataFrame,
      time_col: str,
      value_col: str,
      seas_name: str,
      col_name: str,
      period: float,
      max_order: int,
      fit_algorithm: str = "ridge",
      plotting: bool = False,
      tolerance: float = 0.0,
      offset: int = 0,
  ) -> dict:
      """Fits the seasonality with Fourier series and find the best order according to a criterion.
  
      Parameters
      ----------
      df : `pandas.DataFrame`
          The input time series after trend adjustment.
      time_col : `str`
          The column name for timestamps in ``df``.
      value_col : `str`
          The column name for values in ``df``.
      seas_name : `str`
          The name for the seasonality component.
      col_name : `str`
          The column name for the column used to build Fourier series.
          Must be in ``df`` or can be generated by
          `~greykite.common.features.timeseries_features.build_time_features_df`.
          See `~greykite.common.features.timeseries_features.fourier_series_multi_func`.
      period : `float`
          The period corresponding to ``col_name``.
          See `~greykite.common.features.timeseries_features.fourier_series_multi_func`.
      max_order : `int`
          The maximum Fourier series order to fit.
      fit_algorithm : `str`, default "ridge"
          The algorithm used to fit the seasonality.
          Supported algorithms are "linear", "ridge" and "sgd".
      plotting : `bool`, default False
          Whether to generate plots.
          If True, the returned dictionary will have plot via the "fig" key.
          Can turn this off to speed up the process.
      tolerance : `float`, default 0.0
          A tolerance on the criterion to allow a smaller order.
          For example, if AIC's minimum is 100 and ``tolerance`` is 0.1,
          then the function will find the smallest order that has AIC less than or equal to 110.
      offset : `int`, default 0
          The offset to be added to the best orders.
  
      Returns
      -------
      result : `dict`
          A result dictionary with the following keys:
  
              - "seas_name": the seasonality name.
              - "orders": the Fourier series orders fitted.
              - "aics": the fitted AICs.
              - "bics": the fitted BICs.
              - "best_aic_order": the order corresponding to the best feasible AIC.
              - "best_bic_order": the order corresponding to the best feasible BIC.
              - "fig": the diagnostic figure.
  
      """
      # Checks inputs.
      if (
          col_name not in df.columns
          and col_name
          not in TimeFeaturesEnum.__dict__["_member_names_"]
      ):
          raise ValueError(
              f"The column name '{col_name}' is not found in ``df`` or time features."
          )
      if max_order <= 0 or int(max_order) != max_order:
          raise ValueError(
              f"The max order must be a positive integer, found {max_order}."
          )
      models = {
          "ridge": RidgeCV,
          "linear": LinearRegression,
          "sgd": SGDRegressor,
      }
      if fit_algorithm not in models:
          raise ValueError(
              f"The fit_algorithm '{fit_algorithm}' is not supported. "
              f"Must be one of {list(models.keys())}."
          )
      model_class = models[fit_algorithm]
  
      if tolerance < 0:
          raise ValueError(
              f"The tolerance percentage must be non-negative, found {tolerance}."
          )
  
      # Generates Fourier series.
      fourier_func = fourier_series_multi_fcn(
          col_names=[col_name],
          periods=[period],
          orders=[max_order],
          seas_names=[seas_name],
      )
      if col_name not in df.columns:
          df = add_time_features_df(
              df=df,
              time_col=time_col,
              conti_year_origin=get_default_origin_for_time_vars(
                  df=df, time_col=time_col
              ),
          )
      seasonality_df = fourier_func(df)["df"]
      df_features = pd.concat(
          [
              df[[value_col]].reset_index(drop=True),
              seasonality_df.reset_index(drop=True),
          ],
          axis=1,
      )
      df_features.dropna(
          inplace=True
      )  # In case ``df_features`` has NANs and fit fails.
  
      # Fits the models and calculates the metrics.
      # Defines a function to fit seasonality model for a single order.
      # This is later used with the `map` method to fast calculate the AIC/BICs for
      # all orders.
      # See https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Loops
      def fit_seasonality_model(order: int) -> (float, float):
          """A function to take a single order and fit
          the seasonality model. Then it calculates the AIC/BIC of the model.
  
          Parameters
          ----------
          order : `int`
              The Fourier series order.
  
          Returns
          -------
          result : `tuple`
              (AIC, BIC) score for the model.
          """
          model = model_class()
          features = [
              col
              for col in df_features.columns
              if col != value_col
              # After split, the 1st element is sin{order} or cos{order}.
              # Takes the order to compare with the current order.
              and int(col.split("_")[0][3:]) <= order
          ]
          model.fit(df_features[features], df_features[value_col])
          pred = model.predict(df_features[features])
          mse = np.mean((df_features[value_col] - pred) ** 2)
          # The number of features in the current model is "2 * order".
          # In the i.i.d. Gaussian case, the -2 * loglikelihood reduces to n * log(MSE).
          aic = 2 * 2 * order + len(df_features) * np.log(mse)
          bic = np.log(len(df_features)) * 2 * order + len(
              df_features
          ) * np.log(mse)
          return aic, bic
  
      # Uses `map` to fast calculate the AIC/BICs.
      orders = list(range(1, max_order + 1))
      abics = list(map(fit_seasonality_model, orders))
      aics = [abic[0] for abic in abics]
      bics = [abic[1] for abic in abics]
  
      min_aic = min(aics)
      min_bic = min(bics)
  
      # If a tolerance is allowed, the search will sacrifice some criterion
      # to use a smaller order of Fourier series.
      if tolerance > 0:
          aic_tol = min_aic + abs(min_aic) * tolerance
          bic_tol = min_bic + abs(min_bic) * tolerance
          # Eligible criteria are those that are less than or equal to the tolerance.
          # The original minimums are guaranteed to be in the lists.
          eligible_aics = [aic for aic in aics if aic <= aic_tol]
          eligible_bics = [bic for bic in bics if bic <= bic_tol]
          # The first element is guaranteed to have order no greater than the original minimum.
          min_aic = eligible_aics[0]
          min_bic = eligible_bics[0]
  
      best_aic_order = max(orders[aics.index(min_aic)] + offset, 0)
      best_bic_order = max(orders[bics.index(min_bic)] + offset, 0)
  
      # Gets the optional plot.
      fig = None
      if plotting:
          fig = self._make_plot(
              df=df,
              df_features=df_features,
              model_class=model_class,
              time_col=time_col,
              value_col=value_col,
              orders=orders,
              aics=aics,
              bics=bics,
              best_aic_order=best_aic_order,
              best_bic_order=best_bic_order,
          )
  
      return {
          "seas_name": seas_name,
          "orders": orders,
          "aics": aics,
          "bics": bics,
          "best_aic_order": best_aic_order,
          "best_bic_order": best_bic_order,
          "fig": fig,
      }
  
imports: from dataclasses import dataclass
  from enum import Enum
  from typing import List
  from typing import Optional
  import numpy
  import pandas
  from plotly import graph_objs
  from plotly.colors import DEFAULT_PLOTLY_COLORS
  from plotly.subplots import make_subplots
  from sklearn.linear_model import LinearRegression
  from sklearn.linear_model import RidgeCV
  from sklearn.linear_model import SGDRegressor
  from greykite.common.constants import TIME_COL
  from greykite.common.constants import VALUE_COL
  from greykite.common.constants import TimeFeaturesEnum
  from greykite.common.features.timeseries_features import add_time_features_df
  from greykite.common.features.timeseries_features import fourier_series_multi_fcn
  from greykite.common.features.timeseries_features import get_default_origin_for_time_vars
label_desc: Use image node_id 7 for calling the SeasonalityInferrer obj's underlying member method code with example usage: obj._gets_seasonality_fitting_metrics(df, time_col, value_col, seas_name, col_name, period, max_order, fit_algorithm, plotting, tolerance, offset) and returns: dict, aic,  bic