diff --git a/Demos/demos_module/iris_demo.py b/Demos/demos_module/main_iris_demo.py similarity index 65% rename from Demos/demos_module/iris_demo.py rename to Demos/demos_module/main_iris_demo.py index 1b517bf..6cd3cc1 100644 --- a/Demos/demos_module/iris_demo.py +++ b/Demos/demos_module/main_iris_demo.py @@ -3,13 +3,8 @@ All rights reserved @author: Javier Fumanal Idocin - University of Essex -@author: Javier Andreu-Perez - University of Essex - -This is a the source file that contains a demo for a tip computation example, where a diferent set of T1-FS are used to compute -a t1 reasoning approach. - -We also show the GA to optimize the rules obtained in classification. +This is a the source file that contains a demo for an Iris classification example using a FRBC where we will also show different possible ways to print the rules, precompute the partitions, etc. """ @@ -22,14 +17,10 @@ import sys - - # In case you run this without installing the package, you need to add the path to the package - # This is for launching from root folder path sys.path.append('./ex_fuzzy/') sys.path.append('./ex_fuzzy/ex_fuzzy/') - # This is for launching from Demos folder sys.path.append('../ex_fuzzy/') sys.path.append('../ex_fuzzy/ex_fuzzy/') @@ -43,45 +34,47 @@ runner = 1 # 1: single thread, 2+: corresponding multi-thread +# GA parameters n_gen = 5 n_pop = 30 - -nRules = 15 -nAnts = 3 -tolerance = 0.001 + +# FRBC parameters +nRules = 15 # Number of maximum rules +nAnts = 3 # Number of maximum antecedents per rule +vl = 3 # Number of linguistic variables +tolerance = 0.001 # Minimum dominance score to accept a rule +fz_type_studied = fs.FUZZY_SETS.t1 # Fuzzy set type + iris = datasets.load_iris() X = pd.DataFrame(iris.data, columns=iris.feature_names) y = pd.Series(iris.target) class_names = iris.target_names -class_names = np.unique(y) -fz_type_studied = fs.FUZZY_SETS.t1 -vl = 3 - - # 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) - -# We create a FRBC with the precomputed partitions and the specified fuzzy set type, +# We create a FRBC with the precomputed partitions or None (will optimize them as well in that case) and the specified fuzzy set type fl_classifier = GA.BaseFuzzyRulesClassifier(nRules=nRules, linguistic_variables=None, nAnts=nAnts, class_names=class_names, n_linguistic_variables=vl, fuzzy_type=fz_type_studied, verbose=True, tolerance=tolerance, runner=runner, allow_unknown=True, ds_mode=0) # fl_classifier.customized_loss(utils.mcc_loss) Use this to change the loss function, but be sure to look at the API first fl_classifier.fit(X_train, y_train, n_gen=n_gen, pop_size=n_pop, checkpoints=0, random_state=0, p_value_compute=True) -# print(vis_rules.rules_to_latex(fl_classifier.rule_base)) +# We evaluate the fuzzy model, this will print the rules, the accuracy, the Matthew's correlation coefficient, etc. fuzzy_evaluator = eval_tools.FuzzyEvaluator(fl_classifier) str_rules = fuzzy_evaluator.eval_fuzzy_model(X_train, y_train, X_test, y_test, - plot_rules=False, print_rules=True, plot_partitions=False, return_rules=True) -rule_matrix = fl_classifier.rule_base.get_rulebase_matrix() + plot_rules=False, print_rules=True, plot_partitions=False, return_rules=True, bootstrap_results_print=True) + +# print(vis_rules.rules_to_latex(fl_classifier.rule_base)) # Do this to print the rules in latex format # Save the rules as a plain text file -with open('rules_iris_t2.txt', 'w') as f: - f.write(str_rules) +#with open('rules_iris_t2.txt', 'w') as f: +# f.write(str_rules) -# Compute the explainable predictions +# Compute the explainable predictions: this will return the predictions, the winning rules, the winning association degrees, and the confidence interval of the certainty of the predictions y_pred, winning_rules, winning_association_degrees, conf_interval_certainty = fl_classifier.explainable_predict(X_test, out_class_names=True) -print('Done') \ No newline at end of file + +print('Done!') \ No newline at end of file diff --git a/docs/build/doctrees/api.doctree b/docs/build/doctrees/api.doctree index c56fd1f..c49ac03 100644 Binary files a/docs/build/doctrees/api.doctree and b/docs/build/doctrees/api.doctree differ diff --git a/docs/build/doctrees/classifiers.doctree b/docs/build/doctrees/classifiers.doctree index c6ebc3d..9df2d85 100644 Binary files a/docs/build/doctrees/classifiers.doctree and b/docs/build/doctrees/classifiers.doctree differ diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle index d489037..4eec1af 100644 Binary files a/docs/build/doctrees/environment.pickle and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/extending.doctree b/docs/build/doctrees/extending.doctree index 7599337..c3353c1 100644 Binary files a/docs/build/doctrees/extending.doctree and b/docs/build/doctrees/extending.doctree differ diff --git a/docs/build/doctrees/function_resume/centroid.doctree b/docs/build/doctrees/function_resume/centroid.doctree index 3e37d09..b08a680 100644 Binary files a/docs/build/doctrees/function_resume/centroid.doctree and b/docs/build/doctrees/function_resume/centroid.doctree differ diff --git a/docs/build/doctrees/function_resume/classifiers.doctree b/docs/build/doctrees/function_resume/classifiers.doctree index f58641a..81fc3e1 100644 Binary files a/docs/build/doctrees/function_resume/classifiers.doctree and b/docs/build/doctrees/function_resume/classifiers.doctree differ diff --git a/docs/build/doctrees/function_resume/cognitive_maps.doctree b/docs/build/doctrees/function_resume/cognitive_maps.doctree index df2f6dd..913bb9c 100644 Binary files a/docs/build/doctrees/function_resume/cognitive_maps.doctree and b/docs/build/doctrees/function_resume/cognitive_maps.doctree differ diff --git a/docs/build/doctrees/function_resume/eval_rules.doctree b/docs/build/doctrees/function_resume/eval_rules.doctree index b074a3e..e4be11b 100644 Binary files a/docs/build/doctrees/function_resume/eval_rules.doctree and b/docs/build/doctrees/function_resume/eval_rules.doctree differ diff --git a/docs/build/doctrees/function_resume/eval_tools.doctree b/docs/build/doctrees/function_resume/eval_tools.doctree index e40a622..6efbe96 100644 Binary files a/docs/build/doctrees/function_resume/eval_tools.doctree and b/docs/build/doctrees/function_resume/eval_tools.doctree differ diff --git a/docs/build/doctrees/function_resume/evolutionary_fit.doctree b/docs/build/doctrees/function_resume/evolutionary_fit.doctree index efd9413..a1f4a8b 100644 Binary files a/docs/build/doctrees/function_resume/evolutionary_fit.doctree and b/docs/build/doctrees/function_resume/evolutionary_fit.doctree differ diff --git a/docs/build/doctrees/function_resume/fuzzy_sets.doctree b/docs/build/doctrees/function_resume/fuzzy_sets.doctree index e3a0aa5..8abcf19 100644 Binary files a/docs/build/doctrees/function_resume/fuzzy_sets.doctree and b/docs/build/doctrees/function_resume/fuzzy_sets.doctree differ diff --git a/docs/build/doctrees/function_resume/pattern_stability.doctree b/docs/build/doctrees/function_resume/pattern_stability.doctree index 0af7e4b..07f432a 100644 Binary files a/docs/build/doctrees/function_resume/pattern_stability.doctree and b/docs/build/doctrees/function_resume/pattern_stability.doctree differ diff --git a/docs/build/doctrees/function_resume/persistence.doctree b/docs/build/doctrees/function_resume/persistence.doctree index d4ef6c3..f84b346 100644 Binary files a/docs/build/doctrees/function_resume/persistence.doctree and b/docs/build/doctrees/function_resume/persistence.doctree differ diff --git a/docs/build/doctrees/function_resume/rule_mining.doctree b/docs/build/doctrees/function_resume/rule_mining.doctree index d390b48..a4fcb0b 100644 Binary files a/docs/build/doctrees/function_resume/rule_mining.doctree and b/docs/build/doctrees/function_resume/rule_mining.doctree differ diff --git a/docs/build/doctrees/function_resume/rules.doctree b/docs/build/doctrees/function_resume/rules.doctree index 6bd0f1b..5339e85 100644 Binary files a/docs/build/doctrees/function_resume/rules.doctree and b/docs/build/doctrees/function_resume/rules.doctree differ diff --git a/docs/build/doctrees/function_resume/temporal.doctree b/docs/build/doctrees/function_resume/temporal.doctree index a53898d..284142c 100644 Binary files a/docs/build/doctrees/function_resume/temporal.doctree and b/docs/build/doctrees/function_resume/temporal.doctree differ diff --git a/docs/build/doctrees/function_resume/utils.doctree b/docs/build/doctrees/function_resume/utils.doctree index 752fa53..275cf1c 100644 Binary files a/docs/build/doctrees/function_resume/utils.doctree and b/docs/build/doctrees/function_resume/utils.doctree differ diff --git a/docs/build/doctrees/function_resume/vis_rules.doctree b/docs/build/doctrees/function_resume/vis_rules.doctree index 76d41e4..ea008f3 100644 Binary files a/docs/build/doctrees/function_resume/vis_rules.doctree and b/docs/build/doctrees/function_resume/vis_rules.doctree differ diff --git a/docs/build/doctrees/gt2.doctree b/docs/build/doctrees/gt2.doctree index 2d0a70a..dc8de51 100644 Binary files a/docs/build/doctrees/gt2.doctree and b/docs/build/doctrees/gt2.doctree differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree index 58a3617..00993b0 100644 Binary files a/docs/build/doctrees/index.doctree and b/docs/build/doctrees/index.doctree differ diff --git a/docs/build/doctrees/optimize.doctree b/docs/build/doctrees/optimize.doctree index 03cbf2a..21fc398 100644 Binary files a/docs/build/doctrees/optimize.doctree and b/docs/build/doctrees/optimize.doctree differ diff --git a/docs/build/doctrees/pattern_stats.doctree b/docs/build/doctrees/pattern_stats.doctree index 4593773..a101f16 100644 Binary files a/docs/build/doctrees/pattern_stats.doctree and b/docs/build/doctrees/pattern_stats.doctree differ diff --git a/docs/build/doctrees/persistence.doctree b/docs/build/doctrees/persistence.doctree index acc75ba..294a920 100644 Binary files a/docs/build/doctrees/persistence.doctree and b/docs/build/doctrees/persistence.doctree differ diff --git a/docs/build/doctrees/precom.doctree b/docs/build/doctrees/precom.doctree index a4e8981..f023319 100644 Binary files a/docs/build/doctrees/precom.doctree and b/docs/build/doctrees/precom.doctree differ diff --git a/docs/build/doctrees/step1.doctree b/docs/build/doctrees/step1.doctree index 3a93437..014acf2 100644 Binary files a/docs/build/doctrees/step1.doctree and b/docs/build/doctrees/step1.doctree differ diff --git a/docs/build/doctrees/step2.doctree b/docs/build/doctrees/step2.doctree index 8819bea..a37deda 100644 Binary files a/docs/build/doctrees/step2.doctree and b/docs/build/doctrees/step2.doctree differ diff --git a/docs/build/doctrees/step3.doctree b/docs/build/doctrees/step3.doctree index 2028dbd..64f0cee 100644 Binary files a/docs/build/doctrees/step3.doctree and b/docs/build/doctrees/step3.doctree differ diff --git a/docs/build/doctrees/step4.doctree b/docs/build/doctrees/step4.doctree index c4f95a0..39c7046 100644 Binary files a/docs/build/doctrees/step4.doctree and b/docs/build/doctrees/step4.doctree differ diff --git a/docs/build/doctrees/tmpfs.doctree b/docs/build/doctrees/tmpfs.doctree index 1ecf24a..2bf159a 100644 Binary files a/docs/build/doctrees/tmpfs.doctree and b/docs/build/doctrees/tmpfs.doctree differ diff --git a/docs/build/doctrees/usage.doctree b/docs/build/doctrees/usage.doctree index 0e72232..463115b 100644 Binary files a/docs/build/doctrees/usage.doctree and b/docs/build/doctrees/usage.doctree differ diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo index b99a06f..1ea0ddd 100644 --- a/docs/build/html/.buildinfo +++ b/docs/build/html/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: bbbf35d0940a0dcfa2922ad1d1125950 +config: 7184ad333879fb5e4f199b3de50a2caf tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/_modules/ex_fuzzy/classifiers.html b/docs/build/html/_modules/ex_fuzzy/classifiers.html index 2452a23..9734e1f 100644 --- a/docs/build/html/_modules/ex_fuzzy/classifiers.html +++ b/docs/build/html/_modules/ex_fuzzy/classifiers.html @@ -54,6 +54,7 @@
try:
from . import rules
from . import fuzzy_sets as fs
- from . import permutation_test as bt
+ from . import permutation_test as pt
+ from . import bootstrapping_test as bt
except ImportError:
import rules
import fuzzy_sets as fs
- import permutation_test as bt
+ import permutation_test as pt
+ import bootstrapping_test as bt
-
-[docs]
-class evalRuleBase():
+[docs]class evalRuleBase():
'''
Class to evaluate a set of rules given a evaluation dataset.
'''
@@ -120,18 +122,16 @@ Source code for ex_fuzzy.eval_rules
self.y = y
self.time_moments = time_moments
- self.consequents = mrule_base.get_consequents()
- self.consequents_names = mrule_base.get_consequents_names()
self.precomputed_truth = precomputed_truth
if isinstance(y[0], str):
- self.y = np.array([list(self.consequents_names).index(str(y)) for y in y])
+ consequents_names = self.mrule_base.get_consequents_names()
+ self.y = np.array([list(consequents_names).index(str(y)) for y in y])
-
-[docs]
- def compute_pattern_support(self) -> np.array:
+
+[docs] def compute_antecedent_pattern_support(self, X: np.array=None) -> np.array:
'''
Computes the pattern support for each of the rules for the given X.
Each pattern support firing strength is the result of the tnorm for all the antecedent memeberships,
@@ -139,12 +139,16 @@ Source code for ex_fuzzy.eval_rules
:return: array of shape rules x 2
'''
+ data_X = X if X is not None else self.X
+ precomputed_truth = self.precomputed_truth if self.precomputed_truth is not None else None
+
if self.time_moments is None:
antecedent_memberships = self.mrule_base.compute_firing_strenghts(
- self.X, precomputed_truth=self.precomputed_truth)
+ data_X, precomputed_truth=precomputed_truth)
else:
antecedent_memberships = self.mrule_base.compute_firing_strenghts(
- self.X, self.time_moments)
+ data_X, self.time_moments)
+
patterns = self._get_all_rules()
@@ -156,20 +160,60 @@ Source code for ex_fuzzy.eval_rules
res = np.zeros((len(patterns), 2))
for ix, pattern in enumerate(patterns):
- consequent_match = np.equal(self.y , self.consequents[ix])
pattern_firing_strength = antecedent_memberships[:, ix]
+ res[ix] = np.mean(pattern_firing_strength)
+
+ if self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.t2 or self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.gt2:
+ res = np.mean(res, axis=1)
+
+ return res
+
+[docs] def compute_pattern_support(self, X: np.array=None, y: np.array=None) -> np.array:
+
+ '''
+ Computes the pattern support for each of the rules for the given X.
+ Each pattern support firing strength is the result of the tnorm for all the antecedent memeberships,
+ dvided by their number.
+
+ :return: array of shape rules x 2
+ '''
+ data_X = X if X is not None else self.X
+ data_y = y if y is not None else self.y
+ precomputed_truth = self.precomputed_truth if self.precomputed_truth is not None else None
+
+ if self.time_moments is None:
+ antecedent_memberships = self.mrule_base.compute_firing_strenghts(
+ data_X, precomputed_truth=precomputed_truth)
+ else:
+ antecedent_memberships = self.mrule_base.compute_firing_strenghts(
+ data_X, self.time_moments)
+
+
+ patterns = self._get_all_rules()
+
+ if self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.t1:
+ res = np.zeros((len(patterns), ))
+ elif self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.t2:
+ res = np.zeros((len(patterns), 2))
+ elif self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.gt2:
+ res = np.zeros((len(patterns), 2))
+ consequents = self.mrule_base.get_consequents()
+ for ix, pattern in enumerate(patterns):
+ consequent_match = np.equal(data_y, consequents[ix])
+ pattern_firing_strength = antecedent_memberships[:, ix]
+
- if pattern_firing_strength[consequent_match].shape[0] > 0:
- res[ix] = np.mean(pattern_firing_strength[consequent_match])
+ res[ix] = np.mean(pattern_firing_strength * consequent_match)
+
+ if self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.t2 or self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.gt2:
+ res = np.mean(res, axis=1)
return res
+[docs] def compute_aux_pattern_support(self) -> np.array:
-
-[docs]
- def compute_aux_pattern_support(self) -> np.array:
'''
Computes the pattern support for each of the rules for each of the classes for the given X.
Each pattern support firing strength is the result of the tnorm for all the antecedent memeberships,
@@ -196,14 +240,14 @@ Source code for ex_fuzzy.eval_rules
for con_ix in range(n_classes):
for ix, pattern in enumerate(patterns):
- consequent_match = self.y == con_ix
+ consequent_match = np.equal(self.y, con_ix)
pattern_firing_strength = antecedent_memberships[:, ix]
- # / pattern_firing_strength.shape[0]
- if pattern_firing_strength[consequent_match].shape[0] > 0:
- res[ix, con_ix] = np.mean(pattern_firing_strength[consequent_match])
- else:
- res[ix, con_ix] = pattern_firing_strength[consequent_match]
+ res[ix, con_ix] = np.mean(pattern_firing_strength * consequent_match)
+
+
+ if self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.t2 or self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.gt2:
+ res = np.mean(res, axis=1)
return res
@@ -222,21 +266,24 @@ Source code for ex_fuzzy.eval_rules
return res
-
-[docs]
- def compute_pattern_confidence(self) -> np.array:
+[docs] def compute_pattern_confidence(self, X: np.array=None, y: np.array=None, precomputed_truth=None) -> np.array:
'''
Computes the pattern confidence for each of the rules for the given X.
Each pattern confidence is the normalized firing strength.
:returns: array of shape 1 x rules
'''
+ data_X = X if X is not None else self.X
+ data_y = y if y is not None else self.y
+ precomputed_truth = self.precomputed_truth if self.precomputed_truth is not None else None
+
if self.time_moments is None:
antecedent_memberships = self.mrule_base.compute_firing_strenghts(
- self.X, precomputed_truth=self.precomputed_truth)
+ data_X, precomputed_truth=precomputed_truth)
else:
antecedent_memberships = self.mrule_base.compute_firing_strenghts(
- self.X, self.time_moments, precomputed_truth=self.precomputed_truth)
+ data_X, self.time_moments, precomputed_truth=precomputed_truth)
+
patterns = self._get_all_rules()
@@ -246,24 +293,28 @@ Source code for ex_fuzzy.eval_rules
res = np.zeros((len(patterns), 2))
elif self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.gt2:
res = np.zeros((len(patterns), 2))
-
+ consequents = self.mrule_base.get_consequents()
for ix, pattern in enumerate(patterns):
- antecedent_consequent_match = np.equal(self.y, self.consequents[ix])
+ antecedent_consequent_match = np.equal(data_y, consequents[ix])
pattern_firing_strength = antecedent_memberships[:, ix]
dem = np.sum(pattern_firing_strength)
if dem == 0:
+
+
res[ix] = 0
else:
res[ix] = np.sum(
pattern_firing_strength[antecedent_consequent_match]) / dem
+ if self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.t2 or self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.gt2:
+ res = np.mean(res, axis=1)
+
return res
-
-[docs]
- def compute_aux_pattern_confidence(self) -> np.array:
+[docs] def compute_aux_pattern_confidence(self) -> np.array:
+
'''
Computes the pattern confidence for each of the rules for the given X.
Each pattern confidence is the normalized firing strength.
@@ -296,14 +347,15 @@ Source code for ex_fuzzy.eval_rules
else:
res[ix, consequent] = np.sum(
pattern_firing_strength[antecedent_consequent_match]) / dem
+
+ if self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.t2 or self.mrule_base.fuzzy_type() == fs.FUZZY_SETS.gt2:
+ res = np.mean(res, axis=1)
return res
-
-[docs]
- def dominance_scores(self) -> np.array:
+[docs] def dominance_scores(self) -> np.array:
'''
Returns the dominance score of each pattern for each rule.
@@ -312,10 +364,7 @@ Source code for ex_fuzzy.eval_rules
return self.compute_pattern_confidence() * self.compute_pattern_support()
-
-
-[docs]
- def association_degree(self) -> np.array:
+[docs] def association_degree(self) -> np.array:
'''
Returns the association degree of each rule for each sample.
@@ -330,10 +379,7 @@ Source code for ex_fuzzy.eval_rules
return res
-
-
-[docs]
- def aux_dominance_scores(self) -> np.array:
+[docs] def aux_dominance_scores(self) -> np.array:
'''
Returns the dominance score of each pattern for each rule.
@@ -342,10 +388,7 @@ Source code for ex_fuzzy.eval_rules
return self.compute_aux_pattern_confidence() * self.compute_aux_pattern_support()
-
-
-[docs]
- def add_rule_weights(self) -> None:
+[docs] def add_rule_weights(self) -> None:
'''
Add dominance score field to each of the rules present in the master Rule Base.
'''
@@ -361,12 +404,9 @@ Source code for ex_fuzzy.eval_rules
rules[jx].confidence = confidences[aux_counter]
aux_counter += 1
-
-
-[docs]
- def add_auxiliary_rule_weights(self) -> None:
+[docs] def add_auxiliary_rule_weights(self) -> None:
'''
Add dominance score field to each of the rules present in the master Rule Base for each consequent.
They are labeled as aux_score, aux_support and aux_confidence. (Because they are not the main rule weights)
@@ -385,10 +425,7 @@ Source code for ex_fuzzy.eval_rules
aux_counter += 1
-
-
-[docs]
- def add_classification_metrics(self, X: np.array=None, y: np.array=None) -> None:
+[docs] def add_classification_metrics(self, X: np.array=None, y: np.array=None) -> None:
'''
Adds the accuracy of each rule in the master rule base. It also adds the f1, precision and recall scores.
If X and y are None uses the train set.
@@ -407,23 +444,28 @@ Source code for ex_fuzzy.eval_rules
if isinstance(actual_y, list):
actual_y = np.array(actual_y)
- if not hasattr(self.mrule_base.get_rules()[0], 'score'):
+ if len(self.mrule_base.get_rules()) > 0 and not hasattr(self.mrule_base.get_rules()[0], 'score'):
self.add_rule_weights()
+
if self.time_moments is None:
- winning_rules = self.mrule_base._winning_rules(actual_X, precomputed_truth=self.precomputed_truth, allow_unkown=self.mrule_base.allow_unknown)
+ winning_rules, _winning_association_degrees = self.mrule_base._winning_rules(actual_X, precomputed_truth=self.precomputed_truth, allow_unkown=self.mrule_base.allow_unknown)
preds = self.mrule_base.winning_rule_predict(actual_X, precomputed_truth=self.precomputed_truth)
else:
- winning_rules = self.mrule_base._winning_rules(actual_X, self.time_moments)
+
+ winning_rules, _winning_association_degrees = self.mrule_base._winning_rules(actual_X, self.time_moments)
preds = self.mrule_base.winning_rule_predict(actual_X, self.time_moments)
+
# If preds and labels are not instances of the same type, we convert them to the same type
+ consequents_names = self.mrule_base.get_consequents_names()
if type(preds[0]) != type(actual_y[0]):
if isinstance(actual_y[0], str):
- preds = np.array([self.consequents_names[p].index(str(p)) for p in preds])
+ preds = np.array([consequents_names[p].index(str(p)) for p in preds])
elif isinstance(preds[0], str):
- preds = np.array([list(self.consequents_names).index(str(p)) for p in preds])
+ preds = np.array([consequents_names.index(str(p)) for p in preds])
+
rules = self.mrule_base.get_rules()
for jx in range(len(rules)):
relevant_samples = winning_rules == jx
@@ -434,12 +476,9 @@ Source code for ex_fuzzy.eval_rules
rules[jx].accuracy = accuracy_score(relevant_labels, relevant_preds)
else:
rules[jx].accuracy = 0.0
-
-
-[docs]
- def classification_eval(self) -> float:
+[docs] def classification_eval(self) -> float:
'''
Returns the matthews correlation coefficient for a classification task using
the rules evaluated.
@@ -456,10 +495,7 @@ Source code for ex_fuzzy.eval_rules
return self.mcc
-
-
-[docs]
- def size_antecedents_eval(self, tolerance=0.1) -> float:
+[docs] def size_antecedents_eval(self, tolerance=0.1) -> float:
'''
Returns a score between 0 and 1, where 1 means that the rule base only contains almost no antecedents.
@@ -494,12 +530,9 @@ Source code for ex_fuzzy.eval_rules
rule_density = 0.0
return rule_density
-
-
-[docs]
- def effective_rulesize_eval(self, tolerance=0.1) -> float:
+[docs] def effective_rulesize_eval(self, tolerance=0.1) -> float:
'''
Returns a score between 0 and 1, where 1 means that the rule base only contains almost no antecedents.
@@ -532,10 +565,7 @@ Source code for ex_fuzzy.eval_rules
return rule_density
-
-
-[docs]
- def p_permutation_classifier_validation(self, n=100, r=10) -> float:
+[docs] def p_permutation_classifier_validation(self, n=100, r=10) -> float:
'''
Performs a boostrap test to evaluate the performance of the rule base.
Returns the p-valuefor the label permutation test and the feature coalition test.
@@ -544,40 +574,87 @@ Source code for ex_fuzzy.eval_rules
:param r: int. Number of repetitions to estimate the original error rate.
:return: p-value of the permutation test.
'''
- test1 = bt.permutation_labels_test(self.mrule_base, self.X, self.y, k=n, r=r)
- test2 = bt.permute_columns_class_test(self.mrule_base, self.X, self.y, k=n, r=r)
+ test1 = pt.permutation_labels_test(self.mrule_base, self.X, self.y, k=n, r=r)
+ test2 = pt.permute_columns_class_test(self.mrule_base, self.X, self.y, k=n, r=r)
- bt.rulewise_label_permutation_test(self.mrule_base, self.X, self.y, k=n, r=r)
- bt.rulewise_column_permutation_test(self.mrule_base, self.X, self.y, k=n, r=r)
+ pt.rulewise_label_permutation_test(self.mrule_base, self.X, self.y, k=n, r=r)
+ pt.rulewise_column_permutation_test(self.mrule_base, self.X, self.y, k=n, r=r)
self.mrule_base.p_value_class_structure = test1
self.mrule_base.p_value_feature_coalition = test2
return test1, test2
-
def p_bootstrapping_rules_validation(self, n=100) -> float:
- import ex_fuzzy.bootstrapping_test as bt
rules_p_values = bt.compute_rule_p_value(self.mrule_base, self.X, self.y, n).flatten()
for jx, rule in enumerate(self.mrule_base.get_rules()):
rule.boot_p_value = rules_p_values[jx]
+
+ confidence_interval = self.bootstrap_confidence_confinterval(self.X, self.y, n)
+ support_interval = self.bootstrap_support_confinterval(self.X, self.y, n)
+ for jx, rule in enumerate(self.mrule_base.get_rules()):
+ rule.boot_confidence_interval = confidence_interval[:, jx]
+ rule.boot_support_interval = support_interval[:, jx]
+
+
+
+[docs] def add_full_evaluation(self):
-
-[docs]
- def add_full_evaluation(self):
'''
Adds classification scores, both Dominance Scores and accuracy metrics, for each individual rule.
'''
self.add_rule_weights()
self.add_classification_metrics()
self.classification_eval()
-
+
-
-
+[docs] def bootstrap_support_rules(self, X: np.array, y: np.array, n_samples: int):
+ '''
+ Bootstraps the support of the rules in the classifier.
+ '''
+ samples = bt.generate_bootstrap_samples(X, y, n_samples)
+ supports = np.zeros((n_samples, len(self.mrule_base.get_rules())))
+
+
+ for i, sample in enumerate(samples):
+ supports[i] = self.compute_pattern_support(sample[0], sample[1])
+
+ return supports
+
+
+[docs] def bootstrap_confidence_rules(self, X: np.array, y: np.array, n_samples: int):
+ '''
+ Bootstraps the confidence of the rules in the classifier.
+ '''
+ samples = bt.generate_bootstrap_samples(X, np.array(y), n_samples)
+ confidences = np.zeros((n_samples, len(self.mrule_base.get_rules())))
+
+ for i, sample in enumerate(samples):
+ confidences[i] = self.compute_pattern_confidence(sample[0], sample[1])
+
+ return confidences
+
+
+[docs] def bootstrap_support_confinterval(self, X: np.array, y: np.array, n_samples: int):
+ '''
+ Bootstraps the support of the rules in the classifier.
+ '''
+ supports = self.bootstrap_support_rules(X, np.array(y), n_samples)
+ conf_interval = np.percentile(supports, [2.5, 97.5], axis=0)
+ return conf_interval
+
+
+
+[docs] def bootstrap_confidence_confinterval(self, X: np.array, y: np.array, n_samples: int):
+ '''
+ Bootstraps the confidence of the rules in the classifier.
+ '''
+ confidences = self.bootstrap_confidence_rules(X, np.array(y), n_samples)
+ conf_interval = np.percentile(confidences, [2.5, 97.5], axis=0)
+ return conf_interval
diff --git a/docs/build/html/_modules/ex_fuzzy/eval_tools.html b/docs/build/html/_modules/ex_fuzzy/eval_tools.html
index 809af04..db3b8e1 100644
--- a/docs/build/html/_modules/ex_fuzzy/eval_tools.html
+++ b/docs/build/html/_modules/ex_fuzzy/eval_tools.html
@@ -1,20 +1,22 @@
-
-
-
+
ex_fuzzy.eval_tools — Ex-Fuzzy documentation
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
@@ -52,7 +54,7 @@
Extending Ex-Fuzzy
Persistence
Advanced classifiers
-Studying patterns
+Bootstrapping and rule robustness
API
@@ -97,9 +99,18 @@ Source code for ex_fuzzy.eval_tools
import vis_rules
-
-[docs]
-class FuzzyEvaluator():
+[docs]def eval_fuzzy_model(fl_classifier: evf.BaseFuzzyRulesClassifier, X_train:np.array, y_train:np.array, X_test:np.array, y_test:np.array, plot_rules=False,print_rules:bool=True, plot_partitions:bool=False, return_rules:bool=True, bootstrap_results_print:bool=True) -> str:
+ '''
+ Take a look at the FuzzyEvaluator class for this function documentation.
+ '''
+ fuzzy_evaluator = FuzzyEvaluator(fl_classifier)
+ res = fuzzy_evaluator.eval_fuzzy_model(X_train, y_train, X_test, y_test,
+ plot_rules=plot_rules, print_rules=print_rules, plot_partitions=plot_partitions, return_rules=return_rules, bootstrap_results_print=bootstrap_results_print)
+
+
+ return res
+
+[docs]class FuzzyEvaluator():
'''
Takes a model and associated data and permits rule evaluation
'''
@@ -116,9 +127,7 @@ Source code for ex_fuzzy.eval_tools
return self.fl_classifier.predict(X)
-
-[docs]
- def get_metric(self,metric:str,X_true:np.array,y_true:np.array,**kwargs) -> float:
+[docs] def get_metric(self,metric:str,X_true:np.array,y_true:np.array,**kwargs) -> float:
'''
:param metric: named metric in string format available in sklearn library for evaluation
:param X_true: np.array of X values for prediction
@@ -144,14 +153,11 @@ Source code for ex_fuzzy.eval_tools
return f"Metric '{metric}' not found in sklearn.metrics."
except TypeError:
return f"Invalid arguments passed for the metric '{metric}'."
-
-
-[docs]
- def eval_fuzzy_model(self,X_train: np.array, y_train: np.array,X_test: np.array, y_test: np.array,
+[docs] def eval_fuzzy_model(self,X_train: np.array, y_train: np.array,X_test: np.array, y_test: np.array,
plot_rules=True, print_rules=True, plot_partitions=True,
- return_rules=False, print_accuracy=True, print_matthew=True, export_path:str=None) -> None:
+ return_rules=False, print_accuracy=True, print_matthew=True, export_path:str=None, bootstrap_results_print:bool=True) -> None:
'''
Function that evaluates a fuzzy rule based model. It also plots the rules and the fuzzy partitions.
@@ -192,7 +198,7 @@ Source code for ex_fuzzy.eval_tools
if print_rules or return_rules:
- res = self.fl_classifier.print_rules(True)
+ res = self.fl_classifier.print_rules(True, bootstrap_results=bootstrap_results_print)
if print_rules:
print(res)
@@ -204,9 +210,7 @@ Source code for ex_fuzzy.eval_tools
vis_rules.visualize_rulebase(self.fl_classifier.rule_base, export_path=export_path)
if return_rules:
- return res
-
-
+ return res
diff --git a/docs/build/html/_modules/ex_fuzzy/evolutionary_fit.html b/docs/build/html/_modules/ex_fuzzy/evolutionary_fit.html
index 03443bd..8b232a1 100644
--- a/docs/build/html/_modules/ex_fuzzy/evolutionary_fit.html
+++ b/docs/build/html/_modules/ex_fuzzy/evolutionary_fit.html
@@ -1,20 +1,22 @@
-
-
-
+
ex_fuzzy.evolutionary_fit — Ex-Fuzzy documentation
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
@@ -52,7 +54,7 @@
Extending Ex-Fuzzy
Persistence
Advanced classifiers
-Studying patterns
+Bootstrapping and rule robustness
API
@@ -94,6 +96,7 @@ Source code for ex_fuzzy.evolutionary_fit
from pymoo.algorithms.soo.nonconvex.ga import GA
from pymoo.core.problem import Problem
from pymoo.optimize import minimize
+from pymoo.operators.repair.rounding import RoundingRepair
from pymoo.operators.sampling.rnd import IntegerRandomSampling
from pymoo.operators.crossover.sbx import SBX
from pymoo.operators.mutation.pm import PolynomialMutation
@@ -116,9 +119,7 @@ Source code for ex_fuzzy.evolutionary_fit
-
-[docs]
-class BaseFuzzyRulesClassifier(ClassifierMixin):
+[docs]class BaseFuzzyRulesClassifier(ClassifierMixin):
'''
Class that is used as a classifier for a fuzzy rule based system. Supports precomputed and optimization of the linguistic variables.
'''
@@ -207,9 +208,7 @@ Source code for ex_fuzzy.evolutionary_fit
self.beta_ = 0.0
-
-[docs]
- def customized_loss(self, loss_function):
+[docs] def customized_loss(self, loss_function):
'''
Function to customize the loss function used for the optimization.
@@ -219,12 +218,10 @@ Source code for ex_fuzzy.evolutionary_fit
self.custom_loss = loss_function
-
-
-[docs]
- def fit(self, X: np.array, y: np.array, n_gen:int=70, pop_size:int=30,
+[docs] def fit(self, X: np.array, y: np.array, n_gen:int=70, pop_size:int=30,
checkpoints:int=0, candidate_rules:rules.MasterRuleBase=None, initial_rules:rules.MasterRuleBase=None, random_state:int=33,
- var_prob:float=0.3, sbx_eta:float=3.0, mutation_eta=7.0, tournament_size=3, bootstrap_size=1000) -> None:
+ var_prob:float=0.3, sbx_eta:float=3.0, mutation_eta=7.0, tournament_size=3, bootstrap_size=1000,
+ p_value_compute=False) -> None:
'''
Fits a fuzzy rule based classifier using a genetic algorithm to the given data.
@@ -291,16 +288,16 @@ Source code for ex_fuzzy.evolutionary_fit
if self.custom_loss is not None:
problem.fitness_func = self.custom_loss
- if initial_rules is not None:
+ if initial_rules is None:
+ rules_gene = IntegerRandomSampling()
+ else:
rules_gene = problem.encode_rulebase(initial_rules, self.lvs is None)
rules_gene = (np.ones((pop_size, len(rules_gene))) * rules_gene).astype(int)
- else:
- rules_gene = IntegerRandomSampling()
algorithm = GA(
pop_size=pop_size,
- crossover=SBX(prob=var_prob, eta=sbx_eta),
- mutation=PolynomialMutation(eta=mutation_eta),
+ crossover=SBX(prob=var_prob, eta=sbx_eta, repair=RoundingRepair()),
+ mutation=PolynomialMutation(eta=mutation_eta, repair=RoundingRepair()),
tournament_size=tournament_size,
sampling=rules_gene,
eliminate_duplicates=False)
@@ -333,7 +330,7 @@ Source code for ex_fuzzy.evolutionary_fit
# self.rename_fuzzy_variables() This wont work on checkpoints!
rule_base.purge_rules(self.tolerance)
rule_base.rename_cons(self.classes_names)
- checkpoint_rules = rule_base.print_rules(True)
+ checkpoint_rules = rule_base.print_rules(True, bootstrap_results=True)
f.write(checkpoint_rules)
else:
@@ -371,18 +368,34 @@ Source code for ex_fuzzy.evolutionary_fit
self.rule_base.purge_rules(self.tolerance)
self.eval_performance.add_full_evaluation() # After purging the bad rules we update the metrics.
- self.p_value_class_structure, self.p_value_feature_coalitions = self.eval_performance.p_permutation_classifier_validation()
- self.eval_performance.p_bootstrapping_rules_validation(bootstrap_size)
+ if p_value_compute:
+ self.p_value_validation(bootstrap_size)
+
self.rule_base.rename_cons(self.classes_names)
if self.lvs is None:
self.rename_fuzzy_variables()
+
+
+[docs] def print_rule_bootstrap_results(self) -> None:
+ '''
+ Prints the bootstrap results for each rule.
+ '''
+ self.rule_base.print_rule_bootstrap_results()
+
+
+[docs] def p_value_validation(self, bootstrap_size:int=100):
+ '''
+ Computes the permutation and bootstrapping p-values for the classifier and its rules.
+
+ :param bootstrap_size: integer. Number of bootstraps samples to use.
+ '''
+ self.p_value_class_structure, self.p_value_feature_coalitions = self.eval_performance.p_permutation_classifier_validation()
+ self.eval_performance.p_bootstrapping_rules_validation(bootstrap_size)
-
-[docs]
- def load_master_rule_base(self, rule_base: rules.MasterRuleBase) -> None:
+[docs] def load_master_rule_base(self, rule_base: rules.MasterRuleBase) -> None:
'''
Loads a master rule base to be used in the prediction process.
@@ -393,13 +406,17 @@ Source code for ex_fuzzy.evolutionary_fit
self.nRules = len(rule_base.get_rules())
self.nAnts = len(rule_base.get_rules()[0].antecedents)
self.nclasses_ = len(rule_base)
-
+
+[docs] def explainable_predict(self, X: np.array, out_class_names=False) -> np.array:
+ '''
+ Returns the predicted class for each sample.
+ '''
+ return self.rule_base.explainable_predict(X, out_class_names=out_class_names)
-
-[docs]
- def forward(self, X: np.array, out_class_names=False) -> np.array:
+[docs] def forward(self, X: np.array, out_class_names=False) -> np.array:
'''
+
Returns the predicted class for each sample.
:param X: np array samples x features.
@@ -412,12 +429,9 @@ Source code for ex_fuzzy.evolutionary_fit
pass
return self.rule_base.winning_rule_predict(X, out_class_names=out_class_names)
-
-
-[docs]
- def predict(self, X: np.array, out_class_names=False) -> np.array:
+[docs] def predict(self, X: np.array, out_class_names=False) -> np.array:
'''
Returns the predicted class for each sample.
@@ -426,12 +440,9 @@ Source code for ex_fuzzy.evolutionary_fit
:return: np array samples (x 1) with the predicted class.
'''
return self.forward(X, out_class_names=out_class_names)
-
-
-[docs]
- def predict_proba(self, X: np.array) -> np.array:
+[docs] def predict_proba(self, X: np.array) -> np.array:
'''
Returns the predicted class probabilities for each sample.
@@ -446,20 +457,14 @@ Source code for ex_fuzzy.evolutionary_fit
return self.rule_base.compute_association_degrees(X)
-
-
-[docs]
- def print_rules(self, return_rules:bool=False) -> None:
+[docs] def print_rules(self, return_rules:bool=False, bootstrap_results:bool=False) -> None:
'''
Print the rules contained in the fitted rulebase.
'''
- return self.rule_base.print_rules(return_rules)
+ return self.rule_base.print_rules(return_rules, bootstrap_results)
-
-
-[docs]
- def plot_fuzzy_variables(self) -> None:
+[docs] def plot_fuzzy_variables(self) -> None:
'''
Plot the fuzzy partitions in each fuzzy variable.
'''
@@ -469,10 +474,7 @@ Source code for ex_fuzzy.evolutionary_fit
vis_rules.plot_fuzzy_variable(fv)
-
-
-[docs]
- def rename_fuzzy_variables(self) -> None:
+[docs] def rename_fuzzy_variables(self) -> None:
'''
Renames the linguist labels so that high, low and so on are consistent. It does so usually after an optimization process.
@@ -512,22 +514,16 @@ Source code for ex_fuzzy.evolutionary_fit
fuzzy_sets_vl[x].name = possible_names[jx]
-
-
-[docs]
- def get_rulebase(self) -> list[np.array]:
+[docs] def get_rulebase(self) -> list[np.array]:
'''
Get the rulebase obtained after fitting the classifier to the data.
:return: a matrix format for the rulebase.
'''
return self.rule_base.get_rulebase_matrix()
-
-
-[docs]
- def reparametrice_loss(self, alpha:float, beta:float) -> None:
+[docs] def reparametrice_loss(self, alpha:float, beta:float) -> None:
'''
Changes the parameters in the loss function.
@@ -539,7 +535,6 @@ Source code for ex_fuzzy.evolutionary_fit
self.beta_ = beta
-
def __call__(self, X:np.array) -> np.array:
'''
Returns the predicted class for each sample.
@@ -548,12 +543,9 @@ Source code for ex_fuzzy.evolutionary_fit
:return: np array samples (x 1) with the predicted class.
'''
return self.predict(X)
-
-
-[docs]
-class ExploreRuleBases(Problem):
+[docs]class ExploreRuleBases(Problem):
'''
Class to model as pymoo problem the fitting of a rulebase to a set of data given a series of candidate rules for a classification problem using Evolutionary strategies
Supports type 1 and t2.
@@ -681,9 +673,7 @@ Source code for ex_fuzzy.evolutionary_fit
out["F"] = 1
-
-[docs]
- def fitness_func(self, ruleBase: rules.RuleBase, X:np.array, y:np.array, tolerance:float, alpha:float=0.0, beta:float=0.0, precomputed_truth=None) -> float:
+[docs] def fitness_func(self, ruleBase: rules.RuleBase, X:np.array, y:np.array, tolerance:float, alpha:float=0.0, beta:float=0.0, precomputed_truth=None) -> float:
'''
Fitness function for the optimization problem.
:param ruleBase: RuleBase object
@@ -701,15 +691,11 @@ Source code for ex_fuzzy.evolutionary_fit
score = score_acc + score_rules_size * alpha + score_nrules * beta
- return score
-
-
+ return score
-
-[docs]
-class FitRuleBase(Problem):
+[docs]class FitRuleBase(Problem):
'''
Class to model as pymoo problem the fitting of a rulebase for a classification problem using Evolutionary strategies.
Supports type 1 and iv fs (iv-type 2)
@@ -847,9 +833,13 @@ Source code for ex_fuzzy.evolutionary_fit
self.feature_domain_bounds = np.array(
[[0, 99] for ix in range(self.X.shape[1])])
- size_multiplier = 4 if self.fuzzy_type == fs.FUZZY_SETS.t1 else 8
+ if self.fuzzy_type == fs.FUZZY_SETS.t1:
+ correct_size = [(self.n_lv_possible[ixx]-1) * 4 + 3 for ixx in range(len(self.n_lv_possible))]
+ elif self.fuzzy_type == fs.FUZZY_SETS.t2:
+
+ correct_size = [(self.n_lv_possible[ixx]-1) * 6 + 2 for ixx in range(len(self.n_lv_possible))]
membership_bounds = np.concatenate(
- [[self.feature_domain_bounds[ixx]] * size_multiplier * self.n_lv_possible[ixx] for ixx in range(len(self.n_lv_possible))])
+ [[self.feature_domain_bounds[ixx]] * correct_size[ixx] for ixx in range(len(self.n_lv_possible))])
vars_memberships = {
aux_counter + ix: Integer(bounds=membership_bounds[ix]) for ix in range(len(membership_bounds))}
@@ -913,9 +903,7 @@ Source code for ex_fuzzy.evolutionary_fit
xu=varbound[:, 1])
-
-[docs]
- def encode_rulebase(self, rule_base: rules.MasterRuleBase, optimize_lv: bool, encode_mods:bool=False) -> np.array:
+[docs] def encode_rulebase(self, rule_base: rules.MasterRuleBase, optimize_lv: bool, encode_mods:bool=False) -> np.array:
'''
Given a rule base, constructs the corresponding gene associated with that rule base.
@@ -943,7 +931,7 @@ Source code for ex_fuzzy.evolutionary_fit
if optimize_lv:
# If lv memberships are optimized.
fourth_pointer = 2 * self.nAnts * self.nRules + \
- len(rule_base.antecedents) * n_lv_possible * mf_size
+ len(self.n_lv_possible) * 3 + len(self.n_lv_possible) * 2 + sum(np.array(self.n_lv_possible)-2) * mf_size
else:
# If no memberships are optimized.
fourth_pointer = 2 * self.nAnts * self.nRules
@@ -1004,7 +992,6 @@ Source code for ex_fuzzy.evolutionary_fit
aux_pointer += 1
return np.array(list(map(int, gene)))
-
@@ -1024,20 +1011,26 @@ Source code for ex_fuzzy.evolutionary_fit
rule_list = [[] for _ in range(self.n_classes)]
- mf_size = 4 if fuzzy_type == fs.FUZZY_SETS.t1 else 8
+ mf_size = 4 if fuzzy_type == fs.FUZZY_SETS.t1 else 6
'''
GEN STRUCTURE
First: antecedents chosen by each rule. Size: nAnts * nRules
Second: Variable linguistics used. Size: nAnts * nRules
- Third: Parameters for the fuzzy partitions of the chosen variables. Size: X.shape[1] * self.n_linguistic_variables * 8|4 (2 trapezoidal memberships if t2)
+ Third: Parameters for the fuzzy partitions of the chosen variables. Size: X.shape[1] * ((self.n_linguistic_variables-1) * mf_size + 2)
Four: Consequent classes. Size: nRules
Five: Weights for each rule. Size: nRules (only if ds_mode == 2)
+ Sixth: Modifiers for the membership functions. Size: len(self.lvs) * nAnts * nRules
'''
if self.lvs is None:
# If memberships are optimized.
- fourth_pointer = 2 * self.nAnts * self.nRules + \
- sum(self.n_lv_possible) * mf_size
+ if fuzzy_type == fs.FUZZY_SETS.t1:
+ fourth_pointer = 2 * self.nAnts * self.nRules + \
+ len(self.n_lv_possible) * 3 + sum(np.array(self.n_lv_possible)-1) * 4
+ elif fuzzy_type == fs.FUZZY_SETS.t2:
+ fourth_pointer = 2 * self.nAnts * self.nRules + \
+ len(self.n_lv_possible) * 2 + sum(np.array(self.n_lv_possible)-1) * mf_size
+
else:
# If no memberships are optimized.
fourth_pointer = 2 * self.nAnts * self.nRules
@@ -1053,6 +1046,7 @@ Source code for ex_fuzzy.evolutionary_fit
sixth_pointer = fifth_pointer
+
aux_pointer = 0
min_domain = np.min(self.X, axis=0)
max_domain = np.max(self.X, axis=0)
@@ -1083,6 +1077,7 @@ Source code for ex_fuzzy.evolutionary_fit
init_rule_antecedents[ant] = antecedent_parameters[jx]
consequent_idx = x[fourth_pointer + aux_pointer]
+
assert consequent_idx < self.n_classes, "Consequent class is not valid. Something in the gene is wrong."
aux_pointer += 1
@@ -1112,7 +1107,7 @@ Source code for ex_fuzzy.evolutionary_fit
rs_instance)
- # If we optimize the membership functions
+ # If we optimize the membership functions - change to delta system
if self.lvs is None:
third_pointer = 2 * self.nAnts * self.nRules
aux_pointer = 0
@@ -1120,30 +1115,141 @@ Source code for ex_fuzzy.evolutionary_fit
for fuzzy_variable in range(self.X.shape[1]):
linguistic_variables = []
+ lv_FS = []
- for linguistic_variable in range(self.n_lv_possible[fuzzy_variable]):
+ for lx in range(self.n_lv_possible[fuzzy_variable]):
parameter_pointer = third_pointer + aux_pointer
- fz_parameters_idx = x[parameter_pointer:parameter_pointer + mf_size]
- fz_parameters = self.antecedents_referencial[fuzzy_variable][fz_parameters_idx]
- aux_pointer += mf_size
-
- if fuzzy_type == fs.FUZZY_SETS.t2:
- fz_parameters[0:6] = np.sort(fz_parameters[0:6])
- mu = [np.min(fz_parameters[0:2]), fz_parameters[2],
- fz_parameters[3], np.max(fz_parameters[4:6])]
- ml = [np.max(fz_parameters[0:2]), fz_parameters[2],
- fz_parameters[3], np.min(fz_parameters[4:6])]
- height = fz_parameters[6] / np.max(fz_parameters)
-
- ivfs = fs.IVFS(self.vl_names[fuzzy_variable][linguistic_variable], ml, mu,
- (min_domain[fuzzy_variable], max_domain[fuzzy_variable]), lower_height=height)
- else:
- ivfs = fs.FS(self.vl_names[fuzzy_variable][linguistic_variable], np.sort(fz_parameters[0:4]),
- (min_domain[fuzzy_variable], max_domain[fuzzy_variable]))
- linguistic_variables.append(ivfs)
+ if fuzzy_type == fs.FUZZY_SETS.t1:
+ if lx == 0:
+ fz_parameters_idx0 = x[parameter_pointer]
+ fz_parameters_idx1 = x[parameter_pointer + 1]
+ fz_parameters_idx2 = x[parameter_pointer + 2]
+ fz_parameters_idx3 = x[parameter_pointer + 3]
+
+ fz0 = fz_parameters_idx0
+ fz1 = fz_parameters_idx0
+ fz2 = fz1 + fz_parameters_idx1
+ next_fz0 = fz2 + fz_parameters_idx2
+ fz3 = next_fz0 + fz_parameters_idx3
+
+ fz_parameters = np.array([fz0, fz1, fz2, fz3])
+ aux_pointer += 4
+
+ elif lx == self.n_lv_possible[fuzzy_variable] - 1:
+ fz_parameters_idx1 = x[parameter_pointer]
+ fz_parameters_idx2 = x[parameter_pointer + 1]
+
+ fz0 = next_fz0
+ fz1 = fz3 + fz_parameters_idx1
+ fz2 = fz1 + fz_parameters_idx2
+ fz3 = fz2
+
+ fz_parameters = np.array([fz0, fz1, fz2, fz3])
+ aux_pointer += 3
+ else:
+ fz_parameters_idx1 = x[parameter_pointer]
+ fz_parameters_idx2 = x[parameter_pointer + 1]
+ fz_parameters_idx3 = x[parameter_pointer + 2]
+ fz_parameters_idx4 = x[parameter_pointer + 3]
+
+ fz0 = next_fz0
+ fz1 = fz3 + fz_parameters_idx1
+ fz2 = fz1 + fz_parameters_idx2
+ next_fz0 = fz2 + fz_parameters_idx3
+ fz3 = next_fz0 + fz_parameters_idx4
+ aux_pointer += 4
+
+ fz_parameters = np.array([fz0, fz1, fz2, fz3])
+
+ lv_FS.append(fz_parameters)
+
+ elif fuzzy_type == fs.FUZZY_SETS.t2:
+ if lx == 0:
+ fz_parameters_idx0 = x[parameter_pointer]
+ fz_parameters_idx1 = x[parameter_pointer + 1]
+ fz_parameters_idx2 = x[parameter_pointer + 2]
+ fz_parameters_idx3 = x[parameter_pointer + 3]
+ fz_parameters_idx4 = x[parameter_pointer + 4]
+ fz_parameters_idx5 = x[parameter_pointer + 5]
+
+ l_fz0 = fz_parameters_idx0
+ l_fz1 = l_fz0
+ l_fz2 = l_fz1 + fz_parameters_idx1
+ next_ufz0 = l_fz2 + fz_parameters_idx2
+ next_lfz0 = next_ufz0 + fz_parameters_idx3
+ l_fz3 = next_lfz0 + fz_parameters_idx4
+
+ u_fz0 = l_fz0
+ u_fz1 = u_fz0
+ u_fz2 = l_fz2
+ u_fz3 = l_fz3 + fz_parameters_idx5
+
+ l_fz_parameters = np.array([l_fz0, l_fz1, l_fz2, l_fz3])
+ u_fz_parameters = np.array([u_fz0, u_fz1, u_fz2, u_fz3])
+ next_init = l_fz2 + fz_parameters_idx4
+ aux_pointer += 6
+
+ elif lx == self.n_lv_possible[fuzzy_variable] - 1:
+ fz_parameters_idx0 = x[parameter_pointer]
+ fz_parameters_idx1 = x[parameter_pointer + 1]
+
+ u_fz0 = next_ufz0
+ l_fz0 = next_lfz0
+ u_fz1 = u_fz3 + fz_parameters_idx0
+ l_fz1 = u_fz1
+ u_fz2 = l_fz1 + fz_parameters_idx1
+ l_fz2 = u_fz2
+ l_fz3 = l_fz2
+ u_fz3 = l_fz3
+
+
+ l_fz_parameters = np.array([l_fz0, l_fz1, l_fz2, l_fz3])
+ u_fz_parameters = np.array([u_fz0, u_fz1, u_fz2, u_fz3])
+ aux_pointer += 2
+
+ else:
+ fz_parameters_idx0 = x[parameter_pointer]
+ fz_parameters_idx1 = x[parameter_pointer + 1]
+ fz_parameters_idx2 = x[parameter_pointer + 2]
+ fz_parameters_idx3 = x[parameter_pointer + 3]
+ fz_parameters_idx4 = x[parameter_pointer + 4]
+ fz_parameters_idx5 = x[parameter_pointer + 5]
+
+ u_fz0 = next_ufz0
+ l_fz0 = next_lfz0
+
+ l_fz1 = u_fz3 + fz_parameters_idx0
+ u_fz1 = l_fz1
+ l_fz2 = l_fz1 + fz_parameters_idx1
+ u_fz2 = l_fz2
+
+ next_ufz0 = l_fz2 + fz_parameters_idx2
+ next_lfz0 = next_ufz0 + fz_parameters_idx3
+
+ l_fz3 = next_lfz0 + fz_parameters_idx4
+ u_fz3 = l_fz3 + fz_parameters_idx5
+
+ l_fz_parameters = np.array([l_fz0, l_fz1, l_fz2, l_fz3])
+ u_fz_parameters = np.array([u_fz0, u_fz1, u_fz2, u_fz3])
+ aux_pointer += 6
+
+
+ lv_FS.append((l_fz_parameters, u_fz_parameters))
+
+ min_lv = np.min(np.array(lv_FS))
+ max_lv = np.max(np.array(lv_FS))
+
+ for lx, relevant_lv in enumerate(lv_FS):
+ relevant_lv = (relevant_lv - min_lv) / (max_lv - min_lv) * max_domain[fuzzy_variable]
+ if fuzzy_type == fs.FUZZY_SETS.t1:
+ proper_FS = fs.FS(self.vl_names[fuzzy_variable][lx], relevant_lv, (min_domain[fuzzy_variable], max_domain[fuzzy_variable]))
+ elif fuzzy_type == fs.FUZZY_SETS.t2:
+ proper_FS = fs.IVFS(self.vl_names[fuzzy_variable][lx], relevant_lv[0], relevant_lv[1], (min_domain[fuzzy_variable], max_domain[fuzzy_variable]))
+ linguistic_variables.append(proper_FS)
antecedents.append(fs.fuzzyVariable(
- self.var_names[fuzzy_variable], linguistic_variables))
+ self.var_names[fuzzy_variable], linguistic_variables))
+
else:
try:
antecedents = self.lvs[kwargs['time_moment']]
@@ -1190,9 +1296,7 @@ Source code for ex_fuzzy.evolutionary_fit
out["F"] = 1 - score
-
-[docs]
- def fitness_func(self, ruleBase: rules.RuleBase, X:np.array, y:np.array, tolerance:float, alpha:float=0.0, beta:float=0.0, precomputed_truth:np.array=None) -> float:
+[docs] def fitness_func(self, ruleBase: rules.RuleBase, X:np.array, y:np.array, tolerance:float, alpha:float=0.0, beta:float=0.0, precomputed_truth:np.array=None) -> float:
'''
Fitness function for the optimization problem.
:param ruleBase: RuleBase object
@@ -1220,9 +1324,7 @@ Source code for ex_fuzzy.evolutionary_fit
else:
score = 0.0
- return score
-
-
+ return score
diff --git a/docs/build/html/_modules/ex_fuzzy/fuzzy_sets.html b/docs/build/html/_modules/ex_fuzzy/fuzzy_sets.html
index 89823d9..8de616c 100644
--- a/docs/build/html/_modules/ex_fuzzy/fuzzy_sets.html
+++ b/docs/build/html/_modules/ex_fuzzy/fuzzy_sets.html
@@ -1,20 +1,22 @@
-
-
-
+
ex_fuzzy.fuzzy_sets — Ex-Fuzzy documentation
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
@@ -52,7 +54,7 @@
Extending Ex-Fuzzy
Persistence
Advanced classifiers
-Studying patterns
+Bootstrapping and rule robustness
API
@@ -116,9 +118,7 @@ Source code for ex_fuzzy.fuzzy_sets
return self.value == __value.value
-
-[docs]
-def trapezoidal_membership(x: np.array, params: list[float], epsilon=10E-5) -> np.array:
+[docs]def trapezoidal_membership(x: np.array, params: list[float], epsilon=10E-5) -> np.array:
'''
Trapezoidal membership functions.
@@ -162,7 +162,6 @@ Source code for ex_fuzzy.fuzzy_sets
-
def __gaussian2(x, params: list[float]) -> np.array:
'''
Gaussian membership functions.
@@ -175,9 +174,7 @@ Source code for ex_fuzzy.fuzzy_sets
return amplitude * np.exp(- ((x - mean) / standard_deviation) ** 2)
-
-[docs]
-class FS():
+[docs]class FS():
'''
Class that defines the most basic fuzzy sets (also known as Type 1 fuzzy sets or Zadeh sets).
'''
@@ -199,9 +196,7 @@ Source code for ex_fuzzy.fuzzy_sets
self.membership_parameters = membership_parameters
-
-[docs]
- def membership(self, x: np.array) -> np.array:
+[docs] def membership(self, x: np.array) -> np.array:
'''
Computes the membership of a point or a vector.
@@ -210,10 +205,7 @@ Source code for ex_fuzzy.fuzzy_sets
return trapezoidal_membership(x, self.membership_parameters)
-
-
-[docs]
- def type(self) -> FUZZY_SETS:
+[docs] def type(self) -> FUZZY_SETS:
'''
Returns the corresponding fuzzy set type according to FUZZY_SETS enum.
@@ -222,7 +214,6 @@ Source code for ex_fuzzy.fuzzy_sets
return FUZZY_SETS.t1
-
def __call__(self, x: np.array) -> np.array:
'''
Calling the Fuzzy set returns its membership.
@@ -243,10 +234,7 @@ Source code for ex_fuzzy.fuzzy_sets
-
-
-[docs]
-class categoricalFS(FS):
+[docs]class categoricalFS(FS):
def __init__(self, name: str, category) -> None:
'''
@@ -264,9 +252,7 @@ Source code for ex_fuzzy.fuzzy_sets
mnt.usage_data[mnt.usage_categories.FuzzySets][self.type().name] += 1
-
-[docs]
- def membership(self, x: np.array) -> np.array:
+[docs] def membership(self, x: np.array) -> np.array:
'''
Computes the membership of a point or vector of elements.
@@ -286,15 +272,11 @@ Source code for ex_fuzzy.fuzzy_sets
return res
-
-
-[docs]
- def type(self) -> FUZZY_SETS:
+[docs] def type(self) -> FUZZY_SETS:
'''
Returns the corresponding fuzzy set type according to FUZZY_SETS enum.
'''
return FUZZY_SETS.t1
-
def __str__(self) -> str:
@@ -306,10 +288,7 @@ Source code for ex_fuzzy.fuzzy_sets
return f'Categorical set: {self.name}, type 1 output'
-
-
-[docs]
-class IVFS(FS):
+[docs]class IVFS(FS):
'''
Class to define a iv fuzzy set.
'''
@@ -342,9 +321,7 @@ Source code for ex_fuzzy.fuzzy_sets
self.lower_height = lower_height
-
-[docs]
- def membership(self, x: np.array) -> np.array:
+[docs] def membership(self, x: np.array) -> np.array:
'''
Computes the iv-membership of a point or a vector.
@@ -363,15 +340,11 @@ Source code for ex_fuzzy.fuzzy_sets
return np.stack([lower, upper], axis=-1)
-
-
-[docs]
- def type(self) -> FUZZY_SETS:
+[docs] def type(self) -> FUZZY_SETS:
'''
Returns the corresponding fuzzy set type according to FUZZY_SETS enum: (t2)
'''
return FUZZY_SETS.t2
-
def __str__(self) -> str:
@@ -383,10 +356,7 @@ Source code for ex_fuzzy.fuzzy_sets
return f'{self.name} ({self.type().name}) - {self.secondMF_lower} - {self.secondMF_upper}'
-
-
-[docs]
-class categoricalIVFS(IVFS):
+[docs]class categoricalIVFS(IVFS):
'''
Class to define a iv fuzzy set with categorical membership.
'''
@@ -408,9 +378,7 @@ Source code for ex_fuzzy.fuzzy_sets
mnt.usage_data[mnt.usage_categories.FuzzySets][self.type().name] += 1
-
-[docs]
- def membership(self, x: np.array) -> np.array:
+[docs] def membership(self, x: np.array) -> np.array:
'''
Computes the membership of a point or vector of elements.
@@ -435,17 +403,13 @@ Source code for ex_fuzzy.fuzzy_sets
return res
-
-
-[docs]
- def type(self) -> FUZZY_SETS:
+[docs] def type(self) -> FUZZY_SETS:
'''
Returns the corresponding fuzzy set type according to FUZZY_SETS enum.
'''
return FUZZY_SETS.t2
-
def __str__(self) -> str:
'''
Returns the name of the fuzzy set, its type and its parameters.
@@ -453,13 +417,10 @@ Source code for ex_fuzzy.fuzzy_sets
:return: string.
'''
return f'Categorical set: {self.name}, type 2 output'
-
-
-[docs]
-class GT2(FS):
+[docs]class GT2(FS):
'''
Class to define a gt2 fuzzy set.
'''
@@ -530,9 +491,7 @@ Source code for ex_fuzzy.fuzzy_sets
self.iv_secondary_memberships[alpha_cut] = array_level_memberships
-
-[docs]
- def membership(self, x: np.array) -> np.array:
+[docs] def membership(self, x: np.array) -> np.array:
'''
Computes the alpha cut memberships of a point.
@@ -557,12 +516,8 @@ Source code for ex_fuzzy.fuzzy_sets
return np.swapaxes(alpha_cut_memberships, 0, 1)
-
-
-[docs]
- def type(self) -> FUZZY_SETS:
+
-
def _alpha_reduction(self, x) -> np.array:
@@ -580,30 +535,22 @@ Source code for ex_fuzzy.fuzzy_sets
return np.sum(formatted * x, axis=-1) / np.sum(self.alpha_cuts)
-
-[docs]
- def alpha_reduction(self, x) -> np.array:
+[docs] def alpha_reduction(self, x) -> np.array:
'''
Computes the type reduction to reduce the alpha cuts to one value.
:param x: array with the values of the inputs.
:return: array with the memberships of the consequents for each sample.
'''
- return self._alpha_reduction(x)
-
-
+ return self._alpha_reduction(x)
-
-[docs]
-class gaussianIVFS(IVFS):
+[docs]class gaussianIVFS(IVFS):
'''
Class to define a iv fuzzy set with gaussian membership.
'''
-
-[docs]
- def membership(self, input: np.array) -> np.array:
+[docs] def membership(self, input: np.array) -> np.array:
'''
Computes the gaussian iv-membership of a point or a vector.
@@ -616,28 +563,19 @@ Source code for ex_fuzzy.fuzzy_sets
return np.array(np.concatenate([lower, upper])).T
-
-
-[docs]
- def type(self) -> FUZZY_SETS:
+[docs] def type(self) -> FUZZY_SETS:
'''
Returns the type of the fuzzy set. (t1)
'''
- return FUZZY_SETS.t2
-
-
+ return FUZZY_SETS.t2
-
-[docs]
-class gaussianFS(FS):
+[docs]class gaussianFS(FS):
'''
Class to define a gaussian fuzzy set.
'''
-
-[docs]
- def membership(self, input: np.array) -> np.array:
+[docs] def membership(self, input: np.array) -> np.array:
'''
Computes the gaussian membership of a point or a vector.
@@ -647,21 +585,14 @@ Source code for ex_fuzzy.fuzzy_sets
return __gaussian2(input, self.membership_parameters)
-
-
-[docs]
- def type(self) -> FUZZY_SETS:
+[docs] def type(self) -> FUZZY_SETS:
'''
Returns the type of the fuzzy set. (t1)
'''
- return FUZZY_SETS.t1
-
+ return FUZZY_SETS.t1
-
-
-[docs]
-class fuzzyVariable():
+[docs]class fuzzyVariable():
'''
Class to implement a fuzzy Variable. Contains a series of fuzzy sets and computes the memberships to all of them.
'''
@@ -684,9 +615,7 @@ Source code for ex_fuzzy.fuzzy_sets
self.fs_type = self.linguistic_variables[0].type()
-
-[docs]
- def append(self, fs: FS) -> None:
+[docs] def append(self, fs: FS) -> None:
'''
Appends a fuzzy set to the fuzzy variable.
@@ -695,10 +624,7 @@ Source code for ex_fuzzy.fuzzy_sets
self.linguistic_variables.append(fs)
-
-
-[docs]
- def linguistic_variable_names(self) -> list:
+[docs] def linguistic_variable_names(self) -> list:
'''
Returns the name of the linguistic variables.
@@ -707,10 +633,7 @@ Source code for ex_fuzzy.fuzzy_sets
return [fs.name for fs in self.linguistic_variables]
-
-
-[docs]
- def get_linguistic_variables(self) -> list[FS]:
+[docs] def get_linguistic_variables(self) -> list[FS]:
'''
Returns the name of the linguistic variables.
@@ -719,10 +642,7 @@ Source code for ex_fuzzy.fuzzy_sets
return self.linguistic_variables
-
-
-[docs]
- def compute_memberships(self, x: np.array) -> list:
+[docs] def compute_memberships(self, x: np.array) -> list:
'''
Computes the membership to each of the FS in the fuzzy variables.
@@ -737,10 +657,7 @@ Source code for ex_fuzzy.fuzzy_sets
return res
-
-
-[docs]
- def domain(self) -> list[float]:
+[docs] def domain(self) -> list[float]:
'''
Returns the domain of the fuzzy variable.
@@ -749,10 +666,7 @@ Source code for ex_fuzzy.fuzzy_sets
return self.linguistic_variables[0].domain
-
-
-[docs]
- def fuzzy_type(self) -> FUZZY_SETS:
+[docs] def fuzzy_type(self) -> FUZZY_SETS:
'''
Returns the fuzzy type of the domain
@@ -761,7 +675,6 @@ Source code for ex_fuzzy.fuzzy_sets
return self.fs_type
-
def __getitem__(self, item) -> FS:
'''
Returns the corresponding fs.
@@ -812,7 +725,6 @@ Source code for ex_fuzzy.fuzzy_sets
return self.compute_memberships(x)
-
diff --git a/docs/build/html/_modules/ex_fuzzy/rule_mining.html b/docs/build/html/_modules/ex_fuzzy/rule_mining.html
index 5890a5a..00f3320 100644
--- a/docs/build/html/_modules/ex_fuzzy/rule_mining.html
+++ b/docs/build/html/_modules/ex_fuzzy/rule_mining.html
@@ -54,6 +54,7 @@
Extending Ex-Fuzzy
Persistence
Advanced classifiers
+Bootstrapping and rule robustness
API
diff --git a/docs/build/html/_modules/ex_fuzzy/rules.html b/docs/build/html/_modules/ex_fuzzy/rules.html
index 65eb9b7..1972771 100644
--- a/docs/build/html/_modules/ex_fuzzy/rules.html
+++ b/docs/build/html/_modules/ex_fuzzy/rules.html
@@ -1,20 +1,22 @@
-
-
-
+
ex_fuzzy.rules — Ex-Fuzzy documentation
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
@@ -52,7 +54,7 @@
Extending Ex-Fuzzy
Persistence
Advanced classifiers
-Studying patterns
+Bootstrapping and rule robustness
API
@@ -99,9 +101,7 @@ Source code for ex_fuzzy.rules
modifiers_names = {0.5: 'Somewhat', 1.0: '', 1.3: 'A little', 1.7: 'Slightly', 2.0: 'Very', 3.0: 'Extremely', 4.0: 'Very very'}
-
-[docs]
-def compute_antecedents_memberships(antecedents: list[fs.fuzzyVariable], x: np.array) -> list[dict]:
+[docs]def compute_antecedents_memberships(antecedents: list[fs.fuzzyVariable], x: np.array) -> list[dict]:
'''
Returns a list of of dictionaries that contains the memberships for each x value to the ith antecedents, nth linguistic variable.
x must be a vector (only one sample)
@@ -118,12 +118,9 @@ Source code for ex_fuzzy.rules
return cache_antecedent_memberships
-
-
-[docs]
-class RuleError(Exception):
+[docs]class RuleError(Exception):
'''
Exception raised when a rule is not well defined.
'''
@@ -138,7 +135,6 @@ Source code for ex_fuzzy.rules
-
def _myprod(x: np.array, y: np.array) -> np.array:
'''
Proxy function to change the product operation interface
@@ -146,9 +142,7 @@ Source code for ex_fuzzy.rules
return x*y
-
-[docs]
-class Rule():
+[docs]class Rule():
'''
Class of Rule designed to work with one single rule. It contains the whole inference functionally in itself.
'''
@@ -164,9 +158,7 @@ Source code for ex_fuzzy.rules
self.consequent = consequent
-
-[docs]
- def membership(self, x: np.array, tnorm=_myprod) -> np.array:
+[docs] def membership(self, x: np.array, tnorm=_myprod) -> np.array:
'''
Computes the membership of one input to the antecedents of the rule.
@@ -182,10 +174,7 @@ Source code for ex_fuzzy.rules
return res
-
-
-[docs]
- def consequent_centroid(self) -> np.array:
+[docs] def consequent_centroid(self) -> np.array:
'''
Returns the centroid of the consequent using a Karnik and Mendel algorithm.
'''
@@ -201,15 +190,11 @@ Source code for ex_fuzzy.rules
self.centroid_consequent = centroid.compute_centroid_fs(
domain_linspace, consequent_memberships)
- return self.centroid_consequent
-
-
+ return self.centroid_consequent
-
-[docs]
-class RuleSimple():
+[docs]class RuleSimple():
'''
Class designed to represent rules in its simplest form to optimize the computation in a rulebase.
@@ -287,6 +272,22 @@ Source code for ex_fuzzy.rules
except AttributeError:
pass
+ try:
+ aux += ' p-value bootstrapping membership validation: ' + str(self.boot_p_value)
+ except AttributeError:
+ pass
+
+ try:
+ aux += ' bootstrapping confidence conf. interval: ' + str(self.boot_confidence_interval)
+ except AttributeError:
+ pass
+
+ try:
+ aux += ' bootstrapping support conf. interval: ' + str(self.boot_support_interval)
+ except AttributeError:
+ pass
+
+
return aux
@@ -312,10 +313,7 @@ Source code for ex_fuzzy.rules
-
-
-[docs]
-class RuleBase():
+[docs]class RuleBase():
'''
Class optimized to work with multiple rules at the same time. Right now supports only one consequent. (Solution: use one rulebase per consequent to study)
'''
@@ -356,19 +354,14 @@ Source code for ex_fuzzy.rules
self.delete_duplicates()
-
-[docs]
- def get_rules(self) -> list[RuleSimple]:
+[docs] def get_rules(self) -> list[RuleSimple]:
'''
Returns the list of rules in the rulebase.
'''
return self.rules
-
-
-[docs]
- def add_rule(self, new_rule: RuleSimple):
+[docs] def add_rule(self, new_rule: RuleSimple):
'''
Adds a new rule to the rulebase.
:param new_rule: rule to add.
@@ -376,10 +369,7 @@ Source code for ex_fuzzy.rules
self.rules.append(new_rule)
-
-
-[docs]
- def add_rules(self, new_rules: list[RuleSimple]):
+[docs] def add_rules(self, new_rules: list[RuleSimple]):
'''
Adds a list of new rules to the rulebase.
@@ -388,10 +378,7 @@ Source code for ex_fuzzy.rules
self.rules += new_rules
-
-
-[docs]
- def remove_rule(self, ix: int) -> None:
+[docs] def remove_rule(self, ix: int) -> None:
'''
Removes the rule in the given index.
:param ix: index of the rule to remove.
@@ -399,10 +386,7 @@ Source code for ex_fuzzy.rules
del self.rules[ix]
-
-
-[docs]
- def remove_rules(self, delete_list: list[int]) -> None:
+[docs] def remove_rules(self, delete_list: list[int]) -> None:
'''
Removes the rules in the given list of indexes.
@@ -412,10 +396,7 @@ Source code for ex_fuzzy.rules
self.rules) if ix not in delete_list]
-
-
-[docs]
- def get_rulebase_matrix(self):
+[docs] def get_rulebase_matrix(self):
'''
Returns a matrix with the antecedents values for each rule.
'''
@@ -427,10 +408,7 @@ Source code for ex_fuzzy.rules
return res
-
-
-[docs]
- def get_scores(self):
+[docs] def get_scores(self):
'''
Returns an array with the dominance score for each rule.
(Must been already computed by an evalRule object)
@@ -441,12 +419,9 @@ Source code for ex_fuzzy.rules
res[ix] = rule.score
return res
-
-
-[docs]
- def get_weights(self):
+[docs] def get_weights(self):
'''
Returns an array with the weights for each rule.
(Different from dominance scores: must been already computed by an optimization algorithm)
@@ -459,7 +434,6 @@ Source code for ex_fuzzy.rules
return res
-
def delete_rule_duplicates(self, list_rules:list[RuleSimple]):
# Delete the rules that are duplicated in the rule list
unique = {}
@@ -474,9 +448,7 @@ Source code for ex_fuzzy.rules
return new_list
-
-[docs]
- def compute_antecedents_memberships(self, x: np.array) -> list[dict]:
+[docs] def compute_antecedents_memberships(self, x: np.array) -> list[dict]:
'''
Returns a list of of dictionaries that contains the memberships for each x value to the ith antecedents, nth linguistic variable.
x must be a vector (only one sample)
@@ -506,10 +478,7 @@ Source code for ex_fuzzy.rules
return [np.zeros((x.shape[0], len(self.alpha_cuts), 2))]
-
-
-[docs]
- def compute_rule_antecedent_memberships(self, x: np.array, scaled=False, antecedents_memberships:list[np.array]=None) -> np.array:
+[docs] def compute_rule_antecedent_memberships(self, x: np.array, scaled=False, antecedents_memberships:list[np.array]=None) -> np.array:
'''
Computes the antecedent truth value of an input array.
@@ -602,10 +571,7 @@ Source code for ex_fuzzy.rules
return res
-
-
-[docs]
- def print_rules(self, return_rules:bool=False) -> None:
+[docs] def print_rules(self, return_rules:bool=False, bootstrap_results:bool=True) -> None:
'''
Print the rules from the rule base.
@@ -613,7 +579,7 @@ Source code for ex_fuzzy.rules
'''
all_rules = ''
for ix, rule in enumerate(self.rules):
- str_rule = generate_rule_string(rule, self.antecedents)
+ str_rule = generate_rule_string(rule, self.antecedents, bootstrap_results)
all_rules += str_rule + '\n'
@@ -622,13 +588,10 @@ Source code for ex_fuzzy.rules
else:
return all_rules
-
-
-[docs]
- @abc.abstractmethod
+[docs] @abc.abstractmethod
def inference(self, x: np.array) -> np.array:
'''
Computes the fuzzy output of the fl inference system.
@@ -641,10 +604,7 @@ Source code for ex_fuzzy.rules
raise NotImplementedError
-
-
-[docs]
- @abc.abstractmethod
+[docs] @abc.abstractmethod
def forward(self, x: np.array) -> np.array:
'''
Computes the deffuzified output of the fl inference system.
@@ -657,10 +617,7 @@ Source code for ex_fuzzy.rules
raise NotImplementedError
-
-
-[docs]
- @abc.abstractmethod
+[docs] @abc.abstractmethod
def fuzzy_type(self) -> fs.FUZZY_SETS:
'''
Returns the corresponding type of the RuleBase using the enum type in the fuzzy_sets module.
@@ -670,7 +627,6 @@ Source code for ex_fuzzy.rules
raise NotImplementedError
-
def __len__(self):
'''
Returns the number of rules in the rule base.
@@ -678,9 +634,7 @@ Source code for ex_fuzzy.rules
return len(self.rules)
-
-[docs]
- def prune_bad_rules(self, tolerance=0.01) -> None:
+[docs] def prune_bad_rules(self, tolerance=0.01) -> None:
'''
Delete the rules from the rule base that do not have a dominance score superior to the threshold or have 0 accuracy in the training set.
@@ -705,10 +659,7 @@ Source code for ex_fuzzy.rules
self.remove_rules(delete_list)
-
-
-[docs]
- def scores(self) -> np.array:
+[docs] def scores(self) -> np.array:
'''
Returns the dominance score for each rule.
@@ -721,7 +672,6 @@ Source code for ex_fuzzy.rules
return np.array(scores)
-
def __getitem__(self, item: int) -> RuleSimple:
'''
Returns the corresponding rulebase.
@@ -780,20 +730,22 @@ Source code for ex_fuzzy.rules
return RuleBase(self.antecedents, self.rules + other.rules, self.consequent, self.tnorm)
-
-[docs]
- def n_linguistic_variables(self) -> int:
+[docs] def n_linguistic_variables(self) -> int:
'''
Returns the number of linguistic variables in the rule base.
'''
return [len(amt) for amt in self.antecedents]
-
+[docs] def print_rule_bootstrap_results(self) -> None:
+ '''
+ Prints the bootstrap results for each rule.
+ '''
+ for ix, rule in enumerate(self.rules):
+ print('Rule ' + str(ix) + ': ' + str(rule) + ' - Confidence: ' + str(rule.boot_confidence_interval) + ' - Support: ' + str(rule.boot_support_interval))
-
-[docs]
-class RuleBaseT2(RuleBase):
+
+[docs]class RuleBaseT2(RuleBase):
'''
Class optimized to work with multiple rules at the same time. Supports only one consequent.
(Use one rulebase per consequent to study classification problems. Check MasterRuleBase class for more documentation)
@@ -838,9 +790,7 @@ Source code for ex_fuzzy.rules
self.consequent_centroids_rules[ix] = self.consequent_centroids[consequent_ix]
-
-[docs]
- def inference(self, x: np.array) -> np.array:
+[docs] def inference(self, x: np.array) -> np.array:
'''
Computes the iv output of the t2 inference system.
@@ -859,10 +809,7 @@ Source code for ex_fuzzy.rules
return res
-
-
-[docs]
- def forward(self, x: np.array) -> np.array:
+[docs] def forward(self, x: np.array) -> np.array:
'''
Computes the deffuzified output of the t2 inference system.
@@ -874,23 +821,16 @@ Source code for ex_fuzzy.rules
return np.mean(self.inference(x))
-
-
-[docs]
- def fuzzy_type(self) -> fs.FUZZY_SETS:
+[docs] def fuzzy_type(self) -> fs.FUZZY_SETS:
'''
Returns the correspoing type of the RuleBase using the enum type in the fuzzy_sets module.
:return: the corresponding fuzzy set type of the RuleBase.
'''
- return fs.FUZZY_SETS.t2
-
+ return fs.FUZZY_SETS.t2
-
-
-[docs]
-class RuleBaseGT2(RuleBase):
+[docs]class RuleBaseGT2(RuleBase):
'''
Class optimized to work with multiple rules at the same time. Supports only one consequent.
(Use one rulebase per consequent to study classification problems. Check MasterRuleBase class for more documentation)
@@ -921,9 +861,7 @@ Source code for ex_fuzzy.rules
self.fuzzy_modifier = fuzzy_modifiers
-
-[docs]
- def inference(self, x: np.array) -> np.array:
+[docs] def inference(self, x: np.array) -> np.array:
'''
Computes the output of the gt2 inference system.
@@ -942,7 +880,6 @@ Source code for ex_fuzzy.rules
return res
-
def _alpha_reduction(self, x) -> np.array:
'''
Computes the type reduction to reduce the alpha cuts to one value.
@@ -955,9 +892,7 @@ Source code for ex_fuzzy.rules
return np.sum(formtatted * x, axis=2) / np.sum(self.alpha_cuts)
-
-[docs]
- def forward(self, x: np.array) -> np.array:
+[docs] def forward(self, x: np.array) -> np.array:
'''
Computes the deffuzified output of the t2 inference system.
@@ -969,10 +904,7 @@ Source code for ex_fuzzy.rules
return np.sum(np.array(self.alpha_cuts) * (self.inference(x)), axis=1) / np.sum(self.alpha_cuts)
-
-
-[docs]
- def fuzzy_type(self) -> fs.FUZZY_SETS:
+[docs] def fuzzy_type(self) -> fs.FUZZY_SETS:
'''
Returns the correspoing type of the RuleBase using the enum type in the fuzzy_sets module.
@@ -981,10 +913,7 @@ Source code for ex_fuzzy.rules
return fs.FUZZY_SETS.gt2
-
-
-[docs]
- def compute_rule_antecedent_memberships(self, x: np.array, scaled=True, antecedents_memberships=None) -> np.array:
+[docs] def compute_rule_antecedent_memberships(self, x: np.array, scaled=True, antecedents_memberships=None) -> np.array:
'''
Computes the membership for the antecedents performing the alpha_cut reduction.
@@ -999,10 +928,7 @@ Source code for ex_fuzzy.rules
return self._alpha_reduction(rules_truth_values)
-
-
-[docs]
- def alpha_compute_rule_antecedent_memberships(self, x: np.array, scaled=True, antecedents_memberships=None) -> np.array:
+[docs] def alpha_compute_rule_antecedent_memberships(self, x: np.array, scaled=True, antecedents_memberships=None) -> np.array:
'''
Computes the membership for the antecedents for all the alpha cuts.
@@ -1011,15 +937,11 @@ Source code for ex_fuzzy.rules
:param antecedents_memberships: precomputed antecedent memberships. Not supported for GT2.
:return: array with the memberships of the antecedents for each sample.
'''
- return super().compute_rule_antecedent_memberships(x, scaled)
-
+ return super().compute_rule_antecedent_memberships(x, scaled)
-
-
-[docs]
-class RuleBaseT1(RuleBase):
+[docs]class RuleBaseT1(RuleBase):
'''
Class optimized to work with multiple rules at the same time. Supports only one consequent.
(Use one rulebase per consequent to study classification problems. Check MasterRuleBase class for more documentation)
@@ -1062,9 +984,7 @@ Source code for ex_fuzzy.rules
self.consequent_centroids_rules[ix] = self.consequent_centroids[consequent_ix]
-
-[docs]
- def inference(self, x: np.array) -> np.array:
+[docs] def inference(self, x: np.array) -> np.array:
'''
Computes the output of the t1 inference system.
@@ -1083,10 +1003,7 @@ Source code for ex_fuzzy.rules
return res
-
-
-[docs]
- def forward(self, x: np.array) -> np.array:
+[docs] def forward(self, x: np.array) -> np.array:
'''
Same as inference() in the t1 case.
@@ -1098,24 +1015,17 @@ Source code for ex_fuzzy.rules
return self.inference(x)
-
-
-[docs]
- def fuzzy_type(self) -> fs.FUZZY_SETS:
+[docs] def fuzzy_type(self) -> fs.FUZZY_SETS:
'''
Returns the correspoing type of the RuleBase using the enum type in the fuzzy_sets module.
:return: the corresponding fuzzy set type of the RuleBase.
'''
- return fs.FUZZY_SETS.t1
-
-
+ return fs.FUZZY_SETS.t1
-
-[docs]
-class MasterRuleBase():
+[docs]class MasterRuleBase():
'''
This Class encompasses a list of rule bases where each one corresponds to a different class.
'''
@@ -1140,19 +1050,14 @@ Source code for ex_fuzzy.rules
self.allow_unknown = allow_unknown
-
-[docs]
- def rename_cons(self, consequent_names: list[str]) -> None:
+[docs] def rename_cons(self, consequent_names: list[str]) -> None:
'''
Renames the consequents of the rule base.
'''
self.consequent_names = consequent_names
-
-
-[docs]
- def add_rule(self, rule: RuleSimple, consequent: int) -> None:
+[docs] def add_rule(self, rule: RuleSimple, consequent: int) -> None:
'''
Adds a rule to the rule base of the given consequent.
@@ -1160,12 +1065,9 @@ Source code for ex_fuzzy.rules
:param consequent: index of the rule base to add the rule.
'''
self.rule_bases[consequent].add_rule(rule)
-
-
-[docs]
- def get_consequents(self) -> list[int]:
+[docs] def get_consequents(self) -> list[int]:
'''
Returns a list with the consequents of each rule base.
@@ -1174,22 +1076,16 @@ Source code for ex_fuzzy.rules
return sum([[ix]*len(x) for ix, x in enumerate(self.rule_bases)], [])
-
-