From 9125582c9c29a136aad28ab1f41c9af4b181bbf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=C3=B4nio=20C=C3=A2ndido?= Date: Wed, 20 Feb 2019 16:02:07 -0300 Subject: [PATCH] Added the data.artificial.SignalEmulator --- pyFTS/common/fts.py | 13 +- pyFTS/data/artificial.py | 199 ++++++++++++++++++ pyFTS/models/ensemble/ensemble.py | 1 + .../models/incremental/IncrementalEnsemble.py | 7 +- pyFTS/tests/ensemble.py | 17 +- pyFTS/tests/general.py | 21 +- 6 files changed, 228 insertions(+), 30 deletions(-) diff --git a/pyFTS/common/fts.py b/pyFTS/common/fts.py index 62e269a..42e7de0 100644 --- a/pyFTS/common/fts.py +++ b/pyFTS/common/fts.py @@ -24,6 +24,8 @@ class FTS(object): """A string with the model name""" self.detail = kwargs.get('name',"") """A string with the model detailed information""" + self.is_wrapper = False + """Indicates that this model is a wrapper for other(s) method(s)""" self.is_high_order = False """A boolean value indicating if the model support orders greater than 1, default: False""" self.min_order = 1 @@ -313,11 +315,12 @@ class FTS(object): if 'partitioner' in kwargs: self.partitioner = kwargs.pop('partitioner') - if (self.sets is None or len(self.sets) == 0) and not self.benchmark_only and not self.is_multivariate: - if self.partitioner is not None: - self.sets = self.partitioner.sets - else: - raise Exception("Fuzzy sets were not provided for the model. Use 'sets' parameter or 'partitioner'. ") + if not self.is_wrapper: + if (self.sets is None or len(self.sets) == 0) and not self.benchmark_only and not self.is_multivariate: + if self.partitioner is not None: + self.sets = self.partitioner.sets + else: + raise Exception("Fuzzy sets were not provided for the model. Use 'sets' parameter or 'partitioner'. ") if 'order' in kwargs: self.order = kwargs.pop('order') diff --git a/pyFTS/data/artificial.py b/pyFTS/data/artificial.py index fb943e3..0f2916e 100644 --- a/pyFTS/data/artificial.py +++ b/pyFTS/data/artificial.py @@ -34,6 +34,83 @@ def generate_gaussian_linear(mu_ini, sigma_ini, mu_inc, sigma_inc, it=100, num=1 return ret +def generate_linear_periodic_gaussian(period, mu_min, sigma_min, mu_max, sigma_max, it=100, num=10, vmin=None, vmax=None): + """ + + :param period: + :param mu_min: + :param sigma_min: + :param mu_max: + :param sigma_max: + :param it: + :param num: + :param vmin: + :param vmax: + :return: + """ + + if period > num: + raise("The 'period' parameter must be lesser than 'it' parameter") + + mu_inc = (mu_max - mu_min)/period + sigma_inc = (sigma_max - sigma_min) / period + mu = mu_min + sigma = sigma_min + ret = [] + signal = True + + for k in np.arange(0, it): + tmp = np.random.normal(mu, sigma, num) + if vmin is not None: + tmp = np.maximum(np.full(num, vmin), tmp) + if vmax is not None: + tmp = np.minimum(np.full(num, vmax), tmp) + ret.extend(tmp) + + if k % period == 0: + signal = not signal + + mu += (mu_inc if signal else -mu_inc) + sigma += (sigma_inc if signal else -sigma_inc) + + return ret + + +def generate_senoidal_periodic_gaussian(period, mu_min, sigma_min, mu_max, sigma_max, it=100, num=10, vmin=None, vmax=None): + """ + + :param period: + :param mu_min: + :param sigma_min: + :param mu_max: + :param sigma_max: + :param it: + :param num: + :param vmin: + :param vmax: + :return: + """ + + mu_range = mu_max - mu_min + sigma_range = sigma_max - sigma_min + mu = mu_min + sigma = sigma_min + ret = [] + + for k in np.arange(0, it): + tmp = np.random.normal(mu, sigma, num) + if vmin is not None: + tmp = np.maximum(np.full(num, vmin), tmp) + if vmax is not None: + tmp = np.minimum(np.full(num, vmax), tmp) + ret.extend(tmp) + + mu += mu_range * np.sin (period * k) + sigma += mu_range * np.sin (period * k) + + return ret + + def generate_uniform_linear(min_ini, max_ini, min_inc, max_inc, it=100, num=10, vmin=None, vmax=None): """ Generate data sampled from Uniform distribution, with constant or linear changing bounds @@ -78,3 +155,125 @@ def random_walk(n=500, type='gaussian'): return ret + +def _append(additive, start, before, new): + if not additive: + before.extend(new) + return before + else: + for k in range(start): + new.insert(0,0) + tmp = np.array(before) + np.array(new) + return tmp + + + +class SignalEmulator(object): + + def __init__(self, **kwargs): + super(SignalEmulator, self).__init__() + + self.components = [] + + def stationary_gaussian(self, mu, sigma, **kwargs): + """ + Creates a continuous Gaussian signal with mean mu and variance sigma. + :param mu: mean + :param sigma: variance + :keyword cummulative: If False it cancels the previous signal and start this one, if True + this signal is added to the previous one + :keyword start: lag index to start this signal, the default value is 0 + :keyword it: Number of iterations, the default value is 1 + :keyword length: Number of samples generated on each iteration, the default value is 100 + :keyword vmin: Lower bound value of generated data, the default value is None + :keyword vmax: Upper bound value of generated data, the default value is None + :return: A list of it*num float values + """ + parameters = {'mu': mu, 'sigma': sigma} + self.components.append({'dist': 'gaussian', 'type': 'constant', + 'parameters': parameters, 'args': kwargs}) + + def incremental_gaussian(self, mu, sigma, **kwargs): + """ + Creates an additive gaussian interference on a previous signal + :param mu: + :param sigma: + :keyword cummulative: If False it cancels the previous signal and start this one, if True + this signal is added to the previous one + :keyword start: lag index to start this signal, the default value is 0 + :keyword it: Number of iterations, the default value is 1 + :keyword length: Number of samples generated on each iteration, the default value is 100 + :keyword vmin: Lower bound value of generated data, the default value is None + :keyword vmax: Upper bound value of generated data, the default value is None + :return: A list of it*num float values + """ + parameters = {'mu': mu, 'sigma': sigma} + self.components.append({'dist': 'gaussian', 'type': 'incremental', + 'parameters': parameters, 'args': kwargs}) + + def periodic_gaussian(self, type, period, mu_min, sigma_min, mu_max, sigma_max, **kwargs): + """ + Creates an additive periodic gaussian interference on a previous signal + :param mu: + :param sigma: + :keyword additive: If False it cancels the previous signal and start this one, if True + this signal is added to the previous one + :keyword start: lag index to start this signal, the default value is 0 + :keyword it: Number of iterations, the default value is 1 + :keyword length: Number of samples generated on each iteration, the default value is 100 + :keyword vmin: Lower bound value of generated data, the default value is None + :keyword vmax: Upper bound value of generated data, the default value is None + :return: A list of it*num float values + """ + parameters = {'type':type, 'period':period, + 'mu_min': mu_min, 'sigma_min': sigma_min, 'mu_max': mu_max, 'sigma_max': sigma_max} + self.components.append({'dist': 'gaussian', 'type': 'periodic', + 'parameters': parameters, 'args': kwargs}) + + def blip(self, intensity, **kwargs): + """ + + :param intensity: + :param kwargs: + :return: + """ + self.components.append({'dist': 'blip', 'type': 'blip', 'parameters': [intensity, kwargs]}) + + def run(self): + signal = [] + last_it = 10 + last_num = 10 + for ct, component in enumerate(self.components): + parameters = component['parameters'] + kwargs = component['args'] + additive = kwargs.get('additive', True) + start = kwargs.get('start', 0) + it = kwargs.get('it', last_it) + num = kwargs.get('length', last_num) + vmin = kwargs.get('vmin',None) + vmax = kwargs.get('vmax', None) + if component['type'] == 'constant': + tmp = generate_gaussian_linear(parameters['mu'], parameters['sigma'], 0, 0, + it=it, num=num, vmin=vmin, vmax=vmax) + elif component['type'] == 'incremental': + tmp = generate_gaussian_linear(0, 0, parameters['mu'], parameters['sigma'], + it=num, num=1, vmin=vmin, vmax=vmax) + elif component['type'] == 'periodic': + period = component['period'] + mu_min, sigma_min = parameters['mu_min'],parameters['sigma_min'] + mu_max, sigma_max = parameters['mu_max'],parameters['sigma_max'] + + if parameters['type'] == 'senoidal': + tmp = generate_senoidal_periodic_gaussian(period, mu_min, sigma_min, mu_max, sigma_max, + it=num, num=1, vmin=vmin, vmax=vmax) + else: + tmp = generate_linear_periodic_gaussian(period, mu_min, sigma_min, mu_max, sigma_max, + it=num, num=1, vmin=vmin, vmax=vmax) + + last_num = num + last_it = it + + signal = _append(additive, start, signal, tmp) + + return signal + diff --git a/pyFTS/models/ensemble/ensemble.py b/pyFTS/models/ensemble/ensemble.py index 07cd555..5d806c1 100644 --- a/pyFTS/models/ensemble/ensemble.py +++ b/pyFTS/models/ensemble/ensemble.py @@ -31,6 +31,7 @@ class EnsembleFTS(fts.FTS): self.shortname = "EnsembleFTS" self.name = "Ensemble FTS" self.flrgs = {} + self.is_wrapper = True self.has_point_forecasting = True self.has_interval_forecasting = True self.has_probability_forecasting = True diff --git a/pyFTS/models/incremental/IncrementalEnsemble.py b/pyFTS/models/incremental/IncrementalEnsemble.py index 2ff5041..67598eb 100644 --- a/pyFTS/models/incremental/IncrementalEnsemble.py +++ b/pyFTS/models/incremental/IncrementalEnsemble.py @@ -46,10 +46,11 @@ class IncrementalEnsembleFTS(ensemble.EnsembleFTS): partitioner = self.partitioner_method(data=data, **self.partitioner_params) model = self.fts_method(partitioner=partitioner, **self.fts_params) - if self.model.is_high_order: - self.model = self.fts_method(partitioner=partitioner, order=self.order, **self.fts_params) + if model.is_high_order: + model = self.fts_method(partitioner=partitioner, order=self.order, **self.fts_params) model.fit(data, **kwargs) - self.models.pop(0) + if len(self.models) > 0: + self.models.pop(0) self.models.append(model) def _point_smoothing(self, forecasts): diff --git a/pyFTS/tests/ensemble.py b/pyFTS/tests/ensemble.py index 7e5b6e4..e990390 100644 --- a/pyFTS/tests/ensemble.py +++ b/pyFTS/tests/ensemble.py @@ -7,15 +7,21 @@ import numpy as np import pandas as pd from pyFTS.partitioners import Grid from pyFTS.common import Transformations -from pyFTS import hofts, song, yu, cheng, ismailefendi, sadaei, hwang from pyFTS.models import chen -from pyFTS.models.ensemble import ensemble +from pyFTS.models.incremental import IncrementalEnsemble, TimeVariant -os.chdir("/home/petronio/dados/Dropbox/Doutorado/Codigos/") +from pyFTS.data import AirPassengers -diff = Transformations.Differential(1) -passengers = pd.read_csv("DataSets/AirPassengers.csv", sep=",") +passengers = AirPassengers.get_data() + +model = IncrementalEnsemble.IncrementalEnsembleFTS(order=2, window_length=20, batch_size=5) + +model.fit(passengers[:40]) + +model.predict(passengers[40:]) + +''' passengers = np.array(passengers["Passengers"]) e = ensemble.AllMethodEnsembleFTS(alpha=0.25, point_method="median", interval_method='quantile') @@ -115,5 +121,6 @@ print(_normal) # distributions=[True,False], save=True, file="pictures/distribution_ahead_arma", # time_from=60, time_to=10, tam=[12,5]) +''' diff --git a/pyFTS/tests/general.py b/pyFTS/tests/general.py index c076458..f8154e8 100644 --- a/pyFTS/tests/general.py +++ b/pyFTS/tests/general.py @@ -14,21 +14,8 @@ from pyFTS.benchmarks import benchmarks as bchmk, Measures from pyFTS.models import chen, yu, cheng, ismailefendi, hofts, pwfts from pyFTS.common import Transformations, Membership -tdiff = Transformations.Differential(1) +from pyFTS.data import artificial - -from pyFTS.data import TAIEX, SP500, NASDAQ, Malaysia, Enrollments - -#from pyFTS.data import mackey_glass -#y = mackey_glass.get_data() - -from pyFTS.partitioners import Grid -from pyFTS.models import pwfts, tsaur - -x = [k for k in np.arange(-2*np.pi, 2*np.pi, 0.5)] -y = [np.sin(k) for k in x] - -part = Grid.GridPartitioner(data=y, npart=35) -model = hofts.HighOrderFTS(order=2, partitioner=part) -model.fit(y) -forecasts = model.predict(y, steps_ahead=10) +cd = artificial.SignalEmulator() +cd.stationary_gaussian(10,3,length=100) +cd.incremental_gaussian(0.5,0,start=100,length=200)