class_docstr: Implementation of the classic statistical model SARIMA (Seasonal
  AutoRegressive Integrated Moving Average) for forecasting.
code: def _forecast_with_exog(
      self,
      time_stamps: List[int],
      time_series_prev: pd.DataFrame = None,
      return_prev=False,
      exog_data: pd.DataFrame = None,
      exog_data_prev: pd.DataFrame = None,
  ) -> Tuple[pd.DataFrame, pd.DataFrame]:
      # If there is a time_series_prev, use it to set the SARIMA model's state, and then obtain its forecast
      if time_series_prev is None:
          last_val = self._last_val
          model = self.model
      else:
          val_prev = time_series_prev.iloc[
              -self._max_lookback :, self.target_seq_index
          ]
          last_val = val_prev[-1]
          exog_data_prev = (
              None
              if exog_data_prev is None
              else exog_data_prev.loc[val_prev.index]
          )
          with warnings.catch_warnings():
              warnings.simplefilter("ignore")
              model = self.model.apply(
                  val_prev,
                  exog=exog_data_prev,
                  validate_specification=False,
              )
  
      try:
          forecast_result = model.get_forecast(
              len(time_stamps), exog=exog_data
          )
          pred = np.asarray(forecast_result.predicted_mean)
          err = np.asarray(forecast_result.se_mean)
          assert len(pred) == len(
              time_stamps
          ), f"Expected SARIMA to return forecast of length {len(time_stamps)}, but got {len(pred)} instead."
      except Exception as e:
          logger.warning(f"Caught {type(e).__name__}: {str(e)}")
          pred = np.full(len(time_stamps), last_val)
          err = np.zeros(len(time_stamps))
  
      if time_series_prev is not None and return_prev:
          m = len(time_series_prev) - len(val_prev)
          params = dict(zip(model.param_names, model.params))
          err_prev = np.concatenate(
              (
                  np.zeros(m),
                  np.full(len(val_prev), np.sqrt(params["sigma2"])),
              )
          )
          pred = np.concatenate(
              (
                  time_series_prev.values[:m, self.target_seq_index],
                  val_prev - model.resid,
                  pred,
              )
          )
          err = np.concatenate((err_prev, err))
          time_stamps = np.concatenate(
              (to_timestamp(time_series_prev.index), time_stamps)
          )
  
      # Check for NaN's
      if any(np.isnan(pred)):
          logger.warning(
              "Trained SARIMA model producing NaN forecast. Using last training point as the prediction."
          )
          pred[np.isnan(pred)] = last_val
      if any(np.isnan(err)):
          err[np.isnan(err)] = 0
  
      # Return the forecast & its standard error
      name = self.target_name
      pred = pd.DataFrame(
          pred, index=to_pd_datetime(time_stamps), columns=[name]
      )
      err = pd.DataFrame(
          err,
          index=to_pd_datetime(time_stamps),
          columns=[f"{name}_err"],
      )
      return pred, err
  
imports: import logging
  import warnings
  from typing import List, Tuple
  import numpy
  import pandas
  import statsmodels.api
  from merlion.models.automl.seasonality import SeasonalityModel
  from merlion.models.forecast.base import ForecasterExogBase, ForecasterExogConfig
  from merlion.transform.resample import TemporalResample
  from merlion.utils.time_series import UnivariateTimeSeries, to_pd_datetime, to_timestamp
label_desc: Use image node_id 8 for calling the Sarima obj's underlying member method code with example usage: obj._forecast_with_exog(time_stamps, time_series_prev, return_prev, exog_data, exog_data_prev) and returns: pred,  err