code: def filter_changepoints(cp_blocks, coef, min_index_distance):
      """Filters change points that are too close.
  
      Given the ``cp_block`` from the output of ``find_neighbor_changepoints`` and the corresponding
      coefficients, finds the list of change points, one in each neighborhood list.
  
      The algorithm keeps all individual change points. If more than one change points are in the
      same block, with the maximum distance less than ``min_index_distance``, then only the one
      with the maximum absolute coefficient is retained. If more than one change points are in the
      same block, and the block covers a few ``max_index_distance`` length, then a greedy method
      is used to select change points. The principle is that, we perform one pass of the change
      points, within each ``max_index_distance``, we select one change point with the maximum
      absolute coefficients. If the next change point is with in ``max_index_distance`` of the
      previous one, the previous one is dropped. A back-tracking is also used to fill possible
      change points when a change point is dropped.
  
      Parameters
      ----------
      cp_blocks : `list`
          A list of list of change points, output from ``find_neighbor_changepoints``.
      coef : `numpy.array`
          The estimated coefficients for all potential change points.
          Note: ``coef`` is a 1-D array, which is the output from regression model.
      min_index_distance : `int`
          The minimum index distance between two selected change points.
          Note that this parameter is added to safe guard the extreme cases: if all
          significant change points are included in the same block, with insignificant
          change points connecting them, then we do not want to drop all and just leave
          one change points. With this parameter, we can leave more than one change point
          from each block, and keep them at least ``min_index_distance`` away.
  
      Returns
      -------
      changepoint_indices : `list`
          The indices of selected change points.
      """
      if min_index_distance <= 0:
          raise ValueError(
              "`min_index_distance` is the minimum distance between change point"
              "indices to consider them separate, and must be positive."
          )
      if min_index_distance == 1:
          return [cp for block in cp_blocks for cp in block]
      coef = coef.ravel()  # makes sure ``coef`` is 1-D array
      coef = coef[
          [i for block in cp_blocks for i in block]
      ]  # Only needs the coefs that correspond to selected cp's.
      selected_changepoints = []
      if cp_blocks == [[]]:
          return []
      for i, block in enumerate(cp_blocks):
          if len(block) == 1:
              # if only one cp in a block, leaves it
              selected_changepoints.append(block[0])
          else:
              # needs coefs to decide
              coef_start = sum([len(block) for block in cp_blocks[:i]])
              coef_block = coef[coef_start : coef_start + len(block)]
              if block[-1] - block[0] < min_index_distance:
                  # If block max distance is less than `min_index_distance`,
                  # only one cp can be selected,
                  # select the one with the greatest absolute coef
                  selected_changepoints.append(
                      block[abs(coef_block).argmax()]
                  )
              else:
                  # For longer blocks, the change points with the maximum
                  # absolute coefficients are selected, while keeping the
                  # distance beyond ``min_index_distance``.
                  # This prevents dropping too many change points in a long block.
                  block_cps = []
                  start = block[0]
                  last_coef = 0
                  while start <= block[-1]:
                      # gets the current ``min_index_distance`` sized sub-block of cps and coefs
                      current_sub_block = [
                          cp
                          for cp in block
                          if start <= cp < start + min_index_distance
                      ]
                      if not current_sub_block:
                          start += min_index_distance
                          continue
                      current_cp = current_sub_block[
                          abs(
                              coef_block[
                                  [
                                      block.index(cp)
                                      for cp in current_sub_block
                                  ]
                              ]
                          ).argmax()
                      ]
                      current_cp_idx = block.index(current_cp)
                      current_coef = coef_block[current_cp_idx]
                      # the first change point, doesn't have to look back
                      if start == block[0]:
                          block_cps.append(current_cp)
                      else:
                          # check the distance from the last change point
                          # if distance is enough, we can fit the new change point
                          last_cp = block_cps[-1]
                          if current_cp - last_cp >= min_index_distance:
                              block_cps.append(current_cp)
                          # if the distance is not enough and the new cp has a greater absolute coef,
                          # we have to remove the previous one with less absolute coef
                          # however, we would like to see if another cp fits in between
                          elif abs(current_coef) > abs(last_coef):
                              # checks if an extra change point can fit between last_last_cp and current_cp
                              if len(block_cps) >= 2:
                                  last_last_cp = block_cps[-2]
                              else:
                                  # if no last_last_cp, there's not lower bound
                                  last_last_cp = -min_index_distance
                              # gets the cps that can fit between last_last_cp and current_cp
                              back_fill_potential_cp = [
                                  cp
                                  for cp in block
                                  if last_last_cp + min_index_distance
                                  <= cp
                                  <= current_cp - min_index_distance
                              ]
                              if back_fill_potential_cp:
                                  # if there is cp that can fit in between, gets the one with max absolute coef
                                  potential_coef = coef[
                                      [
                                          block.index(cp)
                                          for cp in back_fill_potential_cp
                                      ]
                                  ]
                                  back_fill_cp = back_fill_potential_cp[
                                      abs(potential_coef).argmax()
                                  ]
                                  # append the cp in between
                                  block_cps.append(back_fill_cp)
                              # removes the last one and append the new one
                              block_cps.remove(last_cp)
                              block_cps.append(current_cp)
                      # updates last_coef for comparison
                      last_coef = current_coef
                      # search ahead
                      start += min_index_distance
                  selected_changepoints += block_cps
      return selected_changepoints
  
imports: import warnings
  from datetime import timedelta
  import numpy
  import pandas
  import plotly.graph_objects
  from pandas.plotting import register_matplotlib_converters
  from pandas.tseries.frequencies import to_offset
  from sklearn.linear_model import Lasso
  from sklearn.linear_model import LassoCV
  from sklearn.linear_model import LinearRegression
  from sklearn.linear_model import RidgeCV
  from greykite.common.constants import CHANGEPOINT_COL_PREFIX
  from greykite.common.constants import TimeFeaturesEnum
  from greykite.common.features.timeseries_features import add_time_features_df
  from greykite.common.features.timeseries_features import build_time_features_df
  from greykite.common.features.timeseries_features import fourier_series_multi_fcn
  from greykite.common.features.timeseries_features import get_changepoint_dates_from_changepoints_dict
  from greykite.common.features.timeseries_features import get_changepoint_features
  from greykite.common.features.timeseries_features import get_changepoint_features_and_values_from_config
  from greykite.common.features.timeseries_features import get_default_origin_for_time_vars
  from greykite.common.python_utils import get_pattern_cols
  from greykite.common.python_utils import unique_elements_in_list
label_desc: Use image node_id 11 for calling a global function with example usage: filter_changepoints(cp_blocks, coef, min_index_distance) and returns: selected_changepoints, unknown, list