From f7c1b4443ee3404e6a2279445cb68335a464a0c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=C3=B4nio=20C=C3=A2ndido?= Date: Wed, 27 Feb 2019 10:32:58 -0300 Subject: [PATCH] Improvements on EnsembleFTS and IncrementalEnsembleFTS --- pyFTS/data/artificial.py | 2 +- pyFTS/models/ensemble/ensemble.py | 15 +++++++++++---- .../models/incremental/IncrementalEnsemble.py | 18 ++++++++---------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/pyFTS/data/artificial.py b/pyFTS/data/artificial.py index 29f4afb..b29ba22 100644 --- a/pyFTS/data/artificial.py +++ b/pyFTS/data/artificial.py @@ -299,7 +299,7 @@ def white_noise(n=500): def random_walk(n=500, type='gaussian'): """ Simple random walk - + :param n: number of samples :param type: 'gaussian' or 'uniform' :return: diff --git a/pyFTS/models/ensemble/ensemble.py b/pyFTS/models/ensemble/ensemble.py index 5d806c1..dc5060c 100644 --- a/pyFTS/models/ensemble/ensemble.py +++ b/pyFTS/models/ensemble/ensemble.py @@ -43,10 +43,12 @@ class EnsembleFTS(fts.FTS): self.alpha = kwargs.get("alpha", 0.05) """The quantiles """ self.point_method = kwargs.get('point_method', 'mean') - """The method used to mix the several model's forecasts into a unique point forecast. Options: mean, median, quantile""" + """The method used to mix the several model's forecasts into a unique point forecast. Options: mean, median, quantile, exponential""" self.interval_method = kwargs.get('interval_method', 'quantile') """The method used to mix the several model's forecasts into a interval forecast. Options: quantile, extremum, normal""" - self.order = 1 + #self.order = 1 + self.exp_factor = kwargs.get('exp_factor', 0.5) + """Multiplicative factor on exponential averaging of the models""" def append_model(self, model): """ @@ -80,9 +82,9 @@ class EnsembleFTS(fts.FTS): data = self.indexer.get_data(data) sample = data[-model.order:] - forecast = model.forecast(sample) + forecast = model.predict(sample) if isinstance(forecast, (list,np.ndarray)) and len(forecast) > 0: - forecast = int(forecast[-1]) + forecast = forecast[-1] elif isinstance(forecast, (list,np.ndarray)) and len(forecast) == 0: forecast = np.nan if isinstance(forecast, list): @@ -99,6 +101,11 @@ class EnsembleFTS(fts.FTS): elif self.point_method == 'quantile': alpha = kwargs.get("alpha",0.05) ret = np.percentile(forecasts, alpha*100) + elif self.point_method == 'exponential': + l = len(self.models) + if l == 1: + return forecasts + ret = np.nansum([np.exp(-(self.exp_factor * (l - k))) * forecasts[k] for k in range(l)]) return ret diff --git a/pyFTS/models/incremental/IncrementalEnsemble.py b/pyFTS/models/incremental/IncrementalEnsemble.py index 67598eb..435faa4 100644 --- a/pyFTS/models/incremental/IncrementalEnsemble.py +++ b/pyFTS/models/incremental/IncrementalEnsemble.py @@ -38,6 +38,11 @@ class IncrementalEnsembleFTS(ensemble.EnsembleFTS): self.batch_size = kwargs.get('batch_size', 10) """The batch interval between each retraining""" + self.num_models = kwargs.get('num_models', 5) + """The number of models to hold in the ensemble""" + + self.point_method = kwargs.get('point_method', 'exponential') + self.is_high_order = True self.uod_clip = False #self.max_lag = self.window_length + self.max_lag @@ -49,16 +54,9 @@ class IncrementalEnsembleFTS(ensemble.EnsembleFTS): if model.is_high_order: model = self.fts_method(partitioner=partitioner, order=self.order, **self.fts_params) model.fit(data, **kwargs) - if len(self.models) > 0: + self.append_model(model) + if len(self.models) > self.num_models: self.models.pop(0) - self.models.append(model) - - def _point_smoothing(self, forecasts): - l = len(self.models) - - ret = np.nansum([np.exp(-(l-k)) * forecasts[k] for k in range(l)]) - - return ret def forecast(self, data, **kwargs): l = len(data) @@ -79,7 +77,7 @@ class IncrementalEnsembleFTS(ensemble.EnsembleFTS): sample = data[k - self.max_lag: k] tmp = self.get_models_forecasts(sample) - point = self._point_smoothing(tmp) + point = self.get_point(tmp) ret.append(point) return ret