diff --git a/pyFTS/nonstationary/common.py b/pyFTS/nonstationary/common.py index 9d4d34f..3f6fd0c 100644 --- a/pyFTS/nonstationary/common.py +++ b/pyFTS/nonstationary/common.py @@ -8,6 +8,8 @@ IEEE Transactions on Fuzzy Systems, v. 16, n. 4, p. 1072-1086, 2008. import numpy as np from pyFTS import * from pyFTS.common import FuzzySet, Membership +from pyFTS.partitioners import partitioner +from pyFTS.nonstationary import perturbation class MembershipFunction(object): @@ -32,23 +34,31 @@ class MembershipFunction(object): self.parameters = parameters self.location = kwargs.get("location", None) self.location_params = kwargs.get("location_params", None) + self.location_roots = kwargs.get("location_roots", 0) self.width = kwargs.get("width", None) self.width_params = kwargs.get("width_params", None) + self.width_roots = kwargs.get("width_roots", 0) self.noise = kwargs.get("noise", None) self.noise_params = kwargs.get("noise_params", None) - self.cache = {} + self.perturbated_parameters = {} + + if self.location is not None and not isinstance(self.location, (list,set)): + self.location = [self.location] + self.location_params = [self.location_params] + self.location_roots = [self.location_roots] + + if self.width is not None and not isinstance(self.width, (list, set)): + self.width = [self.width] + self.width_params = [self.width_params] + self.width_roots = [self.width_roots] def perform_location(self, t, param): if self.location is None: return param - if not isinstance(self.location, (list,set)): - self.location = [self.location] - self.location_params = [self.location_params] - l = len(self.location) - inc = sum([self.location[k](t, self.location_params[k]) for k in np.arange(0,l)] ) + inc = sum([self.location[k](t + self.location_roots[k], self.location_params[k]) for k in np.arange(0,l)] ) if self.mf == Membership.gaussmf: #changes only the mean parameter @@ -66,13 +76,9 @@ class MembershipFunction(object): if self.width is None: return param - if not isinstance(self.width, (list, set)): - self.width = [self.width] - self.width_params = [self.width_params] - l = len(self.width) - inc = sum([self.width[k](t, self.width_params[k]) for k in np.arange(0, l)]) + inc = sum([self.width[k](t + self.width_roots[k], self.width_params[k]) for k in np.arange(0, l)]) if self.mf == Membership.gaussmf: #changes only the variance parameter @@ -81,7 +87,8 @@ class MembershipFunction(object): #changes only the smooth parameter return [param[0] + inc, param[1]] elif self.mf == Membership.trimf: - return [param[0] - inc, param[1], param[2] + inc] + tmp = inc/2 + return [param[0] - tmp, param[1], param[2] + tmp] elif self.mf == Membership.trapmf: l = (param[3]-param[0]) rab = (param[1] - param[0]) / l @@ -97,19 +104,83 @@ class MembershipFunction(object): :return: membership value of x at this fuzzy set """ - if t not in self.cache: - param = self.parameters - param = self.perform_location(t, param) - param = self.perform_width(t, param) - self.cache[t] = param + self.perturbate_parameters(t) - tmp = self.mf(x, self.cache[t]) + tmp = self.mf(x, self.perturbated_parameters[t]) if self.noise is not None: tmp += self.noise(t, self.noise_params) return tmp + def perturbate_parameters(self, t): + if t not in self.perturbated_parameters: + param = self.parameters + param = self.perform_location(t, param) + param = self.perform_width(t, param) + self.perturbated_parameters[t] = param + + def __str__(self): + tmp = "" + if self.location is not None: + tmp += "Loc. Pert.: " + for ct, f in enumerate(self.location): + tmp += str(f.__name__) + "(" + str(self.location_params[ct]) + ") " + if self.width is not None: + tmp += "Wid. Pert.: " + for ct, f in enumerate(self.width): + tmp += str(f.__name__) + "(" + str(self.width_params[ct]) + ") " + return str(self.mf.__name__) + "(" + str(self.parameters) + ") " + tmp + + def get_midpoint(self, t): + + self.perturbate_parameters(t) + + if self.mf == Membership.gaussmf: + return self.perturbated_parameters[t][0] + elif self.mf == Membership.sigmf: + return self.perturbated_parameters[t][1] + elif self.mf == Membership.trimf: + return self.perturbated_parameters[t][1] + elif self.mf == Membership.trapmf: + param = self.perturbated_parameters[t] + return (param[2] - param[1]) / 2 + else: + return self.perturbated_parameters[t] + + def get_lower(self, t): + + self.perturbate_parameters(t) + param = self.perturbated_parameters[t] + + if self.mf == Membership.gaussmf: + return param[0] - 3*param[1] + elif self.mf == Membership.sigmf: + return param[0] - param[1] + elif self.mf == Membership.trimf: + return param[0] + elif self.mf == Membership.trapmf: + return param[0] + else: + return param + + def get_upper(self, t): + + self.perturbate_parameters(t) + param = self.perturbated_parameters[t] + + if self.mf == Membership.gaussmf: + return param[0] + 3*param[1] + elif self.mf == Membership.sigmf: + return param[0] + param[1] + elif self.mf == Membership.trimf: + return param[2] + elif self.mf == Membership.trapmf: + return param[3] + else: + return param + + class FuzzySet(FuzzySet.FuzzySet): """ @@ -127,4 +198,105 @@ class FuzzySet(FuzzySet.FuzzySet): def membership(self, x, t): return self.mf.membership(x,t) + def __str__(self): + return self.name + ": " + str(self.mf) + + +class PolynomialNonStationaryPartitioner(partitioner.Partitioner): + """ + Non Stationary Universe of Discourse Partitioner + """ + + def __init__(self, data, part, **kwargs): + """""" + super(PolynomialNonStationaryPartitioner, self).__init__(name=part.name, data=data, npart=part.partitions, + func=part.membership_function, names=part.setnames, + prefix=part.prefix, transformation=part.transformation, + indexer=part.indexer) + + self.sets = [] + + loc_params, wid_params = self.get_polynomial_perturbations(data, **kwargs) + + for ct, set in enumerate(part.sets): + loc_roots = np.roots(loc_params[ct])[0] + wid_roots = np.roots(wid_params[ct])[0] + mf = MembershipFunction(name=set.name, mf=set.mf, parameters=set.parameters, + location=perturbation.polynomial, + location_params=loc_params[ct], + location_roots=loc_roots, #**kwargs) + width=perturbation.polynomial, + width_params=wid_params[ct], + width_roots=wid_roots, **kwargs) + tmp = FuzzySet(set.name, mf, **kwargs) + + self.sets.append(tmp) + + def poly_width(self, par1, par2, rng, deg): + a = np.polyval(par1, rng) + b = np.polyval(par2, rng) + diff = [b[k] - a[k] for k in rng] + tmp = np.polyfit(rng, diff, deg=deg) + return tmp + + def get_polynomial_perturbations(self, data, **kwargs): + w = kwargs.get("window_size", int(len(data) / 5)) + deg = kwargs.get("degree", 2) + xmax = [0] + tmax = [0] + xmin = [0] + tmin = [0] + lengs = [0] + tlengs = [0] + l = len(data) + + for i in np.arange(0, l, w): + sample = data[i:i + w] + tx = max(sample) + xmax.append(tx) + tmax.append(np.ravel(np.argwhere(data == tx)).tolist()[0]) + 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 = [] + + for d in np.arange(0, deg + 1): + cmed.append(np.linspace(cmin[d], cmax[d], self.partitions)[1:self.partitions - 1]) + + loc_params = [cmin.tolist()] + for i in np.arange(0, self.partitions - 2): + tmp = [cmed[k][i] for k in np.arange(0, deg + 1)] + loc_params.append(tmp) + loc_params.append(cmax.tolist()) + + rng = np.arange(0, l) + + clen = [] + + for i in np.arange(1, self.partitions-1): + tmp = self.poly_width(loc_params[i - 1], loc_params[i + 1], rng, deg) + clen.append(tmp) + + tmp = self.poly_width(loc_params[0], loc_params[1], rng, deg) + clen.insert(0, tmp) + + tmp = self.poly_width(loc_params[self.partitions-2], loc_params[self.partitions-1], rng, deg) + clen.append(tmp) + + tmp = (loc_params, clen) + + return tmp + + def build(self, data): + pass diff --git a/pyFTS/nonstationary/perturbation.py b/pyFTS/nonstationary/perturbation.py index 3251eb9..7a8a079 100644 --- a/pyFTS/nonstationary/perturbation.py +++ b/pyFTS/nonstationary/perturbation.py @@ -8,15 +8,11 @@ from pyFTS.common import FuzzySet, Membership def linear(x, parameters): - return parameters[0]*x + parameters[1] + return np.polyval(parameters, x) def polynomial(x, parameters): - n = len(parameters) - tmp = 0.0 - for k in np.arange(0,n): - tmp += parameters[k] * x**k - return tmp + return np.polyval(parameters, x) def exponential(x, parameters): @@ -24,4 +20,4 @@ def exponential(x, parameters): def periodic(x, parameters): - return np.sin(x * parameters[0] + parameters[1]) \ No newline at end of file + return parameters[0] * np.sin(x * parameters[1]) \ No newline at end of file diff --git a/pyFTS/nonstationary/util.py b/pyFTS/nonstationary/util.py index 07094c6..5243954 100644 --- a/pyFTS/nonstationary/util.py +++ b/pyFTS/nonstationary/util.py @@ -6,19 +6,22 @@ import matplotlib.pyplot as plt from pyFTS.common import Membership, Util -def plot_sets(uod, sets, num=10, tam=[5, 5], colors=None, save=False, file=None): +def plot_sets(uod, sets, start=0, end=10, tam=[5, 5], colors=None, save=False, file=None): ticks = [] fig, axes = plt.subplots(nrows=1, ncols=1, figsize=tam) - for t in np.arange(0,num,1): + for t in np.arange(start,end,1): for ct, set in enumerate(sets): - x = [2*t + set.membership(v, t) for v in uod] - if colors is not None: c = colors[ct] - axes.plot(x, uod, c=c) + set.membership(0, t) + param = set.mf.perturbated_parameters[t] + + if set.mf.mf == Membership.trimf: + axes.plot([t, t+1, t], param) + ticks.extend(["t+"+str(t),""]) axes.set_ylabel("Universe of Discourse") axes.set_xlabel("Time") - plt.xticks([k for k in np.arange(0,2*num,1)], ticks, rotation='vertical') + plt.xticks([k for k in np.arange(0,2*end,1)], ticks, rotation='vertical') plt.tight_layout() diff --git a/pyFTS/tests/nonstationary.py b/pyFTS/tests/nonstationary.py index 1f96337..9628b7f 100644 --- a/pyFTS/tests/nonstationary.py +++ b/pyFTS/tests/nonstationary.py @@ -1,20 +1,38 @@ import numpy as np from pyFTS.common import Membership -from pyFTS.nonstationary import common,pertubation,util -import importlib +from pyFTS.nonstationary import common,perturbation,util +from pyFTS.partitioners import Grid -importlib.reload(util) -uod = np.arange(0,20,0.1) +def generate_heteroskedastic_linear(mu_ini, sigma_ini, mu_inc, sigma_inc, it=10, num=35): + mu = mu_ini + sigma = sigma_ini + ret = [] + for k in np.arange(0,it): + ret.extend(np.random.normal(mu, sigma, num)) + mu += mu_inc + sigma += sigma_inc + return ret -kwargs = {'location': pertubation.linear, 'location_params': [1,0], - 'width': pertubation.linear, 'width_params': [1,0]} -mf1 = common.MembershipFunction('A1',Membership.trimf,[0,1,2], **kwargs) -mf2 = common.MembershipFunction('A2',Membership.trimf,[1,2,3], **kwargs) -mf3 = common.MembershipFunction('A3',Membership.trimf,[2,3,4], **kwargs) -mf4 = common.MembershipFunction('A4',Membership.trimf,[3,4,5], **kwargs) +lmv1 = generate_heteroskedastic_linear(1,0.1,1,0.3) -sets = [mf1, mf2, mf3, mf4] -util.plot_sets(uod, sets) + +ns = 5 #number of fuzzy sets +ts = 200 +train = lmv1[:ts] +w = 25 +deg = 4 + + +tmp_fs = Grid.GridPartitioner(train[:35], 10) + +fs = common.PolynomialNonStationaryPartitioner(train, tmp_fs, window_size=35, degree=1) + +uod = np.arange(0, 2, step=0.02) + +util.plot_sets(uod, fs.sets,tam=[15, 5], start=0, end=10) + +for set in fs.sets: + print(set)