class_docstr: Stores predicted and actual values.
  Provides functionality to evaluate a forecast:
  
      - plots true against actual with prediction bands.
      - evaluates model performance.
  
  Input should be one of two kinds of forecast results:
  
      - model fit to train data, forecast on test set (actuals available).
      - model fit to all data, forecast on future dates (actuals not available).
  
  The input ``df`` is a concatenation of fitted and forecasted values.
  
  Attributes
  ----------
  df : `pandas.DataFrame`
      Timestamp, predicted, and actual values.
  time_col : `str`
      Column in ``df`` with timestamp (default "ts").
  actual_col : `str`
      Column in ``df`` with actual values (default "y").
  predicted_col : `str`
      Column in ``df`` with predicted values (default "forecast").
  predicted_lower_col : `str` or None
      Column in ``df`` with predicted lower bound (default "forecast_lower", optional).
  predicted_upper_col : `str` or None
      Column in ``df`` with predicted upper bound (default "forecast_upper", optional).
  null_model_predicted_col : `str` or None
      Column in ``df`` with predicted value of null model (default "forecast_null", optional).
  ylabel : `str`
      Unit of measurement (default "y")
  train_end_date : `str` or `datetime` or None, default None
      End date for train period. If `None`, assumes all data were used for training.
  test_start_date : `str` or `datetime` or None, default None
      Start date of test period. If `None`, set to the ``time_col`` value immediately after
      ``train_end_date``. This assumes that all data not used in training were used for testing.
  forecast_horizon : `int` or None, default None
      Number of periods forecasted into the future. Must be > 0.
  coverage : `float` or None
      Intended coverage of the prediction bands (0.0 to 1.0).
  r2_loss_function : `function`
      Loss function to calculate ``cst.R2_null_model_score``, with signature
      ``loss_func(y_true, y_pred)`` (default mean_squared_error)
  estimator : An instance of an estimator that implements `greykite.models.base_forecast_estimator.BaseForecastEstimator`.
      The fitted estimator, the last step in the forecast pipeline.
  relative_error_tolerance : `float` or None, default None
      Threshold to compute the ``Outside Tolerance`` metric,
      defined as the fraction of forecasted values whose relative
      error is strictly greater than ``relative_error_tolerance``.
      For example, 0.05 allows for 5% relative error.
      If `None`, the metric is not computed.
  df_train : `pandas.DataFrame`
      Subset of ``df`` where ``df[time_col]`` <= ``train_end_date``.
  df_test : `pandas.DataFrame`
      Subset of ``df`` where ``df[time_col]`` > ``train_end_date``.
  train_evaluation : `dict` [`str`, `float`]
      Evaluation metrics on training set.
  test_evaluation : `dict` [`str`, `float`]
      Evaluation metrics on test set (if actual values provided after train_end_date).
  test_na_count : `int`
      Count of NA values in test data.
code: def __init__(
      self,
      df,
      time_col=cst.TIME_COL,
      actual_col=cst.ACTUAL_COL,
      predicted_col=cst.PREDICTED_COL,
      predicted_lower_col=cst.PREDICTED_LOWER_COL,
      predicted_upper_col=cst.PREDICTED_UPPER_COL,
      null_model_predicted_col=cst.NULL_PREDICTED_COL,
      ylabel=cst.VALUE_COL,
      train_end_date=None,
      test_start_date=None,
      forecast_horizon=None,
      coverage=0.95,
      r2_loss_function=mean_squared_error,
      estimator=None,
      relative_error_tolerance=None,
  ):
      if (
          predicted_lower_col is not None
          or predicted_upper_col is not None
      ):
          if coverage is None:
              raise ValueError(
                  "`coverage` must be provided when lower/upper bounds are set"
              )
          elif coverage < 0.0 or coverage > 1.0:
              raise ValueError("`coverage` must be between 0.0 and 1.0")
  
      if train_end_date is not None and not all(
          pd.Series(train_end_date).isin(df[time_col])
      ):
          raise ValueError(
              f"train_end_date {train_end_date} is not found in time column.\n"
              f"The time range in data is: from {min(df[time_col])} to {max(df[time_col])}.\n"
              f"The data size is: {df.shape[0]}.\n"
              f"Type of train_end_date is {type(train_end_date)}.\n"
              f"Type of df[time_col] is {df[time_col].dtype}.\n"
          )
  
      if any(
          [
              col not in df.columns
              for col in [
                  time_col,
                  actual_col,
                  predicted_col,
                  predicted_lower_col,
                  predicted_upper_col,
                  null_model_predicted_col,
              ]
              if col is not None
          ]
      ):
          raise ValueError(f"Column not found in data frame")
  
      self.df = df
      self.time_col = time_col
      self.actual_col = actual_col
      self.predicted_col = predicted_col
      self.predicted_lower_col = predicted_lower_col
      self.predicted_upper_col = predicted_upper_col
      self.null_model_predicted_col = null_model_predicted_col
      self.ylabel = ylabel
      self.train_end_date = train_end_date
      self.test_start_date = test_start_date
      self.forecast_horizon = forecast_horizon
      self.coverage = coverage
      self.r2_loss_function = r2_loss_function
      self.estimator = estimator
      self.relative_error_tolerance = relative_error_tolerance
  
      self.df[self.time_col] = pd.to_datetime(
          df[time_col], infer_datetime_format=True
      )
      if train_end_date is None:
          self.train_end_date = df[self.time_col].max()
      else:
          self.train_end_date = pd.to_datetime(
              train_end_date, infer_datetime_format=True
          )
      self.df_train = df[df[time_col] <= self.train_end_date]
  
      if test_start_date is None:
          # This expects no gaps in time column
          inferred_freq = infer_freq(self.df, self.time_col)
          # Uses pd.date_range because pd.Timedelta does not work for complicated frequencies e.g. "W-MON"
          self.test_start_date = pd.date_range(
              start=self.train_end_date, periods=2, freq=inferred_freq
          )[-1]
      else:
          self.test_start_date = pd.to_datetime(
              test_start_date, infer_datetime_format=True
          )
      self.df_test = df[df[time_col] >= self.test_start_date]
  
      self.test_na_count = self.df_test[actual_col].isna().sum()
  
      # compute evaluation metrics
      self.train_evaluation = None
      self.test_evaluation = None
      self.compute_evaluation_metrics_split()
  
imports: from functools import partial
  import pandas
  from sklearn.metrics import mean_squared_error
  from greykite.common import constants
  from greykite.common.evaluation import ElementwiseEvaluationMetricEnum
  from greykite.common.evaluation import EvaluationMetricEnum
  from greykite.common.evaluation import add_finite_filter_to_scorer
  from greykite.common.evaluation import calc_pred_coverage
  from greykite.common.evaluation import calc_pred_err
  from greykite.common.evaluation import fraction_outside_tolerance
  from greykite.common.evaluation import r2_null_model_score
  from greykite.common.python_utils import apply_func_to_columns
  from greykite.common.time_properties import infer_freq
  from greykite.common.viz.timeseries_plotting import add_groupby_column
  from greykite.common.viz.timeseries_plotting import flexible_grouping_evaluation
  from greykite.common.viz.timeseries_plotting import grouping_evaluation
  from greykite.common.viz.timeseries_plotting import plot_forecast_vs_actual
  from greykite.common.viz.timeseries_plotting import plot_multivariate
  from greykite.common.viz.timeseries_plotting import plot_univariate
  from greykite.framework.input.univariate_time_series import UnivariateTimeSeries
label_desc: Use image node_id 1 to create a new UnivariateForecast object  with example: obj = UnivariateForecast(df, time_col, actual_col, predicted_col, predicted_lower_col, predicted_upper_col, null_model_predicted_col, ylabel, train_end_date, test_start_date, forecast_horizon, coverage, r2_loss_function, estimator, relative_error_tolerance)