From 6f455f32154dda7798caddf5380c6a45beb001dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=C3=B4nio=20C=C3=A2ndido?= Date: Wed, 11 Oct 2017 22:51:50 -0300 Subject: [PATCH] - Improvements and refactorings on HOFTS, IFTS and PWFTS; - Complete version of NSFTS --- pyFTS/benchmarks/Measures.py | 17 ++++ pyFTS/benchmarks/ResidualAnalysis.py | 26 ++++++ pyFTS/common/FLR.py | 19 ++++ pyFTS/common/FuzzySet.py | 2 + pyFTS/flrg.py | 10 +++ pyFTS/hofts.py | 64 +++++++++++++- pyFTS/ifts.py | 10 --- pyFTS/nonstationary/common.py | 61 +++++++++---- pyFTS/nonstationary/flrg.py | 42 +++++++-- pyFTS/nonstationary/honsfts.py | 125 ++++++++++++++++++++------- pyFTS/nonstationary/nsfts.py | 59 +++++++++---- pyFTS/nonstationary/util.py | 45 +++++++--- pyFTS/partitioners/partitioner.py | 5 +- pyFTS/pwfts.py | 25 ++---- pyFTS/tests/nonstationary.py | 26 +++--- 15 files changed, 402 insertions(+), 134 deletions(-) diff --git a/pyFTS/benchmarks/Measures.py b/pyFTS/benchmarks/Measures.py index e777362..b348e3f 100644 --- a/pyFTS/benchmarks/Measures.py +++ b/pyFTS/benchmarks/Measures.py @@ -35,6 +35,10 @@ def rmse(targets, forecasts): :param forecasts: :return: """ + if isinstance(targets, list): + targets = np.array(targets) + if isinstance(forecasts, list): + forecasts = np.array(forecasts) return np.sqrt(np.nanmean((targets - forecasts) ** 2)) @@ -56,6 +60,10 @@ def mape(targets, forecasts): :param forecasts: :return: """ + if isinstance(targets, list): + targets = np.array(targets) + if isinstance(forecasts, list): + forecasts = np.array(forecasts) return np.mean(np.abs(targets - forecasts) / targets) * 100 @@ -67,6 +75,10 @@ def smape(targets, forecasts, type=2): :param type: :return: """ + if isinstance(targets, list): + targets = np.array(targets) + if isinstance(forecasts, list): + forecasts = np.array(forecasts) if type == 1: return np.mean(np.abs(forecasts - targets) / ((forecasts + targets)/2)) elif type == 2: @@ -88,6 +100,11 @@ def UStatistic(targets, forecasts): :return: """ l = len(targets) + if isinstance(targets, list): + targets = np.array(targets) + if isinstance(forecasts, list): + forecasts = np.array(forecasts) + naive = [] y = [] for k in np.arange(0,l-1): diff --git a/pyFTS/benchmarks/ResidualAnalysis.py b/pyFTS/benchmarks/ResidualAnalysis.py index 1de63fe..b0d3c5c 100644 --- a/pyFTS/benchmarks/ResidualAnalysis.py +++ b/pyFTS/benchmarks/ResidualAnalysis.py @@ -128,3 +128,29 @@ def plot_residuals(targets, models, tam=[8, 8], save=False, file=None): plt.tight_layout() Util.showAndSaveImage(fig, file, save) + + +def single_plot_residuals(targets, forecasts, order, tam=[8, 8], save=False, file=None): + fig, axes = plt.subplots(nrows=1, ncols=3, figsize=tam) + + ax = axes + res = residuals(targets, forecasts, order) + + ax[0].set_title("Residuals", size='large') + ax[0].set_ylabel("Model", size='large') + ax[0].set_xlabel(' ') + ax[0].plot(res) + + ax[1].set_title("Residuals Autocorrelation", size='large') + ax[1].set_ylabel('ACS') + ax[1].set_xlabel('Lag') + ax[1].acorr(res) + + ax[2].set_title("Residuals Histogram", size='large') + ax[2].set_ylabel('Freq') + ax[2].set_xlabel('Bins') + ax[2].hist(res) + + plt.tight_layout() + + Util.showAndSaveImage(fig, file, save) diff --git a/pyFTS/common/FLR.py b/pyFTS/common/FLR.py index 0ff89b1..1661cdc 100644 --- a/pyFTS/common/FLR.py +++ b/pyFTS/common/FLR.py @@ -35,6 +35,25 @@ class IndexedFLR(FLR): def __str__(self): return str(self.index) + ": "+ self.LHS.name + " -> " + self.RHS.name +def generate_high_order_recurrent_flr(fuzzyData): + """ + Create a ordered FLR set from a list of fuzzy sets with recurrence + :param fuzzyData: ordered list of fuzzy sets + :return: ordered list of FLR + """ + flrs = [] + for i in np.arange(1,len(fuzzyData)): + lhs = fuzzyData[i - 1] + rhs = fuzzyData[i] + if isinstance(lhs, list) and isinstance(rhs, list): + for l in lhs: + for r in rhs: + tmp = FLR(l, r) + flrs.append(tmp) + else: + tmp = FLR(lhs,rhs) + flrs.append(tmp) + return flrs def generateRecurrentFLRs(fuzzyData): """ diff --git a/pyFTS/common/FuzzySet.py b/pyFTS/common/FuzzySet.py index 789fc0e..f9d1620 100644 --- a/pyFTS/common/FuzzySet.py +++ b/pyFTS/common/FuzzySet.py @@ -27,6 +27,7 @@ class FuzzySet(object): elif self.mf == Membership.gaussmf: self.lower = parameters[0] - parameters[1]*3 self.upper = parameters[0] + parameters[1]*3 + self.metadata = {} def membership(self, x): """ @@ -89,6 +90,7 @@ def getMaxMembershipFuzzySet(inst, fuzzySets): mv = fuzzyInstance(inst, fuzzySets) return fuzzySets[np.argwhere(mv == max(mv))[0, 0]] + def getMaxMembershipFuzzySetIndex(inst, fuzzySets): """ Fuzzify a data point, returning the fuzzy set with maximum membership value diff --git a/pyFTS/flrg.py b/pyFTS/flrg.py index 8151bc1..f6e116e 100644 --- a/pyFTS/flrg.py +++ b/pyFTS/flrg.py @@ -11,6 +11,16 @@ class FLRG(object): self.lower = None self.upper = None + def get_membership(self, data): + ret = 0.0 + if isinstance(self.LHS, (list, set)): + assert len(self.LHS) == len(data) + ret = min([self.LHS[ct].membership(dat) for ct, dat in enumerate(data)]) + else: + ret = self.LHS.membership(data) + return ret + + def get_midpoint(self): if self.midpoint is None: self.midpoint = sum(self.get_midpoints())/len(self.RHS) diff --git a/pyFTS/hofts.py b/pyFTS/hofts.py index 892d1de..77c3119 100644 --- a/pyFTS/hofts.py +++ b/pyFTS/hofts.py @@ -7,7 +7,7 @@ Fuzzy Sets Syst., vol. 81, no. 3, pp. 311–319, 1996. import numpy as np from pyFTS.common import FuzzySet,FLR -from pyFTS import fts, flrg +from pyFTS import fts, flrg, tree class HighOrderFLRG(flrg.FLRG): @@ -57,6 +57,27 @@ class HighOrderFTS(fts.FTS): self.setsDict = {} self.is_high_order = True + def build_tree(self, node, lags, level): + if level >= self.order: + return + + for s in lags[level]: + node.appendChild(tree.FLRGTreeNode(s)) + + for child in node.getChildren(): + self.build_tree(child, lags, level + 1) + + def build_tree_without_order(self, node, lags, level): + + if level not in lags: + return + + for s in lags[level]: + node.appendChild(tree.FLRGTreeNode(s)) + + for child in node.getChildren(): + self.build_tree_without_order(child, lags, level + 1) + def generateFLRG(self, flrs): flrgs = {} l = len(flrs) @@ -73,6 +94,43 @@ class HighOrderFTS(fts.FTS): flrgs[flrg.strLHS()].appendRHS(flrs[k].RHS) return (flrgs) + def generate_flrg(self, data): + flrgs = {} + l = len(data) + for k in np.arange(self.order, l): + if self.dump: print("FLR: " + str(k)) + + sample = data[k - self.order: k] + + rhs = [set for set in self.sets if set.membership(data[k]) > 0.0] + + lags = {} + + for o in np.arange(0, self.order): + lhs = [set for set in self.sets if set.membership(sample[o]) > 0.0] + + lags[o] = lhs + + root = tree.FLRGTreeNode(None) + + self.build_tree_without_order(root, lags, 0) + + # Trace the possible paths + for p in root.paths(): + flrg = HighOrderFLRG(self.order) + path = list(reversed(list(filter(None.__ne__, p)))) + + for lhs in enumerate(path, start=0): + flrg.appendLHS(lhs) + + if flrg.strLHS() not in flrgs: + flrgs[flrg.strLHS()] = flrg; + + for st in rhs: + flrgs[flrg.strLHS()].appendRHS(st) + + return flrgs + def train(self, data, sets, order=1,parameters=None): data = self.doTransformations(data, updateUoD=True) @@ -80,9 +138,7 @@ class HighOrderFTS(fts.FTS): self.order = order self.sets = sets for s in self.sets: self.setsDict[s.name] = s - tmpdata = FuzzySet.fuzzySeries(data, sets) - flrs = FLR.generateRecurrentFLRs(tmpdata) - self.flrgs = self.generateFLRG(flrs) + self.flrgs = self.generate_flrg(data) def forecast(self, data, **kwargs): diff --git a/pyFTS/ifts.py b/pyFTS/ifts.py index d534c1b..9fb6d26 100644 --- a/pyFTS/ifts.py +++ b/pyFTS/ifts.py @@ -43,16 +43,6 @@ class IntervalFTS(hofts.HighOrderFTS): mb = [fuzzySets[k].membership(data[k]) for k in np.arange(0, len(data))] return mb - def build_tree(self, node, lags, level): - if level >= self.order: - return - - for s in lags[level]: - node.appendChild(tree.FLRGTreeNode(s)) - - for child in node.getChildren(): - self.build_tree(child, lags, level + 1) - def forecastInterval(self, data, **kwargs): ndata = np.array(self.doTransformations(data)) diff --git a/pyFTS/nonstationary/common.py b/pyFTS/nonstationary/common.py index cf35b23..f45be30 100644 --- a/pyFTS/nonstationary/common.py +++ b/pyFTS/nonstationary/common.py @@ -172,14 +172,15 @@ class FuzzySet(FS.FuzzySet): def __str__(self): tmp = "" if self.location is not None: - tmp += "Loc. Pert.: " + tmp += "Location: " for ct, f in enumerate(self.location): - tmp += str(f.__name__) + "(" + str(self.location_params[ct]) + ") " + tmp += str(f.__name__) + "(" + str(["{0:.2f}".format(p) for p in self.location_params[ct]]) + ") " if self.width is not None: - tmp += "Wid. Pert.: " + tmp += "Width: " for ct, f in enumerate(self.width): - tmp += str(f.__name__) + "(" + str(self.width_params[ct]) + ") " - return self.name + ": " + str(self.mf.__name__) + "(" + str(self.parameters) + ") " + tmp + tmp += str(f.__name__) + "(" + str(["{0:.2f}".format(p) for p in self.width_params[ct]]) + ") " + tmp = "(" + str(["{0:.2f}".format(p) for p in self.parameters]) + ") " + tmp + return self.name + ": " + str(self.mf.__name__) + tmp class PolynomialNonStationaryPartitioner(partitioner.Partitioner): @@ -218,6 +219,14 @@ class PolynomialNonStationaryPartitioner(partitioner.Partitioner): tmp = np.polyfit(rng, diff, deg=deg) return tmp + def scale_up(self,x,pct): + if x > 0: return x*(1+pct) + else: return x*pct + + def scale_down(self,x,pct): + if x > 0: return x*pct + else: return x*(1+pct) + def get_polynomial_perturbations(self, data, **kwargs): w = kwargs.get("window_size", int(len(data) / 5)) deg = kwargs.get("degree", 2) @@ -225,8 +234,7 @@ class PolynomialNonStationaryPartitioner(partitioner.Partitioner): tmax = [0] xmin = [data[0]] tmin = [0] - lengs = [0] - tlengs = [0] + l = len(data) for i in np.arange(0, l, w): @@ -237,15 +245,9 @@ class PolynomialNonStationaryPartitioner(partitioner.Partitioner): tn = min(sample) xmin.append(tn) tmin.append(np.ravel(np.argwhere(data == tn)).tolist()[0]) - lengs.append((tx - tn)/self.partitions) - tlengs.append(i) - cmax = np.polyfit(tmax, xmax, deg=deg) - #cmax = cmax.tolist() cmin = np.polyfit(tmin, xmin, deg=deg) - #cmin = cmin.tolist() - cmed = [] @@ -297,12 +299,37 @@ def fuzzify(inst, t, fuzzySets): return ret -def fuzzySeries(data, fuzzySets): +def fuzzySeries(data, fuzzySets, window_size=1, method='fuzzy'): fts = [] for t, i in enumerate(data): - mv = np.array([fs.membership(i, t) for fs in fuzzySets]) - ix = np.ravel(np.argwhere(mv > 0.0)) - sets = [fuzzySets[i] for i in ix] + tdisp = window_index(t, window_size) + mv = np.array([fs.membership(i, tdisp) for fs in fuzzySets]) + if len(mv) == 0: + sets = [check_bounds(i, fuzzySets, tdisp)] + else: + if method == 'fuzzy': + ix = np.ravel(np.argwhere(mv > 0.0)) + elif method == 'maximum': + mx = max(mv) + ix = np.ravel(np.argwhere(mv == mx)) + sets = [fuzzySets[i] for i in ix] fts.append(sets) return fts + +def window_index(t, window_size): + return t - (t % window_size) + + +def check_bounds(data, sets, t): + if data < sets[0].get_lower(t): + return sets[0] + elif data > sets[-1].get_upper(t): + return sets[-1] + + +def check_bounds_index(data, sets, t): + if data < sets[0].get_lower(t): + return 0 + elif data > sets[-1].get_upper(t): + return len(sets) -1 diff --git a/pyFTS/nonstationary/flrg.py b/pyFTS/nonstationary/flrg.py index 48e0803..0f46f8f 100644 --- a/pyFTS/nonstationary/flrg.py +++ b/pyFTS/nonstationary/flrg.py @@ -1,5 +1,6 @@ from pyFTS import flrg +from pyFTS.nonstationary import common class NonStationaryFLRG(flrg.FLRG): @@ -9,18 +10,41 @@ class NonStationaryFLRG(flrg.FLRG): self.LHS = LHS self.RHS = set() - def get_midpoint(self, t): - if self.midpoint is None: - tmp = [r.get_midpoint(t) for r in self.RHS] - self.midpoint = sum(tmp) / len(tmp) - return self.midpoint + def get_membership(self, data, t, window_size=1): + ret = 0.0 + if isinstance(self.LHS, (list, set)): + assert len(self.LHS) == len(data) - def get_lower(self, t): + ret = min([self.LHS[ct].membership(dat, common.window_index(t - (self.order - ct), window_size)) + for ct, dat in enumerate(data)]) + else: + ret = self.LHS.membership(data, common.window_index(t, window_size)) + return ret + + def get_midpoint(self, t, window_size=1): + if len(self.RHS) > 0: + if isinstance(self.RHS, (list,set)): + tmp = [r.get_midpoint(common.window_index(t, window_size)) for r in self.RHS] + elif isinstance(self.RHS, dict): + tmp = [self.RHS[r].get_midpoint(common.window_index(t, window_size)) for r in self.RHS.keys()] + return sum(tmp) / len(tmp) + else: + return self.LHS[-1].get_midpoint(common.window_index(t, window_size)) + + + def get_lower(self, t, window_size=1): if self.lower is None: - self.lower = min([r.get_lower(t) for r in self.RHS]) + if len(self.RHS) > 0: + self.lower = min([r.get_lower(common.window_index(t, window_size)) for r in self.RHS]) + else: + self.lower = self.LHS[-1].get_lower(common.window_index(t, window_size)) + return self.lower - def get_upper(self, t): + def get_upper(self, t, window_size=1): if self.upper is None: - self.upper = min([r.get_upper(t) for r in self.RHS]) + if len(self.RHS) > 0: + self.upper = min([r.get_upper(common.window_index(t, window_size)) for r in self.RHS]) + else: + self.upper = self.LHS[-1].get_upper(common.window_index(t, window_size)) return self.upper \ No newline at end of file diff --git a/pyFTS/nonstationary/honsfts.py b/pyFTS/nonstationary/honsfts.py index 98be909..76addfa 100644 --- a/pyFTS/nonstationary/honsfts.py +++ b/pyFTS/nonstationary/honsfts.py @@ -2,6 +2,7 @@ import numpy as np from pyFTS.common import FuzzySet, FLR from pyFTS import fts, hofts from pyFTS.nonstationary import common, flrg +from pyFTS import tree class HighOrderNonStationaryFLRG(flrg.NonStationaryFLRG): @@ -37,31 +38,64 @@ class HighOrderNonStationaryFLRG(flrg.NonStationaryFLRG): return self.strLHS() + " -> " + tmp -class HighOrderNonStationaryFTS(hofts.HighOrderFLRG): +class HighOrderNonStationaryFTS(hofts.HighOrderFTS): """NonStationaryFTS Fuzzy Time Series""" def __init__(self, name, **kwargs): - super(HighOrderNonStationaryFTS, self).__init__(1, "HONSFTS " + name, **kwargs) + super(HighOrderNonStationaryFTS, self).__init__("HONSFTS " + name, **kwargs) self.name = "High Order Non Stationary FTS" self.detail = "" self.flrgs = {} - def generateFLRG(self, flrs): + def generate_flrg(self, data, **kwargs): flrgs = {} - l = len(flrs) - for k in np.arange(self.order + 1, l): - flrg = HighOrderNonStationaryFLRG(self.order) + l = len(data) + window_size = kwargs.get("window_size", 1) + for k in np.arange(self.order, l): + if self.dump: print("FLR: " + str(k)) - for kk in np.arange(k - self.order, k): - flrg.appendLHS(flrs[kk].LHS) + sample = data[k - self.order: k] - if flrg.strLHS() in flrgs: - flrgs[flrg.strLHS()].appendRHS(flrs[k].RHS) - else: - flrgs[flrg.strLHS()] = flrg; - flrgs[flrg.strLHS()].appendRHS(flrs[k].RHS) - return (flrgs) + disp = common.window_index(k, window_size) - def train(self, data, sets=None,order=1,parameters=None): + rhs = [set for set in self.sets if set.membership(data[k], disp) > 0.0] + + if len(rhs) == 0: + rhs = [common.check_bounds(data[k], self.sets, disp)] + + lags = {} + + for o in np.arange(0, self.order): + tdisp = common.window_index(k - (self.order - o), window_size) + lhs = [set for set in self.sets if set.membership(sample[o], tdisp) > 0.0] + + if len(lhs) == 0: + lhs = [common.check_bounds(sample[o], self.sets, tdisp)] + + lags[o] = lhs + + root = tree.FLRGTreeNode(None) + + self.build_tree_without_order(root, lags, 0) + + # Trace the possible paths + for p in root.paths(): + flrg = HighOrderNonStationaryFLRG(self.order) + path = list(reversed(list(filter(None.__ne__, p)))) + + for c, e in enumerate(path, start=0): + flrg.appendLHS(e) + + if flrg.strLHS() not in flrgs: + flrgs[flrg.strLHS()] = flrg; + + for st in rhs: + flrgs[flrg.strLHS()].appendRHS(st) + + return flrgs + + def train(self, data, sets=None, order=2, parameters=None): + + self.order = order if sets is not None: self.sets = sets @@ -69,43 +103,72 @@ class HighOrderNonStationaryFTS(hofts.HighOrderFLRG): self.sets = self.partitioner.sets ndata = self.doTransformations(data) - tmpdata = common.fuzzySeries(ndata, self.sets) - flrs = FLR.generateNonRecurrentFLRs(tmpdata) - self.flrgs = self.generateFLRG(flrs) + #tmpdata = common.fuzzySeries(ndata, self.sets) + #flrs = FLR.generateRecurrentFLRs(ndata) + window_size = parameters if parameters is not None else 1 + self.flrgs = self.generate_flrg(ndata, window_size=window_size) def forecast(self, data, **kwargs): time_displacement = kwargs.get("time_displacement",0) + window_size = kwargs.get("window_size", 1) + ndata = np.array(self.doTransformations(data)) l = len(ndata) ret = [] - for k in np.arange(0, l): + for k in np.arange(self.order, l+1): #print("input: " + str(ndata[k])) - tdisp = k + time_displacement + disp = common.window_index(k + time_displacement, window_size) - affected_sets = [ [set, set.membership(ndata[k], tdisp)] - for set in self.sets if set.membership(ndata[k], tdisp) > 0.0] + affected_flrgs = [] + affected_flrgs_memberships = [] - if len(affected_sets) == 0: - if self.sets[0].get_lower(tdisp) > ndata[k]: - affected_sets.append([self.sets[0], 1.0]) - elif self.sets[-1].get_upper(tdisp) < ndata[k]: - affected_sets.append([self.sets[-1], 1.0]) + lags = {} + + sample = ndata[k - self.order: k] + + for ct, dat in enumerate(sample): + tdisp = common.window_index((k + time_displacement) - (self.order - ct), window_size) + sel = [ct for ct, set in enumerate(self.sets) if set.membership(dat, tdisp) > 0.0] + + if len(sel) == 0: + sel.append(common.check_bounds_index(dat, self.sets, tdisp)) + + lags[ct] = sel + + # Build the tree with all possible paths + + root = tree.FLRGTreeNode(None) + + self.build_tree(root, lags, 0) + + # Trace the possible paths and build the PFLRG's + + for p in root.paths(): + path = list(reversed(list(filter(None.__ne__, p)))) + flrg = HighOrderNonStationaryFLRG(self.order) + + for kk in path: + flrg.appendLHS(self.sets[kk]) + + affected_flrgs.append(flrg) + affected_flrgs_memberships.append(flrg.get_membership(ndata[k - self.order: k], disp)) #print(affected_sets) tmp = [] - for aset in affected_sets: - if aset[0] in self.flrgs: - tmp.append(self.flrgs[aset[0].name].get_midpoint(tdisp) * aset[1]) + for ct, aset in enumerate(affected_flrgs): + if aset.strLHS() in self.flrgs: + tmp.append(self.flrgs[aset.strLHS()].get_midpoint(tdisp) * + affected_flrgs_memberships[ct]) else: - tmp.append(aset[0].get_midpoint(tdisp) * aset[1]) + tmp.append(aset.LHS[-1].get_midpoint(tdisp)) pto = sum(tmp) diff --git a/pyFTS/nonstationary/nsfts.py b/pyFTS/nonstationary/nsfts.py index 616733f..39b7cbb 100644 --- a/pyFTS/nonstationary/nsfts.py +++ b/pyFTS/nonstationary/nsfts.py @@ -32,8 +32,9 @@ class NonStationaryFTS(fts.FTS): self.name = "Non Stationary FTS" self.detail = "" self.flrgs = {} + self.method = kwargs.get("method",'fuzzy') - def generateFLRG(self, flrs): + def generate_flrg(self, flrs, **kwargs): flrgs = {} for flr in flrs: if flr.LHS.name in flrgs: @@ -41,9 +42,9 @@ class NonStationaryFTS(fts.FTS): else: flrgs[flr.LHS.name] = ConventionalNonStationaryFLRG(flr.LHS) flrgs[flr.LHS.name].append(flr.RHS) - return (flrgs) + return flrgs - def train(self, data, sets=None,order=1,parameters=None): + def train(self, data, sets=None, order=1, parameters=None): if sets is not None: self.sets = sets @@ -51,14 +52,19 @@ class NonStationaryFTS(fts.FTS): self.sets = self.partitioner.sets ndata = self.doTransformations(data) - tmpdata = common.fuzzySeries(ndata, self.sets) - flrs = FLR.generateNonRecurrentFLRs(tmpdata) - self.flrgs = self.generateFLRG(flrs) + window_size = parameters if parameters is not None else 1 + tmpdata = common.fuzzySeries(ndata, self.sets, window_size, method=self.method) + #print([k[0].name for k in tmpdata]) + flrs = FLR.generateRecurrentFLRs(tmpdata) + #print([str(k) for k in flrs]) + self.flrgs = self.generate_flrg(flrs) def forecast(self, data, **kwargs): time_displacement = kwargs.get("time_displacement",0) + window_size = kwargs.get("window_size", 1) + ndata = np.array(self.doTransformations(data)) l = len(ndata) @@ -69,25 +75,38 @@ class NonStationaryFTS(fts.FTS): #print("input: " + str(ndata[k])) - tdisp = k + time_displacement + tdisp = common.window_index(k + time_displacement, window_size) - affected_sets = [ [set, set.membership(ndata[k], tdisp)] - for set in self.sets if set.membership(ndata[k], tdisp) > 0.0] + if self.method == 'fuzzy': + affected_sets = [ [set, set.membership(ndata[k], tdisp)] + for set in self.sets if set.membership(ndata[k], tdisp) > 0.0] + elif self.method == 'maximum': + mv = [set.membership(ndata[k], tdisp) for set in self.sets] + ix = np.ravel(np.argwhere(mv == max(mv))) + affected_sets = [self.sets[x] for x in ix] if len(affected_sets) == 0: - if self.sets[0].get_lower(tdisp) > ndata[k]: - affected_sets.append([self.sets[0], 1.0]) - elif self.sets[-1].get_upper(tdisp) < ndata[k]: - affected_sets.append([self.sets[-1], 1.0]) + if self.method == 'fuzzy': + affected_sets.append([common.check_bounds(ndata[k], self.sets, tdisp), 1.0]) + else: + affected_sets.append(common.check_bounds(ndata[k], self.sets, tdisp)) #print(affected_sets) tmp = [] - for aset in affected_sets: - if aset[0] in self.flrgs: - tmp.append(self.flrgs[aset[0].name].get_midpoint(tdisp) * aset[1]) - else: - tmp.append(aset[0].get_midpoint(tdisp) * aset[1]) + + if len(affected_sets) == 1 and self.method == 'fuzzy': + tmp.append(affected_sets[0][0].get_midpoint(tdisp)) + else: + for aset in affected_sets: + if self.method == 'fuzzy': + if aset[0].name in self.flrgs: + tmp.append(self.flrgs[aset[0].name].get_midpoint(tdisp) * aset[1]) + elif self.method == 'maximum': + if aset.name in self.flrgs: + tmp.append(self.flrgs[aset.name].get_midpoint(tdisp)) + else: + tmp.append(aset.get_midpoint(tdisp)) pto = sum(tmp) @@ -103,6 +122,8 @@ class NonStationaryFTS(fts.FTS): time_displacement = kwargs.get("time_displacement",0) + window_size = kwargs.get("window_size", 1) + ndata = np.array(self.doTransformations(data)) l = len(ndata) @@ -111,7 +132,7 @@ class NonStationaryFTS(fts.FTS): for k in np.arange(0, l): - tdisp = k + time_displacement + tdisp = common.window_index(k + time_displacement, window_size) affected_sets = [ [set.name, set.membership(ndata[k], tdisp)] for set in self.sets if set.membership(ndata[k], tdisp) > 0.0] diff --git a/pyFTS/nonstationary/util.py b/pyFTS/nonstationary/util.py index 7214d6e..8c4b4b7 100644 --- a/pyFTS/nonstationary/util.py +++ b/pyFTS/nonstationary/util.py @@ -6,29 +6,48 @@ import matplotlib.pyplot as plt from pyFTS.common import Membership, Util -def plot_sets(uod, sets, start=0, end=10, tam=[5, 5], colors=None, save=False, file=None): +def plot_sets(sets, start=0, end=10, step=1, tam=[5, 5], colors=None, + save=False, file=None, axes=None, data=None, window_size = 1, only_lines=False): + + range = np.arange(start,end,step) ticks = [] - fig, axes = plt.subplots(nrows=1, ncols=1, figsize=tam) - for t in np.arange(start,end,1): - for ct, set in enumerate(sets): - set.membership(0, t) - param = set.perturbated_parameters[t] + if axes is None: + fig, axes = plt.subplots(nrows=1, ncols=1, figsize=tam) - if set.mf == Membership.trimf: - if t == start: - axes.plot([t, t+1, t], param, label=set.name) - else: - axes.plot([t, t + 1, t], param) + for ct, set in enumerate(sets): + if not only_lines: + for t in range: + tdisp = t - (t % window_size) + set.membership(0, tdisp) + param = set.perturbated_parameters[tdisp] - ticks.extend(["t+"+str(t),""]) + if set.mf == Membership.trimf: + if t == start: + line = axes.plot([t, t+1, t], param, label=set.name) + set.metadata['color'] = line[0].get_color() + else: + axes.plot([t, t + 1, t], param,c=set.metadata['color']) + + ticks.extend(["t+"+str(t),""]) + else: + tmp = [] + for t in range: + tdisp = t - (t % window_size) + set.membership(0, tdisp) + param = set.perturbated_parameters[tdisp] + tmp.append(np.polyval(param, tdisp)) + axes.plot(range, tmp, ls="--", c="blue") axes.set_ylabel("Universe of Discourse") axes.set_xlabel("Time") - plt.xticks([k for k in np.arange(0,end,1)], ticks, rotation='vertical') + plt.xticks([k for k in range], ticks, rotation='vertical') handles0, labels0 = axes.get_legend_handles_labels() lgd = axes.legend(handles0, labels0, loc=2, bbox_to_anchor=(1, 1)) + if data is not None: + axes.plot(np.arange(start, start + len(data), 1), data,c="black") + plt.tight_layout() Util.showAndSaveImage(fig, file, save) diff --git a/pyFTS/partitioners/partitioner.py b/pyFTS/partitioners/partitioner.py index d7f9f4b..d704e53 100644 --- a/pyFTS/partitioners/partitioner.py +++ b/pyFTS/partitioners/partitioner.py @@ -79,4 +79,7 @@ class Partitioner(object): ax.plot(tmpx, tmpy) def __str__(self): - return self.name + ":\n ".join([str(a) + "\n" for a in self.sets]) + tmp = self.name + ":\n" + for a in self.sets: + tmp += str(a)+ "\n" + return tmp diff --git a/pyFTS/pwfts.py b/pyFTS/pwfts.py index 382c598..0dcd8ff 100644 --- a/pyFTS/pwfts.py +++ b/pyFTS/pwfts.py @@ -127,9 +127,9 @@ class ProbabilisticWeightedFTS(ifts.IntervalFTS): flrs = FLR.generateRecurrentFLRs(tmpdata) self.flrgs = self.generateFLRG(flrs) else: - self.flrgs = self.generateFLRGfuzzy(data) + self.flrgs = self.generate_flrg(data) - def generateFLRGfuzzy(self, data): + def generate_flrg(self, data): flrgs = {} l = len(data) for k in np.arange(self.order, l): @@ -175,7 +175,7 @@ class ProbabilisticWeightedFTS(ifts.IntervalFTS): self.global_frequency_count = self.global_frequency_count + tmp_fq - return (flrgs) + return flrgs def generateFLRG(self, flrs): flrgs = {} @@ -274,18 +274,6 @@ class ProbabilisticWeightedFTS(ifts.IntervalFTS): ret = sum(np.array([pi * s.lower for s in flrg.LHS])) return ret - def build_tree_without_order(self, node, lags, level): - - if level not in lags: - return - - for s in lags[level]: - node.appendChild(tree.FLRGTreeNode(s)) - - for child in node.getChildren(): - self.build_tree_without_order(child, lags, level + 1) - - def forecast(self, data, **kwargs): ndata = np.array(self.doTransformations(data)) @@ -299,19 +287,17 @@ class ProbabilisticWeightedFTS(ifts.IntervalFTS): # print(k) affected_flrgs = [] - affected_rhs = [] affected_flrgs_memberships = [] norms = [] mp = [] # Find the sets which membership > 0 for each lag - count = 0 lags = {} if self.order > 1: subset = ndata[k - (self.order - 1): k + 1] - for instance in subset: + for count, instance in enumerate(subset): mb = FuzzySet.fuzzyInstance(instance, self.sets) tmp = np.argwhere(mb) idx = np.ravel(tmp) # flatten the array @@ -325,7 +311,6 @@ class ProbabilisticWeightedFTS(ifts.IntervalFTS): raise Exception(instance) lags[count] = idx - count = count + 1 # Build the tree with all possible paths @@ -346,7 +331,7 @@ class ProbabilisticWeightedFTS(ifts.IntervalFTS): affected_flrgs.append(flrg) # Find the general membership of FLRG - affected_flrgs_memberships.append(min(self.get_sequence_membership(subset, flrg.LHS))) + affected_flrgs_memberships.append(flrg.get_membership()) else: diff --git a/pyFTS/tests/nonstationary.py b/pyFTS/tests/nonstationary.py index 3df23c1..c33286c 100644 --- a/pyFTS/tests/nonstationary.py +++ b/pyFTS/tests/nonstationary.py @@ -1,7 +1,7 @@ import os import numpy as np from pyFTS.common import Membership -from pyFTS.nonstationary import common,perturbation,util,nsfts +from pyFTS.nonstationary import common,perturbation,util,nsfts, honsfts from pyFTS.partitioners import Grid import matplotlib.pyplot as plt import pandas as pd @@ -50,27 +50,33 @@ print(tmp) passengers = pd.read_csv("DataSets/AirPassengers.csv", sep=",") passengers = np.array(passengers["Passengers"]) -ts = 80 +ts = 100 +ws=12 trainp = passengers[:ts] testp = passengers[ts:] -tmp_fsp = Grid.GridPartitioner(trainp[:50], 10) +tmp_fsp = Grid.GridPartitioner(trainp[:ws], 15) -fsp = common.PolynomialNonStationaryPartitioner(trainp, tmp_fsp, window_size=20, degree=1) +fsp = common.PolynomialNonStationaryPartitioner(trainp, tmp_fsp, window_size=ws, degree=1) -nsftsp = nsfts.NonStationaryFTS("", partitioner=fsp) -nsftsp.train(trainp[:50]) +#nsftsp = honsfts.HighOrderNonStationaryFTS("", partitioner=fsp) +nsftsp = nsfts.NonStationaryFTS("", partitioner=fsp, method='fuzzy') + +#nsftsp.train(trainp, order=1, parameters=ws) print(fsp) -print(nsftsp) +#print(nsftsp) -tmpp = nsftsp.forecast(testp, time_displacement=ts) +#tmpp = nsftsp.forecast(passengers[55:65], time_displacement=55, window_size=ws) -print(testp) -print(tmpp) +#print(passengers[100:120]) +#print(tmpp) + +#util.plot_sets(fsp.sets,tam=[10, 5], start=0, end=100, step=2, data=passengers[:100], +# window_size=ws, only_lines=False) #fig, axes = plt.subplots(nrows=1, ncols=1, figsize=[15,5])