class_docstr: A class to implement change point detection.
  
  Currently supports long-term change point detection only. Input is a dataframe with time_col
  indicating the column of time info (the format should be able to be parsed by pd.to_datetime),
  and value_col indicating the column of observed time series values.
  
  Attributes
  ----------
  original_df : `pandas.DataFrame`
      The original data df, used to retrieve original observations, if aggregation is used in
      fitting change points.
  time_col : `str`
      The column name for time column.
  value_col : `str`
      The column name for value column.
  trend_potential_changepoint_n: `int`
      The number of change points that are evenly distributed over the time period.
  yearly_seasonality_order : `int`
      The yearly seasonality order used when fitting trend.
  y : `pandas.Series`
      The observations after aggregation.
  trend_df : `pandas.DataFrame`
      The augmented df of the original_df, including regressors of trend change points and
      Fourier series for yearly seasonality.
  trend_model : `sklearn.base.RegressionMixin`
      The fitted trend model.
  trend_coef : `numpy.array`
      The estimated trend coefficients.
  trend_intercept : `float`
      The estimated trend intercept.
  adaptive_lasso_coef : `list`
      The list of length two, first element is estimated trend coefficients, and second element
      is intercept, both estimated by adaptive lasso.
  trend_changepoints : `list`
      The list of detected trend change points, parsable by pd.to_datetime
  trend_estimation : `pd.Series`
      The estimated trend with detected trend change points.
  seasonality_df : `pandas.DataFrame`
      The augmented df of ``original_df``, including regressors of seasonality change points with
      different Fourier series frequencies.
  seasonality_changepoints : `dict`
      The dictionary of detected seasonality change points for each component.
      Keys are component names, and values are list of change points.
  seasonality_estimation : `pandas.Series`
      The estimated seasonality with detected seasonality change points.
      The series has the same length as ``original_df``. Index is timestamp, and values
      are the estimated seasonality at each timestamp.
      The seasonality estimation is the estimated of seasonality effect with trend estimated
      by `~greykite.algo.changepoint.adalasso.changepoints_utils.estimate_trend_with_detected_changepoints`
      removed.
  
  Methods
  -------
  find_trend_changepoints : callable
      Finds the potential trend change points for a given time series df.
  plot : callable
      Plot the results after implementing find_trend_changepoints.
docstr: Finds trend change points automatically by adaptive lasso.
  
  The algorithm does an aggregation with a user-defined frequency, defaults daily.
  
  If ``potential_changepoint_distance`` is not given,  ``potential_changepoint_n``
  potential change points are evenly distributed over the time period, else
  ``potential_changepoint_n`` is overridden by::
  
          total_time_length / ``potential_changepoint_distance``
  
  Users can specify either ``no_changepoint_proportion_from_end`` to specify what proportion
  from the end of data they do not want changepoints, or ``no_changepoint_distance_from_end``
  (overrides ``no_changepoint_proportion_from_end``) to specify how long from the end they
  do not want change points.
  
  Then all potential change points will be selected by adaptive lasso, with the initial
  estimator specified by ``adaptive_lasso_initial_estimator``. If user specifies
  ``regularization_strength``, then the adaptive lasso will be run with a single tuning
  parameter calculated based on user provided prior, else a cross-validation will be run to
  automatically select the tuning parameter.
  
  A yearly seasonality is also fitted at the same time, preventing trend from catching
  yearly periodical changes.
  
  A rule-based guard function is applied at the end to ensure change points are not
  too close, as specified by ``actual_changepoint_min_distance``.
  
  Parameters
  ----------
  df: `pandas.DataFrame`
      The data df
  time_col : `str`
      Time column name in ``df``
  value_col : `str`
      Value column name in ``df``
  shift_detector: `greykite.algo.changepoint.shift_detection.shift_detector.ShiftDetection`
      An instance of ShiftDetection for identifying level shifts and computing regressors. Level
      shift points will be considered as regressors when selecting change points by adaptive lasso.
  yearly_seasonality_order : `int`, default 8
      Fourier series order to capture yearly seasonality.
  yearly_seasonality_change_freq : `DateOffset`, `Timedelta` or `str` or `None`, default `None`
      How often to change the yearly seasonality model. Set to `None` to disable this feature.
  
      This is useful if you have more than 2.5 years of data and the detected trend without this
      feature is inaccurate because yearly seasonality changes over the training period.
      Modeling yearly seasonality separately over the each period can prevent trend changepoints
      from fitting changes in yearly seasonality. For example, if you have 2.5 years of data and
      yearly seasonality increases in magnitude after the first year, setting this parameter to
      "365D" will model each year's yearly seasonality differently and capture both shapes.
      However, without this feature, both years will have the same yearly seasonality, roughly
      the average effect across the training set.
  
      Note that if you use `str` as input, the maximal supported unit is day, i.e.,
      you might use "200D" but not "12M" or "1Y".
  resample_freq : `DateOffset`, `Timedelta`, `str` or None, default "D".
      The frequency to aggregate data.
      Coarser aggregation leads to fitting longer term trends.
      If None, no aggregation will be done.
  trend_estimator : `str` in ["ridge", "lasso" or "ols"], default "ridge".
      The estimator to estimate trend. The estimated trend is only for plotting purposes.
      'ols' is not recommended when ``yearly_seasonality_order`` is specified other than 0,
      because significant over-fitting will happen.
      In this case, the given value is overridden by "ridge".
  adaptive_lasso_initial_estimator : `str` in ["ridge", "lasso" or "ols"], default "ridge".
      The initial estimator to compute adaptive lasso weights
  regularization_strength : `float` in [0, 1] or `None`
      The regularization for change points. Greater value implies fewer change points.
      0 indicates all change points, and 1 indicates no change point.
      If `None`, the turning parameter will be selected by cross-validation.
      If a value is given, it will be used as the tuning parameter.
  actual_changepoint_min_distance : `DateOffset`, `Timedelta` or `str`, default "30D"
      The minimal distance allowed between detected change points. If consecutive change points
      are within this minimal distance, the one with smaller absolute change coefficient will
      be dropped.
      Note: maximal unit is 'D', i.e., you may use units no more than 'D' such as
      '10D', '5H', '100T', '200S'. The reason is that 'W', 'M' or higher has either
      cycles or indefinite number of days, thus is not parsable by pandas as timedelta.
  potential_changepoint_distance : `DateOffset`, `Timedelta`, `str` or None, default None
      The distance between potential change points.
      If provided, will override the parameter ``potential_changepoint_n``.
      Note: maximal unit is 'D', i.e., you may only use units no more than 'D' such as
      '10D', '5H', '100T', '200S'. The reason is that 'W', 'M' or higher has either
      cycles or indefinite number of days, thus is not parsable by pandas as timedelta.
  potential_changepoint_n : `int`, default 100
      Number of change points to be evenly distributed, recommended 1-2 per month, based
      on the training data length.
  potential_changepoint_n_max : `int` or None, default None
      The maximum number of potential changepoints.
      This parameter is effective when user specifies ``potential_changepoint_distance``,
      and the number of potential changepoints in the training data is more than ``potential_changepoint_n_max``,
      then it is equivalent to specifying ``potential_changepoint_n = potential_changepoint_n_max``,
      and ignoring ``potential_changepoint_distance``.
  no_changepoint_distance_from_begin : `DateOffset`, `Timedelta`, `str` or None, default None
      The length of time from the beginning of training data, within which no change point will be placed.
      If provided, will override the parameter ``no_changepoint_proportion_from_begin``.
      Note: maximal unit is 'D', i.e., you may only use units no more than 'D' such as
      '10D', '5H', '100T', '200S'. The reason is that 'W', 'M' or higher has either
      cycles or indefinite number of days, thus is not parsable by pandas as timedelta.
  no_changepoint_proportion_from_begin : `float` in [0, 1], default 0.0.
      ``potential_changepoint_n`` change points will be placed evenly over the whole training period,
      however, change points that are located within the first ``no_changepoint_proportion_from_begin``
      proportion of training period will not be used for change point detection.
  no_changepoint_distance_from_end : `DateOffset`, `Timedelta`, `str` or None, default None
      The length of time from the end of training data, within which no change point will be placed.
      If provided, will override the parameter ``no_changepoint_proportion_from_end``.
      Note: maximal unit is 'D', i.e., you may only use units no more than 'D' such as
      '10D', '5H', '100T', '200S'. The reason is that 'W', 'M' or higher has either
      cycles or indefinite number of days, thus is not parsable by pandas as timedelta.
  no_changepoint_proportion_from_end : `float` in [0, 1], default 0.0.
      ``potential_changepoint_n`` change points will be placed evenly over the whole training period,
      however, change points that are located within the last ``no_changepoint_proportion_from_end``
      proportion of training period will not be used for change point detection.
  fast_trend_estimation : `bool`, default True
      If True, the trend estimation is not refitted on the original data,
      but is a linear interpolation of the fitted trend from the resampled time series.
      If False, the trend estimation is refitted on the original data.
  
  Return
  ------
  result : `dict`
      result dictionary with keys:
  
      ``"trend_feature_df"`` : `pandas.DataFrame`
          The augmented df for change detection, in other words, the design matrix for
          the regression model. Columns:
  
              - 'changepoint0': regressor for change point 0, equals the continuous time
                of the observation minus the continuous time for time of origin.
              - ...
              - 'changepoint{potential_changepoint_n}': regressor for change point
                {potential_changepoint_n}, equals the continuous time of the observation
                minus the continuous time of the {potential_changepoint_n}th change point.
              - 'cos1_conti_year_yearly': cosine yearly seasonality regressor of first order.
              - 'sin1_conti_year_yearly': sine yearly seasonality regressor of first order.
              - ...
              - 'cos{yearly_seasonality_order}_conti_year_yearly' : cosine yearly seasonality
                regressor of {yearly_seasonality_order}th order.
              - 'sin{yearly_seasonality_order}_conti_year_yearly' : sine yearly seasonality
                regressor of {yearly_seasonality_order}th order.
  
      ``"trend_changepoints"`` : `list`
          The list of detected change points.
      ``"changepoints_dict"`` : `dict`
          The change point dictionary that is compatible as an input with
          `~greykite.algo.forecast.silverkite.forecast_silverkite.SilverkiteForecast.forecast`
      ``"trend_estimation"`` : `pandas.Series`
          The estimated trend with detected trend change points.
code: def find_trend_changepoints(
      self,
      df,
      time_col,
      value_col,
      shift_detector=None,
      yearly_seasonality_order=8,
      yearly_seasonality_change_freq=None,
      resample_freq="D",
      trend_estimator="ridge",
      adaptive_lasso_initial_estimator="ridge",
      regularization_strength=None,
      actual_changepoint_min_distance="30D",
      potential_changepoint_distance=None,
      potential_changepoint_n=100,
      potential_changepoint_n_max=None,
      no_changepoint_distance_from_begin=None,
      no_changepoint_proportion_from_begin=0.0,
      no_changepoint_distance_from_end=None,
      no_changepoint_proportion_from_end=0.0,
      fast_trend_estimation=True,
  ):
      """Finds trend change points automatically by adaptive lasso.
  
      The algorithm does an aggregation with a user-defined frequency, defaults daily.
  
      If ``potential_changepoint_distance`` is not given,  ``potential_changepoint_n``
      potential change points are evenly distributed over the time period, else
      ``potential_changepoint_n`` is overridden by::
  
              total_time_length / ``potential_changepoint_distance``
  
      Users can specify either ``no_changepoint_proportion_from_end`` to specify what proportion
      from the end of data they do not want changepoints, or ``no_changepoint_distance_from_end``
      (overrides ``no_changepoint_proportion_from_end``) to specify how long from the end they
      do not want change points.
  
      Then all potential change points will be selected by adaptive lasso, with the initial
      estimator specified by ``adaptive_lasso_initial_estimator``. If user specifies
      ``regularization_strength``, then the adaptive lasso will be run with a single tuning
      parameter calculated based on user provided prior, else a cross-validation will be run to
      automatically select the tuning parameter.
  
      A yearly seasonality is also fitted at the same time, preventing trend from catching
      yearly periodical changes.
  
      A rule-based guard function is applied at the end to ensure change points are not
      too close, as specified by ``actual_changepoint_min_distance``.
  
      Parameters
      ----------
      df: `pandas.DataFrame`
          The data df
      time_col : `str`
          Time column name in ``df``
      value_col : `str`
          Value column name in ``df``
      shift_detector: `greykite.algo.changepoint.shift_detection.shift_detector.ShiftDetection`
          An instance of ShiftDetection for identifying level shifts and computing regressors. Level
          shift points will be considered as regressors when selecting change points by adaptive lasso.
      yearly_seasonality_order : `int`, default 8
          Fourier series order to capture yearly seasonality.
      yearly_seasonality_change_freq : `DateOffset`, `Timedelta` or `str` or `None`, default `None`
          How often to change the yearly seasonality model. Set to `None` to disable this feature.
  
          This is useful if you have more than 2.5 years of data and the detected trend without this
          feature is inaccurate because yearly seasonality changes over the training period.
          Modeling yearly seasonality separately over the each period can prevent trend changepoints
          from fitting changes in yearly seasonality. For example, if you have 2.5 years of data and
          yearly seasonality increases in magnitude after the first year, setting this parameter to
          "365D" will model each year's yearly seasonality differently and capture both shapes.
          However, without this feature, both years will have the same yearly seasonality, roughly
          the average effect across the training set.
  
          Note that if you use `str` as input, the maximal supported unit is day, i.e.,
          you might use "200D" but not "12M" or "1Y".
      resample_freq : `DateOffset`, `Timedelta`, `str` or None, default "D".
          The frequency to aggregate data.
          Coarser aggregation leads to fitting longer term trends.
          If None, no aggregation will be done.
      trend_estimator : `str` in ["ridge", "lasso" or "ols"], default "ridge".
          The estimator to estimate trend. The estimated trend is only for plotting purposes.
          'ols' is not recommended when ``yearly_seasonality_order`` is specified other than 0,
          because significant over-fitting will happen.
          In this case, the given value is overridden by "ridge".
      adaptive_lasso_initial_estimator : `str` in ["ridge", "lasso" or "ols"], default "ridge".
          The initial estimator to compute adaptive lasso weights
      regularization_strength : `float` in [0, 1] or `None`
          The regularization for change points. Greater value implies fewer change points.
          0 indicates all change points, and 1 indicates no change point.
          If `None`, the turning parameter will be selected by cross-validation.
          If a value is given, it will be used as the tuning parameter.
      actual_changepoint_min_distance : `DateOffset`, `Timedelta` or `str`, default "30D"
          The minimal distance allowed between detected change points. If consecutive change points
          are within this minimal distance, the one with smaller absolute change coefficient will
          be dropped.
          Note: maximal unit is 'D', i.e., you may use units no more than 'D' such as
          '10D', '5H', '100T', '200S'. The reason is that 'W', 'M' or higher has either
          cycles or indefinite number of days, thus is not parsable by pandas as timedelta.
      potential_changepoint_distance : `DateOffset`, `Timedelta`, `str` or None, default None
          The distance between potential change points.
          If provided, will override the parameter ``potential_changepoint_n``.
          Note: maximal unit is 'D', i.e., you may only use units no more than 'D' such as
          '10D', '5H', '100T', '200S'. The reason is that 'W', 'M' or higher has either
          cycles or indefinite number of days, thus is not parsable by pandas as timedelta.
      potential_changepoint_n : `int`, default 100
          Number of change points to be evenly distributed, recommended 1-2 per month, based
          on the training data length.
      potential_changepoint_n_max : `int` or None, default None
          The maximum number of potential changepoints.
          This parameter is effective when user specifies ``potential_changepoint_distance``,
          and the number of potential changepoints in the training data is more than ``potential_changepoint_n_max``,
          then it is equivalent to specifying ``potential_changepoint_n = potential_changepoint_n_max``,
          and ignoring ``potential_changepoint_distance``.
      no_changepoint_distance_from_begin : `DateOffset`, `Timedelta`, `str` or None, default None
          The length of time from the beginning of training data, within which no change point will be placed.
          If provided, will override the parameter ``no_changepoint_proportion_from_begin``.
          Note: maximal unit is 'D', i.e., you may only use units no more than 'D' such as
          '10D', '5H', '100T', '200S'. The reason is that 'W', 'M' or higher has either
          cycles or indefinite number of days, thus is not parsable by pandas as timedelta.
      no_changepoint_proportion_from_begin : `float` in [0, 1], default 0.0.
          ``potential_changepoint_n`` change points will be placed evenly over the whole training period,
          however, change points that are located within the first ``no_changepoint_proportion_from_begin``
          proportion of training period will not be used for change point detection.
      no_changepoint_distance_from_end : `DateOffset`, `Timedelta`, `str` or None, default None
          The length of time from the end of training data, within which no change point will be placed.
          If provided, will override the parameter ``no_changepoint_proportion_from_end``.
          Note: maximal unit is 'D', i.e., you may only use units no more than 'D' such as
          '10D', '5H', '100T', '200S'. The reason is that 'W', 'M' or higher has either
          cycles or indefinite number of days, thus is not parsable by pandas as timedelta.
      no_changepoint_proportion_from_end : `float` in [0, 1], default 0.0.
          ``potential_changepoint_n`` change points will be placed evenly over the whole training period,
          however, change points that are located within the last ``no_changepoint_proportion_from_end``
          proportion of training period will not be used for change point detection.
      fast_trend_estimation : `bool`, default True
          If True, the trend estimation is not refitted on the original data,
          but is a linear interpolation of the fitted trend from the resampled time series.
          If False, the trend estimation is refitted on the original data.
  
      Return
      ------
      result : `dict`
          result dictionary with keys:
  
          ``"trend_feature_df"`` : `pandas.DataFrame`
              The augmented df for change detection, in other words, the design matrix for
              the regression model. Columns:
  
                  - 'changepoint0': regressor for change point 0, equals the continuous time
                    of the observation minus the continuous time for time of origin.
                  - ...
                  - 'changepoint{potential_changepoint_n}': regressor for change point
                    {potential_changepoint_n}, equals the continuous time of the observation
                    minus the continuous time of the {potential_changepoint_n}th change point.
                  - 'cos1_conti_year_yearly': cosine yearly seasonality regressor of first order.
                  - 'sin1_conti_year_yearly': sine yearly seasonality regressor of first order.
                  - ...
                  - 'cos{yearly_seasonality_order}_conti_year_yearly' : cosine yearly seasonality
                    regressor of {yearly_seasonality_order}th order.
                  - 'sin{yearly_seasonality_order}_conti_year_yearly' : sine yearly seasonality
                    regressor of {yearly_seasonality_order}th order.
  
          ``"trend_changepoints"`` : `list`
              The list of detected change points.
          ``"changepoints_dict"`` : `dict`
              The change point dictionary that is compatible as an input with
              `~greykite.algo.forecast.silverkite.forecast_silverkite.SilverkiteForecast.forecast`
          ``"trend_estimation"`` : `pandas.Series`
              The estimated trend with detected trend change points.
      """
      # Checks parameter rationality
      if potential_changepoint_n < 0:
          raise ValueError(
              "potential_changepoint_n can not be negative. "
              "A large number such as 100 is recommended"
          )
      if yearly_seasonality_order < 0:
          raise ValueError(
              "year_seasonality_order can not be negative. "
              "A number less than or equal to 10 is recommended"
          )
      if df.dropna().shape[0] < 5:
          raise ValueError(
              "Change point detector does not work for less than "
              "5 observations. Please increase sample size."
          )
      if (
          no_changepoint_proportion_from_begin < 0
          or no_changepoint_proportion_from_begin > 1
      ):
          raise ValueError(
              "no_changepoint_proportion_from_begin needs to be between 0 and 1."
          )
      if (
          no_changepoint_proportion_from_end < 0
          or no_changepoint_proportion_from_end > 1
      ):
          raise ValueError(
              "no_changepoint_proportion_from_end needs to be between 0 and 1."
          )
      if no_changepoint_distance_from_begin is not None:
          check_freq_unit_at_most_day(
              no_changepoint_distance_from_begin,
              "no_changepoint_distance_from_begin",
          )
          data_length = pd.to_datetime(
              df[time_col].iloc[-1]
          ) - pd.to_datetime(df[time_col].iloc[0])
          no_changepoint_proportion_from_begin = (
              to_offset(no_changepoint_distance_from_begin).delta
              / data_length
          )
          no_changepoint_proportion_from_begin = min(
              no_changepoint_proportion_from_begin, 1
          )
      if no_changepoint_distance_from_end is not None:
          check_freq_unit_at_most_day(
              no_changepoint_distance_from_end,
              "no_changepoint_distance_from_end",
          )
          data_length = pd.to_datetime(
              df[time_col].iloc[-1]
          ) - pd.to_datetime(df[time_col].iloc[0])
          no_changepoint_proportion_from_end = (
              to_offset(no_changepoint_distance_from_end).delta
              / data_length
          )
          no_changepoint_proportion_from_end = min(
              no_changepoint_proportion_from_end, 1
          )
      if potential_changepoint_distance is not None:
          check_freq_unit_at_most_day(
              potential_changepoint_distance,
              "potential_changepoint_distance",
          )
          data_length = pd.to_datetime(
              df[time_col].iloc[-1]
          ) - pd.to_datetime(df[time_col].iloc[0])
          potential_changepoint_n = (
              data_length
              // to_offset(potential_changepoint_distance).delta
          )
          if potential_changepoint_n_max is not None:
              if potential_changepoint_n_max <= 0:
                  raise ValueError(
                      "potential_changepoint_n_max must be a positive integer."
                  )
              if potential_changepoint_n > potential_changepoint_n_max:
                  log_message(
                      message=f"Number of potential changepoints is capped by 'potential_changepoint_n_max' "
                      f"as {potential_changepoint_n_max}. The 'potential_changepoint_distance' "
                      f"{potential_changepoint_distance} is ignored. "
                      f"The original number of changepoints was {potential_changepoint_n}.",
                      level=LoggingLevelEnum.INFO,
                  )
                  potential_changepoint_n = potential_changepoint_n_max
      if regularization_strength is not None and (
          regularization_strength < 0 or regularization_strength > 1
      ):
          raise ValueError(
              "regularization_strength must be between 0.0 and 1.0."
          )
      df = df.copy()
      self.trend_potential_changepoint_n = potential_changepoint_n
      self.time_col = time_col
      self.value_col = value_col
      self.original_df = df
      self.shift_detector = shift_detector
  
      # If a shift detector object is passed to the constructor, we're making Changepoint cognizant of level shifts
      # and it will handle the level shifts as independent regressors.
      if self.shift_detector is not None:
          (
              self.level_shift_cols,
              self.level_shift_df,
          ) = self.shift_detector.detect(
              df.copy(),
              time_col=time_col,
              value_col=value_col,
              forecast_horizon=0,
          )
  
      # Resamples df to get a coarser granularity to get rid of shorter seasonality.
      # The try except below speeds up unnecessary datetime transformation.
      if resample_freq is not None:
          try:
              df_resample = (
                  df.resample(resample_freq, on=time_col)
                  .mean()
                  .reset_index()
              )
          except TypeError:
              df[time_col] = pd.to_datetime(df[time_col])
              df_resample = (
                  df.resample(resample_freq, on=time_col)
                  .mean()
                  .reset_index()
              )
      else:
          df[time_col] = pd.to_datetime(df[time_col])
          df_resample = df.copy()
      # The ``df.resample`` function creates NA when the original df has a missing observation
      # or its value is NA.
      # The estimation algorithm does not allow NA, so we drop those rows.
      df_resample = df_resample.dropna()
      self.original_df[time_col] = df[time_col]
      # Prepares response df.
      y = df_resample[value_col]
      y.index = df_resample[time_col]
      self.y = y
      # Prepares trend feature df.
      # Potential changepoints are placed uniformly among rows without a missing value, after resampling.
      trend_df = build_trend_feature_df_with_changes(
          df=df_resample,
          time_col=time_col,
          changepoints_dict={
              "method": "uniform",
              "n_changepoints": potential_changepoint_n,
          },
      )
      # Gets changepoint features only in range filtered by ``no_changepoint_proportion_from_begin`` and
      # ``no_changepoint_proportion_from_end`` of time period.
      n_changepoints_within_range_begin = int(
          potential_changepoint_n * no_changepoint_proportion_from_begin
      )
      n_changepoints_within_range_end = int(
          potential_changepoint_n
          * (1 - no_changepoint_proportion_from_end)
      )
      if (
          n_changepoints_within_range_begin
          < n_changepoints_within_range_end
      ):
          trend_df = trend_df.iloc[
              :,
              [0]
              + list(
                  range(
                      n_changepoints_within_range_begin + 1,
                      n_changepoints_within_range_end + 1,
                  )
              ),
          ]
      else:
          # Linear growth term only.
          trend_df = trend_df.iloc[:, [0]]
      # Builds yearly seasonality feature df
      if (
          yearly_seasonality_order is not None
          and yearly_seasonality_order > 0
      ):
          self.yearly_seasonality_order = yearly_seasonality_order
          # Gets yearly seasonality changepoints, allowing varying yearly seasonality coefficients
          # to capture yearly seasonality shape change.
          yearly_seasonality_changepoint_dates = get_yearly_seasonality_changepoint_dates_from_freq(
              df=df,
              time_col=time_col,
              yearly_seasonality_change_freq=yearly_seasonality_change_freq,
          )
          long_seasonality_df = (
              build_seasonality_feature_df_with_changes(
                  df=df_resample,
                  time_col=time_col,
                  changepoints_dict=dict(
                      method="custom",
                      dates=yearly_seasonality_changepoint_dates,
                  ),
                  fs_components_df=pd.DataFrame(
                      {
                          "name": [TimeFeaturesEnum.conti_year.value],
                          "period": [1.0],
                          "order": [yearly_seasonality_order],
                          "seas_names": ["yearly"],
                      }
                  ),
              )
          )
          trend_df = pd.concat([trend_df, long_seasonality_df], axis=1)
      # Augment the trend_df with the additional level shift regressors.
      if (
          self.shift_detector is not None
          and len(self.level_shift_cols) > 0
      ):
          # Rename each column to have level shift prefixed in name.
          pad_char, pad_size = (
              0,
              4,
          )  # Left pad the levelshift regressors with 0s for sorting.
          new_col_names = {
              col_name: f"levelshift_{ndx:{pad_char}{pad_size}}_{col_name}"
              for ndx, col_name in enumerate(self.level_shift_cols)
          }
          self.level_shift_df.rename(
              columns=new_col_names, inplace=True
          )
          # Save regressors for concatenation to trend_df.
          time_col_and_regressor_cols = [time_col] + sorted(
              new_col_names.values()
          )
          levelshift_regressors_df = self.level_shift_df[
              time_col_and_regressor_cols
          ]
          # Resample level shift df according to resample frequency
          levelshift_regressors_df_copy = (
              levelshift_regressors_df.copy()
          )
          if resample_freq is not None:
              levelshift_regressors_df_resample = (
                  levelshift_regressors_df_copy.resample(
                      resample_freq, on=time_col
                  )
                  .mean()
                  .reset_index()
              )
          else:
              levelshift_regressors_df_resample = (
                  levelshift_regressors_df_copy
              )
          # Set time column to be the index of the dataframe for level shift regressors.
          levelshift_regressors_df_resample.set_index(
              time_col, inplace=True
          )
          # Concatenate regressors column-wise.
          trend_df = pd.concat(
              [trend_df, levelshift_regressors_df_resample], axis=1
          )
      trend_df.index = df_resample[time_col]
      self.trend_df = trend_df
      # Estimates trend.
      if trend_estimator not in ["ridge", "lasso", "ols"]:
          warnings.warn(
              "trend_estimator not in ['ridge', 'lasso', 'ols'], "
              "estimating using ridge"
          )
          trend_estimator = "ridge"
      if trend_estimator == "ols" and yearly_seasonality_order > 0:
          warnings.warn(
              "trend_estimator = 'ols' with year_seasonality_order > 0 may create "
              "over-fitting, trend_estimator has been set to 'ridge'."
          )
          trend_estimator = "ridge"
      fit_algorithm_dict = {
          "ridge": RidgeCV,
          "lasso": LassoCV,
          "ols": LinearRegression,
      }
      trend_model = fit_algorithm_dict[trend_estimator]().fit(
          trend_df.values, y.values
      )
      self.trend_model = trend_model
      self.trend_coef, self.trend_intercept = (
          trend_model.coef_.ravel(),
          trend_model.intercept_.ravel(),
      )
      # Fetches change point dates for reference as datetime format.
      changepoint_dates = get_evenly_spaced_changepoints_dates(
          df=df_resample,
          time_col=time_col,
          n_changepoints=potential_changepoint_n,
      )
      # Gets the changepoint dates filtered by ``no_changepoint_proportion_from_begin`` and ``no_changepoint_proportion_from_end``.
      if (
          n_changepoints_within_range_begin
          < n_changepoints_within_range_end
      ):
          changepoint_dates = changepoint_dates.iloc[
              [0]
              + list(
                  range(
                      n_changepoints_within_range_begin + 1,
                      n_changepoints_within_range_end + 1,
                  )
              )
          ]
      else:
          # Linear growth term only.
          changepoint_dates = changepoint_dates.iloc[[0]]
      # Calculates the minimal allowed change point index distance.
      min_changepoint_index_distance = compute_min_changepoint_index_distance(
          df=df_resample,
          time_col=time_col,
          n_changepoints=potential_changepoint_n,
          min_distance_between_changepoints=actual_changepoint_min_distance,
      )
      # Uses adaptive lasso to select change points.
      if adaptive_lasso_initial_estimator not in [
          "ridge",
          "lasso",
          "ols",
      ]:
          warnings.warn(
              "adaptive_lasso_initial_estimator not in ['ridge', 'lasso', 'ols'], "
              "estimating with ridge"
          )
          adaptive_lasso_initial_estimator = "ridge"
      if adaptive_lasso_initial_estimator == trend_estimator:
          # When ``adaptive_lasso_initial_estimator`` is the same as ``trend_estimator``, the
          # estimated trend coefficients will be used to calculate the weights. The
          # ``get_trend_changes_from_adaptive_lasso`` function recognizes ``initial_coef`` as
          # `numpy.array` and calculates the weights directly.
          (
              trend_changepoints,
              self.adaptive_lasso_coef,
          ) = get_trend_changes_from_adaptive_lasso(
              x=trend_df.values,
              y=y.values,
              changepoint_dates=changepoint_dates,
              initial_coef=self.trend_coef,
              min_index_distance=min_changepoint_index_distance,
              regularization_strength=regularization_strength,
          )
      else:
          # When ``adaptive_lasso_initial_estimator`` is different from ``trend_estimator``, the
          # ``adaptive_lasso_initial_estimator`` as a `str` will be passed. The
          # ``get_trend_changes_from_adaptive_lasso`` function recognizes ``initial_coef`` as
          # `str` and calculates the initial estimator with the corresponding estimator first
          # then calculates the weights.
          (
              trend_changepoints,
              self.adaptive_lasso_coef,
          ) = get_trend_changes_from_adaptive_lasso(
              x=trend_df.values,
              y=y.values,
              changepoint_dates=changepoint_dates,
              initial_coef=adaptive_lasso_initial_estimator,
              min_index_distance=min_changepoint_index_distance,
              regularization_strength=regularization_strength,
          )
      # Checks if the beginning date is picked as a change point. If yes, drop it, because we
      # always include the growth term in our model.
      trend_changepoints = [
          cp
          for cp in trend_changepoints
          if cp > max(df_resample[time_col][0], df[time_col][0])
      ]
      self.trend_changepoints = trend_changepoints
      # logging
      log_message(
          f"The detected trend change points are\n{trend_changepoints}",
          LoggingLevelEnum.INFO,
      )
      # Creates changepoints_dict for silverkite to use.
      changepoints_dict = {
          "method": "custom",
          "dates": trend_changepoints,
      }
      # Computes trend estimates for seasonality use.
      if fast_trend_estimation:
          # Fast calculation of trend estimation.
          # Do not fit trend again on the original df.
          # This is much faster when the original df has small frequencies.
          # Uses linear interpolation on the trend fitted with the resampled df.
          trend_estimation = (
              np.matmul(
                  trend_df.values[:, : (len(trend_changepoints) + 1)],
                  trend_model.coef_[: (len(trend_changepoints) + 1)],
              )
              + trend_model.intercept_
          )
          trend_estimation = pd.DataFrame(
              {
                  time_col: df_resample[time_col],
                  "trend": trend_estimation,
              }
          )
          trend_estimation = trend_estimation.merge(
              df[[time_col]], on=time_col, how="right"
          )
          trend_estimation["trend"].interpolate(inplace=True)
          trend_estimation.index = df[time_col]
          trend_estimation = trend_estimation["trend"]
      else:
          trend_estimation = estimate_trend_with_detected_changepoints(
              df=df,
              time_col=time_col,
              value_col=value_col,
              changepoints=trend_changepoints,
          )
      self.trend_estimation = trend_estimation
      result = {
          "trend_feature_df": trend_df,
          "trend_changepoints": trend_changepoints,
          "changepoints_dict": changepoints_dict,
          "trend_estimation": trend_estimation,
      }
      return result
  
imports: import warnings
  from datetime import timedelta
  from typing import List
  from typing import Optional
  import numpy
  import pandas
  from pandas.tseries.frequencies import to_offset
  from sklearn.base import RegressorMixin
  from sklearn.exceptions import ConvergenceWarning
  from sklearn.linear_model import LassoCV
  from sklearn.linear_model import LinearRegression
  from sklearn.linear_model import RidgeCV
  from greykite.algo.changepoint.adalasso.changepoints_utils import build_seasonality_feature_df_with_changes
  from greykite.algo.changepoint.adalasso.changepoints_utils import build_trend_feature_df_with_changes
  from greykite.algo.changepoint.adalasso.changepoints_utils import check_freq_unit_at_most_day
  from greykite.algo.changepoint.adalasso.changepoints_utils import combine_detected_and_custom_trend_changepoints
  from greykite.algo.changepoint.adalasso.changepoints_utils import compute_fitted_components
  from greykite.algo.changepoint.adalasso.changepoints_utils import compute_min_changepoint_index_distance
  from greykite.algo.changepoint.adalasso.changepoints_utils import estimate_seasonality_with_detected_changepoints
  from greykite.algo.changepoint.adalasso.changepoints_utils import estimate_trend_with_detected_changepoints
  from greykite.algo.changepoint.adalasso.changepoints_utils import get_changepoint_dates_from_changepoints_dict
  from greykite.algo.changepoint.adalasso.changepoints_utils import get_seasonality_changes_from_adaptive_lasso
  from greykite.algo.changepoint.adalasso.changepoints_utils import get_trend_changes_from_adaptive_lasso
  from greykite.algo.changepoint.adalasso.changepoints_utils import get_yearly_seasonality_changepoint_dates_from_freq
  from greykite.algo.changepoint.adalasso.changepoints_utils import plot_change
  from greykite.algo.changepoint.shift_detection.shift_detector import ShiftDetection
  from greykite.common.constants import TimeFeaturesEnum
  from greykite.common.features.timeseries_features import get_evenly_spaced_changepoints_dates
  from greykite.common.logging import LoggingLevelEnum
  from greykite.common.logging import log_message
  from greykite.common.logging import pprint
  from greykite.common.python_utils import ignore_warnings
label_desc: Use image node_id 2 for calling the ChangepointDetector obj's underlying member method code with example usage: obj.find_trend_changepoints(df, time_col, value_col, shift_detector, yearly_seasonality_order, yearly_seasonality_change_freq, resample_freq, trend_estimator, adaptive_lasso_initial_estimator, regularization_strength, actual_changepoint_min_distance, potential_changepoint_distance, potential_changepoint_n, potential_changepoint_n_max, no_changepoint_distance_from_begin, no_changepoint_proportion_from_begin, no_changepoint_distance_from_end, no_changepoint_proportion_from_end, fast_trend_estimation) and returns: result