code: def test_ensemble(self):
      print("-" * 80)
      logger.info("test_ensemble\n" + "-" * 80 + "\n")
  
      csv_name = join(rootdir, "data", "example.csv")
      ts = csv_to_time_series(
          csv_name, timestamp_unit="ms", data_cols=["kpi"]
      ).align(granularity="1h")
      n_test = len(ts) // 5
      train, test = ts[:-n_test], ts[-n_test:]
  
      # Construct ensemble to forecast up to 120hr in the future
      n = 120
      kwargs = dict(
          max_forecast_steps=n, transform=Identity(), refit=False
      )
      model0 = ETS(
          ETSConfig(
              error="add", trend="add", damped_trend=True, **kwargs
          )
      )
      model1 = ETS(
          ETSConfig(
              error="mul", trend="mul", damped_trend=True, **kwargs
          )
      )
      model2 = ETS(
          ETSConfig(
              error="mul", trend="add", damped_trend=False, **kwargs
          )
      )
      ensemble = ForecasterEnsemble(
          config=ForecasterEnsembleConfig(
              combiner=MetricWeightedMean(metric=ForecastMetric.sMAPE)
          ),
          models=[model0, model1, model2],
      )
  
      # Set up evaluator & run it on the data
      evaluator = ForecastEvaluator(
          model=ensemble,
          config=ForecastEvaluatorConfig(
              retrain_freq="7d",
              horizon="5d",
              cadence=0,
              train_window=None,
          ),
      )
  
      _, pred = evaluator.get_predict(train_vals=train, test_vals=test)
      self.assertIsInstance(pred, list)
      self.assertEqual(len(pred), len(test))
  
      # Compute ensemble's sMAPE
      smape = evaluator.evaluate(
          ground_truth=test, predict=pred, metric=ForecastMetric.sMAPE
      )
      self.assertAlmostEqual(smape, 77.9, delta=2.0)
  
      # Do a quick test of save/load
      ensemble.save("tmp/eval/forecast_ensemble")
      ForecasterEnsemble.load("tmp/eval/forecast_ensemble")
  
file_path: merlion/tests/evaluate/test_eval_forecast.py
imports: import logging
  from os.path import abspath, dirname, join
  import sys
  import unittest
  from merlion.evaluate.forecast import ForecastEvaluator, ForecastEvaluatorConfig, ForecastMetric
  from merlion.models.ensemble.combine import MetricWeightedMean
  from merlion.models.ensemble.forecast import ForecasterEnsemble, ForecasterEnsembleConfig
  from merlion.models.forecast.arima import ArimaConfig, Arima
  from merlion.models.forecast.ets import ETSConfig, ETS
  from merlion.transform.base import Identity
  from merlion.utils.data_io import csv_to_time_series
  from merlion.utils.time_series import UnivariateTimeSeries
label_desc: Use image node_id 3 for calling the TestEvaluateForecast obj's underlying member method code with example usage: obj.test_ensemble() without return types