# Source code for pyFTS.partitioners.partitioner

    from pyFTS.common import FuzzySet, Membership
    import numpy as np
    import matplotlib.pylab as plt

    class Partitioner(object):
        """
        Universe of Discourse partitioner. Split data on several fuzzy sets
        """

        def __init__(self, **kwargs):
            """
            Universe of Discourse partitioner scheme. Split data on several fuzzy sets
            """
            self.name = kwargs.get('name',"")
            """partitioner name"""
            self.partitions = 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.membership_function = kwargs.get('func', Membership.trimf)
            """Fuzzy membership function (pyFTS.common.Membership)"""
            self.setnames = 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')
            """prefix of auto generated partition names"""
            self.transformation = kwargs.get('transformation', None)
            """data transformation to be applied on data"""
            self.indexer = kwargs.get('indexer', None)
            self.variable = kwargs.get('variable', None)
            self.type = kwargs.get('type', 'common')
            self.ordered_sets = None

            if kwargs.get('preprocess',True):

                data = kwargs.get('data',[None])

                if self.indexer is not None:
                    ndata = self.indexer.get_data(data)
                else:
                    ndata = data

                if self.transformation is not None:
                    ndata = self.transformation.apply(ndata)
                else:
                    ndata = data

                if self.indexer is not None:
                    ndata = self.indexer.get_data(ndata)

                _min = np.nanmin(ndata)
                if _min == -np.inf:
                    ndata[ndata == -np.inf] = 0
                    _min = np.nanmin(ndata)

                self.min = float(_min * 1.1 if _min < 0 else _min * 0.9)

                _max = np.nanmax(ndata)
                self.max = float(_max * 1.1 if _max > 0 else _max * 0.9)

                self.sets = self.build(ndata)

                if self.ordered_sets is None and self.setnames is not None:
                    self.ordered_sets = self.setnames
                else:
                    self.ordered_sets = FuzzySet.set_ordered(self.sets)

                del(ndata)

        def build(self, data):
            """
            Perform the partitioning of the Universe of Discourse

            :param data:  training data
            :return:
            """
            pass

        def get_name(self, counter):
            """
            Find the name of the fuzzy set given its counter id.

            :param counter: The number of the fuzzy set
            :return: String
            """
            return self.prefix + str(counter) if self.setnames is None else self.setnames[counter]

        def lower_set(self):
            """
            Return the fuzzy set on lower bound of the universe of discourse.

            :return: Fuzzy Set
            """
            return self.sets[self.ordered_sets[0]]

        def upper_set(self):
            """
            Return the fuzzy set on upper bound of the universe of discourse.

            :return: Fuzzy Set
            """
            return self.sets[self.ordered_sets[-1]]

        def plot(self, ax):
            """
            Plot the partitioning using the Matplotlib axis ax

            :param ax: Matplotlib axis
            """
            ax.set_title(self.name)
            ax.set_ylim([0, 1])
            ax.set_xlim([self.min, self.max])
            ticks = []
            x = []
            for key in self.sets.keys():
                s = self.sets[key]
                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)
            ax.xaxis.set_ticklabels(ticks)
            ax.xaxis.set_ticks(x)

        def plot_set(self, ax, s):
            """
            Plot an isolate fuzzy set on Matplotlib axis

            :param ax: Matplotlib axis
            :param s: Fuzzy Set
            """
            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):
            """
            Return a string representation of the partitioner, the list of fuzzy sets and their parameters

            :return:
            """
            tmp = self.name + ":\n"
            for key in self.sets.keys():
                tmp += str(self.sets[key])+ "\n"
            return tmp

        def __len__(self):
            """
            Return the number of partitions

            :return: number of partitions
            """
            return self.partitions