diff --git a/Demos/demos_module/main_iris_demo.py b/Demos/demos_module/main_iris_demo.py index 6cd3cc1..5110f92 100644 --- a/Demos/demos_module/main_iris_demo.py +++ b/Demos/demos_module/main_iris_demo.py @@ -54,6 +54,7 @@ # Compute the fuzzy partitions using n linguistic variables precomputed_partitions_vl = utils.construct_partitions(X, fz_type_studied, n_partitions=vl) + # Split the data into a training set and a test set X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=0) diff --git a/ex_fuzzy/ex_fuzzy/fuzzy_sets.py b/ex_fuzzy/ex_fuzzy/fuzzy_sets.py index 3db6ac7..2be86d6 100644 --- a/ex_fuzzy/ex_fuzzy/fuzzy_sets.py +++ b/ex_fuzzy/ex_fuzzy/fuzzy_sets.py @@ -77,7 +77,7 @@ def trapezoidal_membership(x: np.array, params: list[float], epsilon=10E-5) -> n -def __gaussian2(x, params: list[float]) -> np.array: +def _gaussian2(x, params: list[float]) -> np.array: ''' Gaussian membership functions. @@ -85,8 +85,8 @@ def __gaussian2(x, params: list[float]) -> np.array: :param amplitude: real number. :param standard_deviation: std of the gaussian function. ''' - mean, amplitude, standard_deviation = params - return amplitude * np.exp(- ((x - mean) / standard_deviation) ** 2) + mean, standard_deviation = params + return np.exp(- ((x - mean) / standard_deviation) ** 2) class FS(): @@ -146,6 +146,15 @@ def __str__(self) -> str: :return: string. ''' return f'{self.name} ({self.type().name}) - {self.membership_parameters}' + + + def shape(self) -> str: + ''' + Returns the shape of the fuzzy set. + + :return: string. + ''' + return 'trapezoid' @@ -201,10 +210,20 @@ def __str__(self) -> str: :return: string. ''' return f'Categorical set: {self.name}, type 1 output' + + + def shape(self) -> str: + ''' + Returns the shape of the fuzzy set. + + :return: string. + ''' + return 'categorical' class IVFS(FS): ''' + Class to define a iv fuzzy set. ''' @@ -334,6 +353,14 @@ def __str__(self) -> str: return f'Categorical set: {self.name}, type 2 output' + def shape(self) -> str: + ''' + Returns the shape of the fuzzy set. + + :return: string. + ''' + return 'categorical' + class GT2(FS): ''' @@ -472,8 +499,8 @@ def membership(self, input: np.array) -> np.array: :param input: input values in the fuzzy set referencial domain. :return: np array samples x 2 ''' - lower = __gaussian2(input, self.secondMF_lower) - upper = __gaussian2(input, self.secondMF_upper) + lower = _gaussian2(input, self.secondMF_lower) + upper = _gaussian2(input, self.secondMF_upper) return np.array(np.concatenate([lower, upper])).T @@ -483,6 +510,15 @@ def type(self) -> FUZZY_SETS: Returns the type of the fuzzy set. (t1) ''' return FUZZY_SETS.t2 + + + def shape(self) -> str: + ''' + Returns the shape of the fuzzy set. + + :return: string. + ''' + return 'gaussian' class gaussianFS(FS): @@ -497,7 +533,7 @@ def membership(self, input: np.array) -> np.array: :param input: input values in the fuzzy set referencial domain. :return: np array samples ''' - return __gaussian2(input, self.membership_parameters) + return _gaussian2(input, self.membership_parameters) def type(self) -> FUZZY_SETS: @@ -505,6 +541,15 @@ def type(self) -> FUZZY_SETS: Returns the type of the fuzzy set. (t1) ''' return FUZZY_SETS.t1 + + + def shape(self) -> str: + ''' + Returns the shape of the fuzzy set. + + :return: string. + ''' + return 'gaussian' class fuzzyVariable(): diff --git a/ex_fuzzy/ex_fuzzy/utils.py b/ex_fuzzy/ex_fuzzy/utils.py index 7d3f8e1..8ac73e5 100644 --- a/ex_fuzzy/ex_fuzzy/utils.py +++ b/ex_fuzzy/ex_fuzzy/utils.py @@ -193,13 +193,13 @@ def t1_n_gaussian_partition_parameters(x: np.array, n_partitions: int) -> np.arr for partition in range(n_partitions): if partition == 0: # First partition # Center at first interior quantile - partition_parameters[:, partition, 0] = quantile_numbers[1, :] # mean + partition_parameters[:, partition, 0] = quantile_numbers[0, :] # mean # Spread based on distance to next quantile partition_parameters[:, partition, 1] = (quantile_numbers[2, :] - quantile_numbers[0, :]) / 2 # std elif partition == n_partitions - 1: # Last partition # Center at last interior quantile - partition_parameters[:, partition, 0] = quantile_numbers[-2, :] # mean + partition_parameters[:, partition, 0] = quantile_numbers[-1, :] # mean # Spread based on distance to previous quantile partition_parameters[:, partition, 1] = (quantile_numbers[-1, :] - quantile_numbers[-3, :]) / 2 # std @@ -390,14 +390,19 @@ def t1_fuzzy_partitions_dataset(x0: np.array, n_partition=3, shape='trapezoid') elif shape == 'gaussian': fz_memberships = t1_n_gaussian_partition_parameters(x, n_partitions=n_partition) else: - raise ValueError('Shape not recognized') + raise ValueError('Shape not recognized, it must be either trapezoid or gaussian') res = [] for fz_parameter in range(fz_memberships.shape[0]): - fzs = [fs.FS(partition_names[ix], fz_memberships[fz_parameter, ix, :], [ - mins[fz_parameter], maxs[fz_parameter]]) for ix in range(fz_memberships.shape[1])] + if shape == 'trapezoid': + fzs = [fs.FS(partition_names[ix], fz_memberships[fz_parameter, ix, :], [ + mins[fz_parameter], maxs[fz_parameter]]) for ix in range(fz_memberships.shape[1])] + elif shape == 'gaussian': + fzs = [fs.gaussianFS(partition_names[ix], fz_memberships[fz_parameter, ix, :], [ + mins[fz_parameter], maxs[fz_parameter]]) for ix in range(fz_memberships.shape[1])] res.append(fs.fuzzyVariable(fv_names[fz_parameter], fzs)) + return res @@ -479,7 +484,7 @@ def gt2_fuzzy_partitions_dataset(x0: np.array, resolution_exp:int=2, n_partition return res -def construct_partitions(X : np.array, fz_type_studied:fs.FUZZY_SETS=fs.FUZZY_SETS.t1, categorical_mask: np.array=None, n_partitions=3) -> list[fs.fuzzyVariable]: +def construct_partitions(X : np.array, fz_type_studied:fs.FUZZY_SETS=fs.FUZZY_SETS.t1, categorical_mask: np.array=None, n_partitions=3, shape='trapezoid') -> list[fs.fuzzyVariable]: ''' Returns a list of linguistic variables according to the kind of fuzzy specified. @@ -506,12 +511,13 @@ def construct_partitions(X : np.array, fz_type_studied:fs.FUZZY_SETS=fs.FUZZY_SE if categorical_mask is None or sum(np.logical_not(categorical_mask)) > 0: if fz_type_studied == fs.FUZZY_SETS.t1: - precomputed_partitions = t1_fuzzy_partitions_dataset(X_numerical, n_partitions) + precomputed_partitions = t1_fuzzy_partitions_dataset(X_numerical, n_partitions, shape) elif fz_type_studied == fs.FUZZY_SETS.t2: - precomputed_partitions = t2_fuzzy_partitions_dataset(X_numerical, n_partitions) + precomputed_partitions = t2_fuzzy_partitions_dataset(X_numerical, n_partitions, shape) elif fz_type_studied == fs.FUZZY_SETS.gt2: - precomputed_partitions = gt2_fuzzy_partitions_dataset(X_numerical, n_partitions) + precomputed_partitions = gt2_fuzzy_partitions_dataset(X_numerical, n_partitions, shape) else: + raise ValueError('Fuzzy set type not recognized') diff --git a/ex_fuzzy/ex_fuzzy/vis_rules.py b/ex_fuzzy/ex_fuzzy/vis_rules.py index 5355bbf..6686ec3 100644 --- a/ex_fuzzy/ex_fuzzy/vis_rules.py +++ b/ex_fuzzy/ex_fuzzy/vis_rules.py @@ -245,19 +245,33 @@ def plot_fuzzy_variable(fuzzy_variable: fs.fuzzyVariable) -> None: fig = plt.figure() ax = plt.axes(projection='3d') - memberships = [0, 1, 1, 0] + unit_resolution = 0.01 + unit_range_sampled = np.arange( + 0, 1 + unit_resolution, unit_resolution) colors = ['b', 'r', 'g', 'orange', 'purple'] for ix, fuzzy_set in enumerate(fuzzy_variable.linguistic_variables): name = fuzzy_set.name initiated = False fz_studied = fuzzy_set.type() + domain_sampled = unit_range_sampled * (fuzzy_set.domain[1] - fuzzy_set.domain[0]) + fuzzy_set.domain[0] + if fuzzy_set.shape() == 'gaussian': + memberships = fuzzy_set.membership(domain_sampled) + else: + memberships = [0, 1, 1, 0] + if fz_studied == fs.FUZZY_SETS.t1: try: - ax.plot(fuzzy_set.membership_parameters, - memberships, colors[ix % len(colors)], label=name) - ax.fill_between(fuzzy_set.membership_parameters, memberships, alpha=0.3) + if fuzzy_set.shape() == 'gaussian': + ax.plot(unit_range_sampled, + memberships, colors[ix % len(colors)], label=name) + ax.fill_between(unit_range_sampled, memberships, alpha=0.3) + elif fuzzy_set.shape() == 'trapezoid': + ax.plot(fuzzy_set.membership_parameters, + memberships, colors[ix % len(colors)], label=name) + ax.fill_between(fuzzy_set.membership_parameters, memberships, alpha=0.3) + except AttributeError: print('Error in the visualization of the fuzzy set: "' + name + '", probably because the fuzzy set is not a trapezoidal fuzzy set.')