diff --git a/pyFTS/common/FuzzySet.py b/pyFTS/common/FuzzySet.py index d25ccd0..841211d 100644 --- a/pyFTS/common/FuzzySet.py +++ b/pyFTS/common/FuzzySet.py @@ -12,15 +12,15 @@ class FuzzySet(object): """ Create a Fuzzy Set """ - self.name = name + self.name : str = name """The fuzzy set name""" - self.mf = mf + self.mf = mf """The membership function""" - self.parameters = parameters + self.parameters : list = parameters """The parameters of the membership function""" - self.centroid = centroid + self.centroid : float = centroid """The fuzzy set center of mass (or midpoint)""" - self.alpha = alpha + self.alpha : float = alpha """The alpha cut value""" self.type = kwargs.get('type', 'common') """The fuzzy set type (common, composite, nonstationary, etc)""" @@ -170,7 +170,7 @@ def set_ordered(fuzzy_sets): return [k.name for k in sorted(tmp1, key=lambda x: x.centroid)] -def fuzzyfy_instance(inst, fuzzy_sets, ordered_sets=None): +def fuzzyfy_instance(inst, fuzzy_sets : dict , ordered_sets : list =None): """ Calculate the membership values for a data point given fuzzy sets @@ -191,7 +191,7 @@ def fuzzyfy_instance(inst, fuzzy_sets, ordered_sets=None): return mv -def fuzzyfy_instances(data, fuzzy_sets, ordered_sets=None): +def fuzzyfy_instances(data : list, fuzzy_sets : dict, ordered_sets=None) -> list: """ Calculate the membership values for a data point given fuzzy sets @@ -209,7 +209,7 @@ def fuzzyfy_instances(data, fuzzy_sets, ordered_sets=None): return ret -def get_fuzzysets(inst, fuzzy_sets, ordered_sets=None, alpha_cut=0.0): +def get_fuzzysets(inst, fuzzy_sets : dict, ordered_sets : list =None, alpha_cut : flota =0.0) -> list: """ Return the fuzzy sets which membership value for a inst is greater than the alpha_cut @@ -232,7 +232,7 @@ def get_fuzzysets(inst, fuzzy_sets, ordered_sets=None, alpha_cut=0.0): raise ex -def get_maximum_membership_fuzzyset(inst, fuzzy_sets, ordered_sets=None): +def get_maximum_membership_fuzzyset(inst, fuzzy_sets, ordered_sets=None) -> FuzzySet: """ Fuzzify a data point, returning the fuzzy set with maximum membership value @@ -248,7 +248,7 @@ def get_maximum_membership_fuzzyset(inst, fuzzy_sets, ordered_sets=None): return fuzzy_sets[key] -def get_maximum_membership_fuzzyset_index(inst, fuzzy_sets): +def get_maximum_membership_fuzzyset_index(inst, fuzzy_sets) -> int: """ Fuzzify a data point, returning the fuzzy set with maximum membership value diff --git a/pyFTS/common/Transformations.py b/pyFTS/common/Transformations.py index 610f375..39edfb8 100644 --- a/pyFTS/common/Transformations.py +++ b/pyFTS/common/Transformations.py @@ -15,6 +15,3 @@ from pyFTS.common.transformations.trend import LinearTrend from pyFTS.common.transformations.som import SOMTransformation from pyFTS.common.transformations.autoencoder import AutoencoderTransformation from pyFTS.common.transformations.normalization import Normalization - - - diff --git a/pyFTS/common/fts.py b/pyFTS/common/fts.py index 3a4f3a7..3d333bb 100644 --- a/pyFTS/common/fts.py +++ b/pyFTS/common/fts.py @@ -1,6 +1,9 @@ import numpy as np import pandas as pd -from pyFTS.common import FuzzySet, SortedCollection, tree, Util +from pyFTS.common import FuzzySet, Util +from pyFTS.common.transformations import transformation +from pyFTS.partitioners import partitioner +from pyFTS.probabilistic import ProbabilityDistribution class FTS(object): @@ -11,72 +14,72 @@ class FTS(object): """ Create a Fuzzy Time Series model """ - self.flrgs = {} + self.flrgs: dict = {} """The list of Fuzzy Logical Relationship Groups - FLRG""" - self.order = kwargs.get('order',1) + self.order : int = kwargs.get('order',1) """A integer with the model order (number of past lags are used on forecasting)""" - self.shortname = kwargs.get('name',"") + self.shortname : str = kwargs.get('name',"") """A string with a short name or alias for the model""" - self.name = kwargs.get('name',"") + self.name : str = kwargs.get('name',"") """A string with the model name""" - self.detail = kwargs.get('name',"") + self.detail : str = kwargs.get('name',"") """A string with the model detailed information""" - self.is_wrapper = False + self.is_wrapper : bool = False """Indicates that this model is a wrapper for other(s) method(s)""" - self.is_high_order = False + self.is_high_order : bool = False """A boolean value indicating if the model support orders greater than 1, default: False""" - self.min_order = 1 + self.min_order : int = 1 """In high order models, this integer value indicates the minimal order supported for the model, default: 1""" - self.has_seasonality = False + self.has_seasonality : bool = False """A boolean value indicating if the model supports seasonal indexers, default: False""" - self.has_point_forecasting = True + self.has_point_forecasting : bool = True """A boolean value indicating if the model supports point forecasting, default: True""" - self.has_interval_forecasting = False + self.has_interval_forecasting : bool = False """A boolean value indicating if the model supports interval forecasting, default: False""" - self.has_probability_forecasting = False + self.has_probability_forecasting : bool = False """A boolean value indicating if the model support probabilistic forecasting, default: False""" - self.is_multivariate = False + self.is_multivariate : bool = False """A boolean value indicating if the model support multivariate time series (Pandas DataFrame), default: False""" - self.is_clustered = False + self.is_clustered : bool = False """A boolean value indicating if the model support multivariate time series (Pandas DataFrame), but works like a monovariate method, default: False""" - self.dump = False - self.transformations = [] + self.dump : bool = False + self.transformations : list[transformation.Transformation] = [] """A list with the data transformations (common.Transformations) applied on model pre and post processing, default: []""" - self.transformations_param = [] + self.transformations_param : list = [] """A list with the specific parameters for each data transformation""" - self.original_max = 0 + self.original_max : float = 0.0 """A float with the upper limit of the Universe of Discourse, the maximal value found on training data""" - self.original_min = 0 + self.original_min : float = 0.0 """A float with the lower limit of the Universe of Discourse, the minimal value found on training data""" - self.partitioner = kwargs.get("partitioner", None) + self.partitioner : partitioner.Partitioner = kwargs.get("partitioner", None) """A pyFTS.partitioners.Partitioner object with the Universe of Discourse partitioner used on the model. This is a mandatory dependecy. """ if self.partitioner != None: self.sets = self.partitioner.sets - self.auto_update = False + self.auto_update : bool = False """A boolean value indicating that model is incremental""" - self.benchmark_only = False + self.benchmark_only : bool = False """A boolean value indicating a façade for external (non-FTS) model used on benchmarks or ensembles.""" self.indexer = kwargs.get("indexer", None) """An pyFTS.models.seasonal.Indexer object for indexing the time series data""" - self.uod_clip = kwargs.get("uod_clip", True) + self.uod_clip : bool = kwargs.get("uod_clip", True) """Flag indicating if the test data will be clipped inside the training Universe of Discourse""" - self.alpha_cut = kwargs.get("alpha_cut", 0.0) + self.alpha_cut : float = kwargs.get("alpha_cut", 0.0) """A float with the minimal membership to be considered on fuzzyfication process""" - self.lags = kwargs.get("lags", None) + self.lags : list[int] = kwargs.get("lags", None) """The list of lag indexes for high order models""" - self.max_lag = self.order + self.max_lag : int = self.order """A integer indicating the largest lag used by the model. This value also indicates the minimum number of past lags needed to forecast a single step ahead""" - self.log = pd.DataFrame([],columns=["Datetime","Operation","Value"]) + self.log : pd.DataFrame = pd.DataFrame([],columns=["Datetime","Operation","Value"]) """""" - self.is_time_variant = False + self.is_time_variant : bool = False """A boolean value indicating if this model is time variant""" - self.standard_horizon = kwargs.get("standard_horizon", 1) + self.standard_horizon : int = kwargs.get("standard_horizon", 1) """Standard forecasting horizon (Default: 1)""" - def fuzzy(self, data): + def fuzzy(self, data) -> dict: """ Fuzzify a data point @@ -198,7 +201,7 @@ class FTS(object): return ret - def forecast(self, data, **kwargs): + def forecast(self, data, **kwargs) -> list: """ Point forecast one step ahead @@ -208,7 +211,7 @@ class FTS(object): """ raise NotImplementedError('This model do not perform one step ahead point forecasts!') - def forecast_interval(self, data, **kwargs): + def forecast_interval(self, data, **kwargs) -> list: """ Interval forecast one step ahead @@ -218,7 +221,7 @@ class FTS(object): """ raise NotImplementedError('This model do not perform one step ahead interval forecasts!') - def forecast_distribution(self, data, **kwargs): + def forecast_distribution(self, data, **kwargs) -> list[ProbabilityDistribution.ProbabilityDistribution]: """ Probabilistic forecast one step ahead @@ -228,7 +231,7 @@ class FTS(object): """ raise NotImplementedError('This model do not perform one step ahead distribution forecasts!') - def forecast_multivariate(self, data, **kwargs): + def forecast_multivariate(self, data, **kwargs) -> pd.DataFrame: """ Multivariate forecast one step ahead @@ -239,7 +242,7 @@ class FTS(object): raise NotImplementedError('This model do not perform one step ahead multivariate forecasts!') - def forecast_ahead(self, data, steps, **kwargs): + def forecast_ahead(self, data, steps, **kwargs) -> list: """ Point forecast from 1 to H steps ahead, where H is given by the steps parameter @@ -269,7 +272,7 @@ class FTS(object): return ret[-steps:] - def forecast_ahead_interval(self, data, steps, **kwargs): + def forecast_ahead_interval(self, data, steps, **kwargs) -> list: """ Interval forecast from 1 to H steps ahead, where H is given by the steps parameter @@ -280,7 +283,7 @@ class FTS(object): """ raise NotImplementedError('This model do not perform multi step ahead interval forecasts!') - def forecast_ahead_distribution(self, data, steps, **kwargs): + def forecast_ahead_distribution(self, data, steps, **kwargs) -> list[ProbabilityDistribution.ProbabilityDistribution]: """ Probabilistic forecast from 1 to H steps ahead, where H is given by the steps parameter @@ -291,7 +294,7 @@ class FTS(object): """ raise NotImplementedError('This model do not perform multi step ahead distribution forecasts!') - def forecast_ahead_multivariate(self, data, steps, **kwargs): + def forecast_ahead_multivariate(self, data, steps, **kwargs) -> pd.DataFrame: """ Multivariate forecast n step ahead @@ -302,7 +305,7 @@ class FTS(object): """ raise NotImplementedError('This model do not perform one step ahead multivariate forecasts!') - def forecast_step(self, data, step, **kwargs): + def forecast_step(self, data, step, **kwargs) -> list: """ Point forecast for H steps ahead, where H is given by the step parameter @@ -573,7 +576,7 @@ class FTS(object): else: return data - def get_UoD(self): + def get_UoD(self) -> set: """ Returns the interval of the known bounds of the universe of discourse (UoD), i. e., the known minimum and maximum values of the time series. @@ -585,7 +588,7 @@ class FTS(object): else: return (self.original_min, self.original_max) - def offset(self): + def offset(self) -> int: """ Returns the number of lags to skip in the input test data in order to synchronize it with the forecasted values given by the predict function. This is necessary due to the order of the diff --git a/pyFTS/data/AirPassengers.py b/pyFTS/data/AirPassengers.py index 0cd0ce6..1458beb 100644 --- a/pyFTS/data/AirPassengers.py +++ b/pyFTS/data/AirPassengers.py @@ -9,7 +9,7 @@ import pandas as pd import numpy as np -def get_data(): +def get_data() -> np.ndarray: """ Get a simple univariate time series data. @@ -19,7 +19,7 @@ def get_data(): dat = np.array(dat["Passengers"]) return dat -def get_dataframe(): +def get_dataframe() -> pd.DataFrame: """ Get the complete multivariate time series data. diff --git a/pyFTS/data/artificial.py b/pyFTS/data/artificial.py index b29ba22..3878c0c 100644 --- a/pyFTS/data/artificial.py +++ b/pyFTS/data/artificial.py @@ -16,7 +16,7 @@ class SignalEmulator(object): self.components = [] """Components of the signal""" - def stationary_gaussian(self, mu, sigma, **kwargs): + def stationary_gaussian(self, mu:float, sigma:float, **kwargs): """ Creates a continuous Gaussian signal with mean mu and variance sigma. @@ -36,7 +36,7 @@ class SignalEmulator(object): 'parameters': parameters, 'args': kwargs}) return self - def incremental_gaussian(self, mu, sigma, **kwargs): + def incremental_gaussian(self, mu:float, sigma:float, **kwargs): """ Creates an additive gaussian interference on a previous signal diff --git a/pyFTS/data/common.py b/pyFTS/data/common.py index 3d7746b..571b3f1 100644 --- a/pyFTS/data/common.py +++ b/pyFTS/data/common.py @@ -7,7 +7,7 @@ from pathlib import Path from urllib import request -def get_dataframe(filename, url, sep=";", compression='infer'): +def get_dataframe(filename: str, url: str, sep:str=";", compression:str='infer') -> pd.DataFrame: """ This method check if filename already exists, read the file and return its data. If the file don't already exists, it will be downloaded and decompressed. diff --git a/pyFTS/partitioners/Singleton.py b/pyFTS/partitioners/Singleton.py index 65cb981..6ffb12c 100644 --- a/pyFTS/partitioners/Singleton.py +++ b/pyFTS/partitioners/Singleton.py @@ -9,7 +9,7 @@ from pyFTS.partitioners import partitioner class SingletonPartitioner(partitioner.Partitioner): - """Singleton Partitioner""" + """Singleton Partitioner: Create singleton fuzzy sets for each distinct value in UoD""" def __init__(self, **kwargs): """ @@ -17,12 +17,12 @@ class SingletonPartitioner(partitioner.Partitioner): """ super(SingletonPartitioner, self).__init__(name="Singleton", **kwargs) - def build(self, data): + def build(self, data : list): sets = {} kwargs = {'type': self.type, 'variable': self.variable} - for count, instance in enumerate(data): + for count, instance in enumerate(set(data)): _name = self.get_name(count) sets[_name] = FuzzySet.FuzzySet(_name, Membership.singleton, [instance], instance, **kwargs) diff --git a/pyFTS/partitioners/Util.py b/pyFTS/partitioners/Util.py index f2be763..396eeca 100644 --- a/pyFTS/partitioners/Util.py +++ b/pyFTS/partitioners/Util.py @@ -18,7 +18,11 @@ all_methods = [Grid.GridPartitioner, Entropy.EntropyPartitioner, FCM.FCMPartitio mfs = [Membership.trimf, Membership.gaussmf, Membership.trapmf] -def plot_sets(data, sets, titles, size=[12, 10], save=False, file=None, axis=None): +def plot_sets(data, sets: dict, titles : list, size=[12, 10], save=False, file=None, axis=None): + """ + Plot all fuzzy sets in a Partitioner + + """ num = len(sets) if axis is None: @@ -53,7 +57,7 @@ def plot_sets(data, sets, titles, size=[12, 10], save=False, file=None, axis=Non def plot_partitioners(data, objs, tam=[12, 10], save=False, file=None, axis=None): sets = [k.sets for k in objs] titles = [k.name for k in objs] - plot_sets(data, sets, titles, tam, save, file, axis) + plot_sets(sets, titles, tam, save, file, axis) def explore_partitioners(data, npart, methods=None, mf=None, transformation=None, @@ -84,6 +88,6 @@ def explore_partitioners(data, npart, methods=None, mf=None, transformation=None obj.name = obj.name + " - " + obj.membership_function.__name__ objs.append(obj) - plot_partitioners(data, objs, size, save, file) + plot_partitioners(objs, size, save, file) return objs diff --git a/pyFTS/partitioners/parallel_util.py b/pyFTS/partitioners/parallel_util.py index f683dd4..cd7acaf 100644 --- a/pyFTS/partitioners/parallel_util.py +++ b/pyFTS/partitioners/parallel_util.py @@ -28,5 +28,5 @@ def explore_partitioners(data, npart, methods=None, mf=None, tam=[12, 10], save= objs = np.ravel(objs).tolist() - Util.plot_partitioners(data, objs, tam, save, file) + Util.plot_partitioners(objs, tam, save, file) diff --git a/pyFTS/partitioners/partitioner.py b/pyFTS/partitioners/partitioner.py index f11ac9f..42dcc4b 100644 --- a/pyFTS/partitioners/partitioner.py +++ b/pyFTS/partitioners/partitioner.py @@ -14,16 +14,17 @@ class Partitioner(object): """ Universe of Discourse partitioner scheme. Split data on several fuzzy sets """ - self.name = kwargs.get('name',"") + self.name : str = kwargs.get('name',"") """partitioner name""" - self.partitions = kwargs.get('npart', 10) + self.partitions : int = kwargs.get('npart', 10) """The number of universe of discourse partitions, i.e., the number of fuzzy sets that will be created""" - self.sets = {} + self.sets : dict = {} + """The fuzzy sets dictionary""" self.membership_function = kwargs.get('func', Membership.trimf) """Fuzzy membership function (pyFTS.common.Membership)""" - self.setnames = kwargs.get('names', None) + self.setnames : list' = kwargs.get('names', None) """list of partitions names. If None is given the partitions will be auto named with prefix""" - self.prefix = kwargs.get('prefix', 'A') + self.prefix : str'' = kwargs.get('prefix', 'A') """prefix of auto generated partition names""" self.transformation = kwargs.get('transformation', None) """data transformation to be applied on data""" @@ -34,13 +35,13 @@ class Partitioner(object): """The type of fuzzy sets that are generated by this partitioner""" self.ordered_sets = None """A ordered list of the fuzzy sets names, sorted by their middle point""" - self.kdtree = None + self.kdtree : KDTree = None """A spatial index to help in fuzzyfication""" - self.margin = kwargs.get("margin", 0.1) + self.margin : float = kwargs.get("margin", 0.1) """The upper and lower exceeding margins for the known UoD. The default value is .1""" - self.lower_margin = kwargs.get("lower_margin", self.margin) + self.lower_margin : float = kwargs.get("lower_margin", self.margin) """Specific lower exceeding margins for the known UoD. The default value is the self.margin parameter""" - self.upper_margin = kwargs.get("lower_margin", self.margin) + self.upper_margin : float = kwargs.get("lower_margin", self.margin) """Specific upper exceeding margins for the known UoD. The default value is the self.margin parameter""" if kwargs.get('preprocess',True): diff --git a/pyFTS/partitioners/som.py b/pyFTS/partitioners/som.py index 059ea11..bc43d3c 100644 --- a/pyFTS/partitioners/som.py +++ b/pyFTS/partitioners/som.py @@ -8,6 +8,7 @@ from typing import Tuple class SOMPartitioner: + """Self Organized Map Partitioner""" def __init__(self, grid_dimension: Tuple, **kwargs): @@ -19,8 +20,8 @@ class SOMPartitioner: # debug attributes - self.name = 'Kohonen Self Organizing Maps FTS' - self.shortname = 'SOM-FTS' + self.name = 'Kohonen Self Organizing Map Partitioner' + self.shortname = 'SOM-Partitioner' def __repr__(self): status = "is trained" if self.is_trained else "not trained"