class_docstr: This class enables anomaly detection algorithms which use baseline
  forecasts in their logic.
  
  The class assumes that for a given dataset (`df`) which can for example include
  timestamps and observed values for a timeseries as well as anomaly labels (optional),
  a number of (say k) forecasts are given.
  The goal is to use those k forecasts as baselines to detect anomalies.
  
  To that end this class implements joining of the k forecasts with the observed
  data (`df`), thus producing k joined dataframes.
  
  This class facilitates the join by implementing it by using the input `join_cols`
  using its `join_with_forecasts` method within the class.
  In order to use this class user only required to implement these three methods:
  
      - ``add_features_one_df``: Note that the class uses this method to fully implement
          ``add_features`` because the class can assume that the same function can
          be used for all k forecasts.
      - ``calc_with_param``: the prediction logic, assuming the optimal param
          is determined
      - ``fit``: The fit method
  
  This class also already implements ``prep_df_for_predict`` which is basically
  a combination of
  
  - Joining with baselines: ``join_with_forecasts``
  - Adding features: ``add_features``
  
  In this way, with minimal implementation, one can implement a large variety of
  "forecast based" anomaly detectors.
  
  
  Parameters
  ----------
  value_cols : `list` [`str`] or None
      The columns for the response metric (which can be multivariate).
      If not None, and also ``pred_cols`` (below) also not None, we expect them
      to be ordered consistently.
  pred_cols : `list` [`str`] or None
      The columns for the response metric (which can be multivariate).
      If not None, and also ``value_cols`` (above) also not None, we expect them
      to be ordered consistently.
  is_anomaly_col : `str` or None
  join_cols : `list` [`str`] or None
  reward : See docstring for
      `~/greykite.detection.detector.detector.Detector`
  anomaly_percent_dict : See docstring for
      `~/greykite.detection.detector.detector.Detector`
  param_iterable : See docstring for
      `~/greykite.detection.detector.detector.Detector`
  
  Attributes
  ----------
  Solely inherited from `~/greykite.detection.detector.detector.Detector`
docstr: Adds features to `joined_dfs` passed.
  Note that if nothing is passed, this will update ``self.joined_dfs``
  
  Parameters
  ----------
  joined_dfs : `list` [`pandas.DataFrame`] or None, default None
      A list of dataframes. If None, ``self.joined_dfs`` will be
      used as input.
  
  Returns
  -------
  joined_dfs : `list` [`pandas.DataFrame`]
      The resulting list of dataframes.
docstr_tok: ["Adds","features","to","`","joined_dfs","`","passed",".","Note","that","if","nothing","is","passed",",","this","will","update","``","self.joined_dfs","``","Parameters","--","--","--","--","--","joined_dfs",":","`","list","`","[","`","pandas.DataFrame","`","]","or","None",",","default","None","A","list","of","dataframes",".","If","None",",","``","self.joined_dfs","``","will","be","used","as","input",".","Returns","--","--","--","-","joined_dfs",":","`","list","`","[","`","pandas.DataFrame","`","]","The","resulting","list","of","dataframes","."]
code: def add_features(self, joined_dfs=None):
      """Adds features to `joined_dfs` passed.
      Note that if nothing is passed, this will update ``self.joined_dfs``
  
      Parameters
      ----------
      joined_dfs : `list` [`pandas.DataFrame`] or None, default None
          A list of dataframes. If None, ``self.joined_dfs`` will be
          used as input.
  
      Returns
      -------
      joined_dfs : `list` [`pandas.DataFrame`]
          The resulting list of dataframes.
  
      """
      if joined_dfs is None:
          joined_dfs = self.joined_dfs
  
      if joined_dfs is None:
          raise ValueError(
              "`joined_dfs` cannot be None."
              "`join_with_forecasts` method is to be called before"
          )
  
      for k, joined_df in joined_dfs.items():
          self.add_features_one_df(joined_df)
      return joined_dfs
  
imports: from greykite.detection.detector.detector import Detector
label_desc: Use image node_id 4 for calling the ForecastBasedDetector obj's underlying member method code with example usage: obj.add_features(joined_dfs) and returns: joined_dfs