From 1d583e8b0a33eafaa16f10934de4fdd0c7e9cea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=C3=B4nio=20C=C3=A2ndido?= Date: Wed, 28 Feb 2018 13:45:55 -0300 Subject: [PATCH] - Seasonal partitioner --- pyFTS/common/Composite.py | 29 ++++++-- pyFTS/common/FuzzySet.py | 6 +- pyFTS/models/seasonal/common.py | 63 ++++++++++++++--- pyFTS/models/seasonal/partitioner.py | 101 +++++++++++++++++++++++++++ pyFTS/partitioners/partitioner.py | 66 ++++++++++------- pyFTS/tests/seasonal.py | 19 +++++ 6 files changed, 240 insertions(+), 44 deletions(-) create mode 100644 pyFTS/models/seasonal/partitioner.py create mode 100644 pyFTS/tests/seasonal.py diff --git a/pyFTS/common/Composite.py b/pyFTS/common/Composite.py index f087ab0..aa1a035 100644 --- a/pyFTS/common/Composite.py +++ b/pyFTS/common/Composite.py @@ -11,14 +11,19 @@ class FuzzySet(FuzzySet.FuzzySet): """ Composite Fuzzy Set """ - def __init__(self, name): + def __init__(self, name, superset=False): """ Create an empty composite fuzzy set :param name: fuzzy set name """ - super(FuzzySet, self).__init__(self, name=name, mf=None, parameters=None, centroid=None) - self.mf = [] - self.parameters = [] + super(FuzzySet, self).__init__(self, name, None, None, None, type='composite') + self.superset = superset + if self.superset: + self.sets = [] + else: + self.mf = [] + self.parameters = [] + def membership(self, x): """ @@ -26,7 +31,10 @@ class FuzzySet(FuzzySet.FuzzySet): :param x: input value :return: membership value of x at this fuzzy set """ - return min([self.mf[ct](x, self.parameters[ct]) for ct in np.arange(0, len(self.mf))]) + if self.superset: + return max([s.membership(x) for s in self.sets]) + else: + return min([self.mf[ct](x, self.parameters[ct]) for ct in np.arange(0, len(self.mf))]) def append(self, mf, parameters): """ @@ -36,4 +44,13 @@ class FuzzySet(FuzzySet.FuzzySet): :return: """ self.mf.append(mf) - self.parameters.append(parameters) \ No newline at end of file + self.parameters.append(parameters) + + def append_set(self, set): + """ + Adds a new function to composition + :param mf: + :param parameters: + :return: + """ + self.sets.append(set) \ No newline at end of file diff --git a/pyFTS/common/FuzzySet.py b/pyFTS/common/FuzzySet.py index 9e14c02..cd38c24 100644 --- a/pyFTS/common/FuzzySet.py +++ b/pyFTS/common/FuzzySet.py @@ -7,7 +7,7 @@ class FuzzySet(object): """ Fuzzy Set """ - def __init__(self, name, mf, parameters, centroid): + def __init__(self, name, mf, parameters, centroid, alpha=1.0, type='common'): """ Create a Fuzzy Set :param name: fuzzy set name @@ -19,6 +19,8 @@ class FuzzySet(object): self.mf = mf self.parameters = parameters self.centroid = centroid + self.alpha = alpha + self.type = type ":param Z: Partition function in respect to the membership function" self.Z = None if self.mf == Membership.trimf: @@ -35,7 +37,7 @@ class FuzzySet(object): :param x: input value :return: membership value of x at this fuzzy set """ - return self.mf(x, self.parameters) + return self.mf(x, self.parameters) * self.alpha def partition_function(self,uod=None, nbins=100): """ diff --git a/pyFTS/models/seasonal/common.py b/pyFTS/models/seasonal/common.py index cdc44d5..6967352 100644 --- a/pyFTS/models/seasonal/common.py +++ b/pyFTS/models/seasonal/common.py @@ -7,13 +7,25 @@ from pyFTS.partitioners import partitioner, Grid class DateTime(Enum): year = 1 - month = 2 - day_of_month = 3 - day_of_year = 4 - day_of_week = 5 + month = 12 + day_of_month = 30 + day_of_year = 364 + day_of_week = 7 hour = 6 minute = 7 second = 8 + hour_of_day = 24 + hour_of_week = 168 + hour_of_month = 744 + hour_of_year = 8736 + minute_of_hour = 60 + minute_of_day = 1440 + minute_of_week = 10080 + minute_of_month = 44640 + minute_of_year = 524160 + second_of_minute = 60.00001 + second_of_hour = 3600 + second_of_day = 86400 def strip_datepart(self, date, date_part): @@ -27,12 +39,45 @@ def strip_datepart(self, date, date_part): tmp = date.day elif date_part == DateTime.day_of_week: tmp = date.weekday() - elif date_part == DateTime.hour: + elif date_part == DateTime.hour or date_part == DateTime.hour_of_day: tmp = date.hour - elif date_part == DateTime.minute: + elif date_part == DateTime.hour_of_week: + wk = (date.weekday()-1) * 24 + tmp = date.hour + wk + elif date_part == DateTime.hour_of_month: + wk = (date.day-1) * 24 + tmp = date.hour + wk + elif date_part == DateTime.hour_of_year: + wk = (date.timetuple().tm_yday-1) * 24 + tmp = date.hour + wk + elif date_part == DateTime.minute or date_part == DateTime.minute_of_hour: tmp = date.minute - elif date_part == DateTime.second: + elif date_part == DateTime.minute_of_day: + wk = date.hour * 60 + tmp = date.minute + wk + elif date_part == DateTime.minute_of_week: + wk1 = (date.weekday()-1) * 1440 #24 * 60 + wk2 = date.hour * 60 + tmp = date.minute + wk1 + wk2 + elif date_part == DateTime.minute_of_month: + wk1 = (date.day - 1) * 1440 #24 * 60 + wk2 = date.hour * 60 + tmp = date.minute + wk1 + wk2 + elif date_part == DateTime.minute_of_year: + wk1 = (date.timetuple().tm_yday - 1) * 1440 #24 * 60 + wk2 = date.hour * 60 + tmp = date.minute + wk1 + wk2 + elif date_part == DateTime.second or date_part == DateTime.second_of_minute: tmp = date.second + elif date_part == DateTime.second_of_hour: + wk1 = date.minute * 60 + tmp = date.second + wk1 + elif date_part == DateTime.second_of_day: + wk1 = date.hour * 3600 #60 * 60 + wk2 = date.minute * 60 + tmp = date.second + wk1 + wk2 + else: + raise Exception("Unknown DateTime value!") return tmp @@ -42,8 +87,8 @@ class FuzzySet(FuzzySet.FuzzySet): Temporal/Seasonal Fuzzy Set """ - def __init__(self, datepart, name, mf, parameters, centroid): - super(FuzzySet, self).__init__(name, mf, parameters, centroid) + def __init__(self, datepart, name, mf, parameters, centroid, alpha=1.0): + super(FuzzySet, self).__init__(name, mf, parameters, centroid, alpha) self.datepart = datepart def membership(self, x): diff --git a/pyFTS/models/seasonal/partitioner.py b/pyFTS/models/seasonal/partitioner.py new file mode 100644 index 0000000..11d686f --- /dev/null +++ b/pyFTS/models/seasonal/partitioner.py @@ -0,0 +1,101 @@ +from pyFTS.common import Membership +from pyFTS.common.Composite import FuzzySet as Composite +from pyFTS.partitioners import partitioner, Grid +from pyFTS.models.seasonal.common import DateTime, FuzzySet, strip_datepart +import numpy as np +import matplotlib.pylab as plt + + +class TimeGridPartitioner(partitioner.Partitioner): + """Even Length DateTime Grid Partitioner""" + + def __init__(self, data, npart, season, func=Membership.trimf, names=None): + """ + Even Length Grid Partitioner + :param data: Training data of which the universe of discourse will be extracted. The universe of discourse is the open interval between the minimum and maximum values of the training data. + :param npart: The number of universe of discourse partitions, i.e., the number of fuzzy sets that will be created + :param func: Fuzzy membership function (pyFTS.common.Membership) + """ + super(TimeGridPartitioner, self).__init__("TimeGrid", data, npart, func=func, names=names, transformation=None, + indexer=None, preprocess=False) + + self.season = season + if self.season == DateTime.year: + ndata = [strip_datepart(k, season) for k in data] + self.min = min(ndata) + self.max = max(ndata) + else: + tmp = (self.season.value / self.partitions) / 2 + self.min = tmp + self.max = self.season.value + tmp + + self.sets = self.build(None) + + def build(self, data): + sets = [] + + if self.season == DateTime.year: + dlen = (self.max - self.min) + partlen = dlen / self.partitions + else: + partlen = self.season.value / self.partitions + pl2 = partlen / 2 + + count = 0 + for c in np.arange(self.min, self.max, partlen): + set_name = self.prefix + str(count) if self.setnames is None else self.setnames[count] + if self.membership_function == Membership.trimf: + if c == self.min: + tmp = Composite(set_name, superset=True) + tmp.append_set(FuzzySet(self.season, set_name, Membership.trimf, + [self.season.value - pl2, self.season.value, + self.season.value + 0.0000001], self.season.value, alpha=.5)) + tmp.append_set(FuzzySet(self.season, set_name, Membership.trimf, + [c - partlen, c, c + partlen], c)) + tmp.centroid = c + sets.append(tmp) + else: + sets.append(FuzzySet(self.season, set_name, Membership.trimf, + [c - partlen, c, c + partlen], c)) + elif self.membership_function == Membership.gaussmf: + sets.append(FuzzySet(self.season, set_name, Membership.gaussmf, [c, partlen / 3], c)) + elif self.membership_function == Membership.trapmf: + q = partlen / 4 + if c == self.min: + tmp = Composite(set_name, superset=True) + tmp.append_set(FuzzySet(self.season, set_name, Membership.trimf, + [self.season.value - pl2, self.season.value, + self.season.value + 0.0000001], 0)) + tmp.append_set(FuzzySet(self.season, set_name, Membership.trapmf, + [c - partlen, c - q, c + q, c + partlen], c)) + tmp.centroid = c + sets.append(tmp) + else: + sets.append(FuzzySet(self.season, set_name, Membership.trapmf, + [c - partlen, c - q, c + q, c + partlen], c)) + count += 1 + + self.min = 0 + + return sets + + def plot(self, ax): + """ + Plot the + :param ax: + :return: + """ + ax.set_title(self.name) + ax.set_ylim([0, 1]) + ax.set_xlim([0, self.season.value]) + ticks = [] + x = [] + for s in self.sets: + if s.type == 'common': + self.plot_set(ax, s) + elif s.type == 'composite': + for ss in s.sets: + self.plot_set(ax, ss) +# ticks.append(str(round(s.centroid, 0)) + '\n' + s.name) +# x.append(s.centroid) +# plt.xticks(x, ticks) diff --git a/pyFTS/partitioners/partitioner.py b/pyFTS/partitioners/partitioner.py index caff6e5..eaa6f78 100644 --- a/pyFTS/partitioners/partitioner.py +++ b/pyFTS/partitioners/partitioner.py @@ -8,7 +8,8 @@ class Partitioner(object): Universe of Discourse partitioner. Split data on several fuzzy sets """ - def __init__(self, name, data, npart, func=Membership.trimf, names=None, prefix="A", transformation=None, indexer=None): + def __init__(self, name, data, npart, func=Membership.trimf, names=None, prefix="A", + transformation=None, indexer=None, preprocess=True): """ Universe of Discourse partitioner scheme. Split data on several fuzzy sets :param name: partitioner name @@ -28,31 +29,33 @@ class Partitioner(object): self.transformation = transformation self.indexer = indexer - if self.indexer is not None: - ndata = self.indexer.get_data(data) - else: - ndata = data + if preprocess: - if transformation is not None: - ndata = transformation.apply(ndata) - else: - ndata = data + if self.indexer is not None: + ndata = self.indexer.get_data(data) + else: + ndata = data - _min = min(ndata) - if _min < 0: - self.min = _min * 1.1 - else: - self.min = _min * 0.9 + if transformation is not None: + ndata = transformation.apply(ndata) + else: + ndata = data - _max = max(ndata) - if _max > 0: - self.max = _max * 1.1 - else: - self.max = _max * 0.9 + _min = min(ndata) + if _min < 0: + self.min = _min * 1.1 + else: + self.min = _min * 0.9 - self.sets = self.build(ndata) + _max = max(ndata) + if _max > 0: + self.max = _max * 1.1 + else: + self.max = _max * 0.9 - del(ndata) + self.sets = self.build(ndata) + + del(ndata) def build(self, data): """ @@ -74,16 +77,25 @@ class Partitioner(object): ticks = [] x = [] for s in self.sets: - if s.mf == Membership.trimf: - ax.plot([s.parameters[0], s.parameters[1], s.parameters[2]], [0, 1, 0]) - elif s.mf == Membership.gaussmf: - tmpx = [kk for kk in np.arange(s.lower, s.upper)] - tmpy = [s.membership(kk) for kk in np.arange(s.lower, s.upper)] - ax.plot(tmpx, tmpy) + if s.type == 'common': + self.plot_set(ax, s) + elif s.type == 'composite': + for ss in s.sets: + self.plot_set(ax, ss) ticks.append(str(round(s.centroid,0))+'\n'+s.name) x.append(s.centroid) plt.xticks(x,ticks) + def plot_set(self, ax, s): + if s.mf == Membership.trimf: + ax.plot([s.parameters[0], s.parameters[1], s.parameters[2]], [0, s.alpha, 0]) + elif s.mf == Membership.gaussmf: + tmpx = [kk for kk in np.arange(s.lower, s.upper)] + tmpy = [s.membership(kk) for kk in np.arange(s.lower, s.upper)] + ax.plot(tmpx, tmpy) + elif s.mf == Membership.trapmf: + ax.plot(s.parameters, [0, s.alpha, s.alpha, 0]) + def __str__(self): tmp = self.name + ":\n" diff --git a/pyFTS/tests/seasonal.py b/pyFTS/tests/seasonal.py new file mode 100644 index 0000000..511e830 --- /dev/null +++ b/pyFTS/tests/seasonal.py @@ -0,0 +1,19 @@ +import matplotlib.pylab as plt +from pyFTS.models.seasonal import partitioner, common +from pyFTS.partitioners import Util +from pyFTS.common import Membership + + +#fs = partitioner.TimeGridPartitioner(None, 12, common.DateTime.day_of_year, func=Membership.trapmf, +# names=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']) + + +#fs = partitioner.TimeGridPartitioner(None, 24, common.DateTime.minute_of_day, func=Membership.trapmf) + +fs = partitioner.TimeGridPartitioner(None, 7, common.DateTime.hour_of_week, func=Membership.trapmf) + + +fig, ax = plt.subplots(nrows=1, ncols=1, figsize=[6, 8]) + +fs.plot(ax) +plt.show()