psyke 0.10.2.dev10__py3-none-any.whl → 0.10.4.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of psyke might be problematic. Click here for more details.
- psyke/extraction/hypercubic/gridrex/__init__.py +2 -2
- psyke/genetic/fgin/__init__.py +74 -0
- psyke/genetic/gin/__init__.py +24 -22
- {psyke-0.10.2.dev10.dist-info → psyke-0.10.4.dev1.dist-info}/METADATA +1 -1
- {psyke-0.10.2.dev10.dist-info → psyke-0.10.4.dev1.dist-info}/RECORD +8 -7
- {psyke-0.10.2.dev10.dist-info → psyke-0.10.4.dev1.dist-info}/WHEEL +0 -0
- {psyke-0.10.2.dev10.dist-info → psyke-0.10.4.dev1.dist-info}/licenses/LICENSE +0 -0
- {psyke-0.10.2.dev10.dist-info → psyke-0.10.4.dev1.dist-info}/top_level.txt +0 -0
|
@@ -5,12 +5,12 @@ from psyke.extraction.hypercubic.gridex import GridEx
|
|
|
5
5
|
|
|
6
6
|
class GridREx(GridEx):
|
|
7
7
|
"""
|
|
8
|
-
Explanator implementing GridREx algorithm.
|
|
8
|
+
Explanator implementing GridREx algorithm, doi:10.24963/kr.2022/57.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
def __init__(self, predictor, grid: Grid, min_examples: int, threshold: float, normalization,
|
|
12
12
|
seed=get_default_random_seed()):
|
|
13
13
|
super().__init__(predictor, grid, min_examples, threshold, Target.REGRESSION, None, normalization, seed)
|
|
14
14
|
|
|
15
|
-
def _default_cube(self) -> RegressionCube:
|
|
15
|
+
def _default_cube(self, dimensions=None) -> RegressionCube:
|
|
16
16
|
return RegressionCube()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
|
|
4
|
+
from psyke import Target
|
|
5
|
+
from psyke.genetic.gin import GIn
|
|
6
|
+
|
|
7
|
+
import skfuzzy as skf
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FGIn(GIn):
|
|
11
|
+
|
|
12
|
+
def __init__(self, train, valid, features, sigmas, slices, min_rules=1, poly=1, alpha=0.5, indpb=0.5, tournsize=3,
|
|
13
|
+
metric='R2', output=Target.REGRESSION, warm=False):
|
|
14
|
+
super().__init__(train, valid, features, sigmas, slices, min_rules, poly, alpha, indpb, tournsize,
|
|
15
|
+
metric, output, warm)
|
|
16
|
+
self.feature_to_idx = {f: i for i, f in enumerate(self.X.columns)}
|
|
17
|
+
|
|
18
|
+
def _evaluate(self, individual=None):
|
|
19
|
+
y_pred, valid_regions = self.__predict(individual or self.best, self.X if self.valid is None else self.valid[0])
|
|
20
|
+
if valid_regions < self.min_rules:
|
|
21
|
+
return -9999,
|
|
22
|
+
return self._score(self.y if self.valid is None else self.valid[1], y_pred),
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def __generate_membership(var, domain, thresholds, shape='tri'):
|
|
26
|
+
th = [var.min()] + [min(max(t, var.min()), var.max()) for t in thresholds] + [var.max()]
|
|
27
|
+
|
|
28
|
+
if shape == 'tri':
|
|
29
|
+
mid = [(x1 + x2) / 2 for x1, x2 in zip(th[:-1], th[1:])]
|
|
30
|
+
return [skf.trapmf(domain, [domain.min()] * 2 + mid[:2])] + \
|
|
31
|
+
[skf.trimf(domain, [x1, x2, x3]) for x1, x2, x3 in zip(mid[:-2], mid[1:-1], mid[2:])] + \
|
|
32
|
+
[skf.trapmf(domain, mid[-2:] + [domain.max()] * 2)]
|
|
33
|
+
if shape == 'trap':
|
|
34
|
+
beg = [None, domain.min()] + [(3 * x1 + x2) / 4 for x1, x2 in zip(th[1:-1], th[2:])] + [domain.max()]
|
|
35
|
+
end = [domain.min()] + [(x1 + 3 * x2) / 4 for x1, x2 in zip(th[:-2], th[1:-1])] + [domain.max()]
|
|
36
|
+
return [skf.trapmf(domain, [end[i - 1], beg[i], end[i], beg[i + 1]]) for i in range(1, len(th))]
|
|
37
|
+
raise ValueError('Supported shape values are only \'tri\' and \'trap\'')
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def __extend_domain(x, q_low=0.05, q_high=0.95, p=0.05, k_sigma=2.0, abs_min_margin=0.0):
|
|
41
|
+
ql, qh = np.quantile(x, [q_low, q_high])
|
|
42
|
+
margin = max(p * (qh - ql), k_sigma * np.std(x), abs_min_margin)
|
|
43
|
+
return np.array([ql - margin, qh + margin])
|
|
44
|
+
|
|
45
|
+
def __get_activations(self, x, functions_domains, valid_masks):
|
|
46
|
+
levels = [np.array([skf.interp_membership(domain, mf, x[index]) for mf in mfs])
|
|
47
|
+
for mfs, domain, index in functions_domains.values()]
|
|
48
|
+
return np.prod(np.meshgrid(*levels, indexing='ij'), axis=0).ravel()[valid_masks]
|
|
49
|
+
|
|
50
|
+
def __fuzzify(self, cuts):
|
|
51
|
+
cuts = dict(zip(self.features, cuts))
|
|
52
|
+
doms = {c: FGIn.__extend_domain(self.X[c]) for c in self.features}
|
|
53
|
+
return {c: (FGIn.__generate_membership(self.X[c], doms[c], cuts[c], 'trap'), doms[c],
|
|
54
|
+
self.feature_to_idx[c]) for c in self.features}
|
|
55
|
+
|
|
56
|
+
def __predict(self, individual=None, to_pred=None):
|
|
57
|
+
cuts = self._get_cuts(individual or self.best)
|
|
58
|
+
masks = np.array([self._region(to_pred, cuts) == r for r in range(np.prod([s + 1 for s in self.slices]))])
|
|
59
|
+
valid_masks = masks.sum(axis=1) >= 3
|
|
60
|
+
|
|
61
|
+
masks = [mask for mask in masks if mask.sum() >= 3]
|
|
62
|
+
functions_domains = self.__fuzzify(cuts)
|
|
63
|
+
|
|
64
|
+
pred = np.array([self._output_estimation(mask, to_pred) for mask in masks]).T
|
|
65
|
+
activations = np.array([self.__get_activations(x, functions_domains, valid_masks) for x in to_pred.values])
|
|
66
|
+
|
|
67
|
+
if self.output == Target.CLASSIFICATION:
|
|
68
|
+
classes, idx = np.unique(pred, return_inverse=True)
|
|
69
|
+
pred = classes[np.argmax(np.vstack([activations[:, idx == i].sum(axis=1) for i, c in enumerate(classes)]),
|
|
70
|
+
axis=0)]
|
|
71
|
+
else:
|
|
72
|
+
pred = (pred * activations).sum(axis=1)
|
|
73
|
+
|
|
74
|
+
return pd.DataFrame(pred, index=to_pred.index), len(masks)
|
psyke/genetic/gin/__init__.py
CHANGED
|
@@ -34,13 +34,13 @@ class GIn:
|
|
|
34
34
|
self.hof = None
|
|
35
35
|
self.best = None
|
|
36
36
|
|
|
37
|
-
self.
|
|
37
|
+
self.__setup(warm)
|
|
38
38
|
|
|
39
|
-
def
|
|
40
|
-
indices = [np.searchsorted(np.array(cut),
|
|
39
|
+
def _region(self, x, cuts):
|
|
40
|
+
indices = [np.searchsorted(np.array(cut), x[f].to_numpy(), side='right')
|
|
41
41
|
for cut, f in zip(cuts, self.features)]
|
|
42
42
|
|
|
43
|
-
regions = np.zeros(len(
|
|
43
|
+
regions = np.zeros(len(x), dtype=int)
|
|
44
44
|
multiplier = 1
|
|
45
45
|
for idx, n in zip(reversed(indices), reversed([len(cut) + 1 for cut in cuts])):
|
|
46
46
|
regions += idx * multiplier
|
|
@@ -48,7 +48,7 @@ class GIn:
|
|
|
48
48
|
|
|
49
49
|
return regions
|
|
50
50
|
|
|
51
|
-
def
|
|
51
|
+
def _output_estimation(self, mask, to_pred):
|
|
52
52
|
if self.output == Target.REGRESSION:
|
|
53
53
|
return LinearRegression().fit(self.poly.fit_transform(self.X)[mask], self.y[mask]).predict(
|
|
54
54
|
self.poly.fit_transform(to_pred))
|
|
@@ -56,9 +56,9 @@ class GIn:
|
|
|
56
56
|
return np.mean(self.y[mask])
|
|
57
57
|
if self.output == Target.CLASSIFICATION:
|
|
58
58
|
return mode(self.y[mask])
|
|
59
|
-
raise
|
|
59
|
+
raise ValueError('Supported outputs are Target.{REGRESSION, CONSTANT, CLASSIFICATION}')
|
|
60
60
|
|
|
61
|
-
def
|
|
61
|
+
def _score(self, true, pred):
|
|
62
62
|
if self.metric == 'R2':
|
|
63
63
|
return r2_score(true, pred)
|
|
64
64
|
if self.metric == 'MAE':
|
|
@@ -69,20 +69,22 @@ class GIn:
|
|
|
69
69
|
return f1_score(true, pred, average='weighted')
|
|
70
70
|
if self.metric == 'ACC':
|
|
71
71
|
return accuracy_score(true, pred)
|
|
72
|
-
raise
|
|
72
|
+
raise ValueError('Supported metrics are R2, MAE, MSE, F1, ACC')
|
|
73
73
|
|
|
74
74
|
def predict(self, to_pred):
|
|
75
75
|
return self.__predict(to_pred=to_pred)[0]
|
|
76
76
|
|
|
77
|
-
def
|
|
78
|
-
individual = individual or self.best
|
|
77
|
+
def _get_cuts(self, individual):
|
|
79
78
|
boundaries = np.cumsum([0] + list(self.slices))
|
|
80
|
-
|
|
79
|
+
return [sorted(individual[boundaries[i]:boundaries[i + 1]]) for i in range(len(self.slices))]
|
|
80
|
+
|
|
81
|
+
def __predict(self, individual=None, to_pred=None):
|
|
82
|
+
cuts = self._get_cuts(individual or self.best)
|
|
81
83
|
|
|
82
|
-
regions = self.
|
|
83
|
-
regionsT = self.
|
|
84
|
+
regions = self._region(to_pred, cuts)
|
|
85
|
+
regionsT = self._region(self.X, cuts)
|
|
84
86
|
|
|
85
|
-
|
|
87
|
+
pred = np.empty(len(to_pred), dtype=f'U{self.y.str.len().max()}') if self.output == Target.CLASSIFICATION \
|
|
86
88
|
else np.zeros(len(to_pred))
|
|
87
89
|
valid_regions = 0
|
|
88
90
|
|
|
@@ -91,20 +93,20 @@ class GIn:
|
|
|
91
93
|
maskT = regionsT == r
|
|
92
94
|
if min(mask.sum(), maskT.sum()) < 3:
|
|
93
95
|
if self.output != Target.CLASSIFICATION:
|
|
94
|
-
|
|
96
|
+
pred[mask] = np.mean(self.y)
|
|
95
97
|
continue
|
|
96
|
-
|
|
98
|
+
pred[mask] = self._output_estimation(maskT, to_pred[mask])
|
|
97
99
|
valid_regions += 1
|
|
98
100
|
|
|
99
|
-
return
|
|
101
|
+
return pred, valid_regions
|
|
100
102
|
|
|
101
|
-
def
|
|
103
|
+
def _evaluate(self, individual=None):
|
|
102
104
|
y_pred, valid_regions = self.__predict(individual or self.best, self.X if self.valid is None else self.valid[0])
|
|
103
105
|
if valid_regions < self.min_rules:
|
|
104
106
|
return -9999,
|
|
105
|
-
return self.
|
|
107
|
+
return self._score(self.y if self.valid is None else self.valid[1], y_pred),
|
|
106
108
|
|
|
107
|
-
def
|
|
109
|
+
def __setup(self, warm=False):
|
|
108
110
|
if not warm:
|
|
109
111
|
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
|
|
110
112
|
creator.create("Individual", list, fitness=creator.FitnessMax)
|
|
@@ -123,7 +125,7 @@ class GIn:
|
|
|
123
125
|
self.toolbox.register("mutate", tools.mutGaussian, indpb=self.indpb, mu=0,
|
|
124
126
|
sigma=sum([[sig] * s for sig, s in zip(self.sigmas, self.slices)], []))
|
|
125
127
|
self.toolbox.register("select", tools.selTournament, tournsize=self.tournsize)
|
|
126
|
-
self.toolbox.register("evaluate", self.
|
|
128
|
+
self.toolbox.register("evaluate", self._evaluate)
|
|
127
129
|
|
|
128
130
|
self.stats = tools.Statistics(lambda ind: ind.fitness.values[0])
|
|
129
131
|
self.stats.register("avg", np.mean)
|
|
@@ -139,4 +141,4 @@ class GIn:
|
|
|
139
141
|
result, log = algorithms.eaSimple(pop, self.toolbox, cxpb=cxpb, mutpb=mutpb, ngen=n_gen,
|
|
140
142
|
stats=self.stats, halloffame=self.hof, verbose=False)
|
|
141
143
|
self.best = tools.selBest(pop, 1)[0]
|
|
142
|
-
return self.best, self.
|
|
144
|
+
return self.best, self._evaluate()[0], result, log
|
|
@@ -18,7 +18,7 @@ psyke/extraction/hypercubic/creepy/__init__.py,sha256=x8a1ftoYHixGpiDfM3u-6QBEDY
|
|
|
18
18
|
psyke/extraction/hypercubic/divine/__init__.py,sha256=ClO8CITKKXoo7nhlBJagR1yAachsxLHYQlqggl-9eGE,3665
|
|
19
19
|
psyke/extraction/hypercubic/ginger/__init__.py,sha256=7G8H07d6PyIfRjcQjTQbe1WhwpHce_ky7Sy61JoxbqA,4713
|
|
20
20
|
psyke/extraction/hypercubic/gridex/__init__.py,sha256=tPPLGRJ-7fCt-OB-qq6W7EV0hqEuQVUGlXs2yyABo98,3161
|
|
21
|
-
psyke/extraction/hypercubic/gridrex/__init__.py,sha256=
|
|
21
|
+
psyke/extraction/hypercubic/gridrex/__init__.py,sha256=4ToRAI1ugNC8FyvE6U2Kne3AEXfXmM5nR3PfypqKmzs,637
|
|
22
22
|
psyke/extraction/hypercubic/hex/__init__.py,sha256=553AZjOT9thfqDGtVDI6WtgYNex2Y6dg53cEyuf7Q80,4805
|
|
23
23
|
psyke/extraction/hypercubic/iter/__init__.py,sha256=bb0neiPcNlyyr-OUUjgw4vdkehnAsoyJzVJ88jAHtQ8,10233
|
|
24
24
|
psyke/extraction/real/__init__.py,sha256=zAE_syurDqmFiopD5oLeIs9bROiuXy06wxoHmVqAhCA,5836
|
|
@@ -26,7 +26,8 @@ psyke/extraction/real/utils.py,sha256=4NNL15Eu7cmkG9b29GBP6CKgMTV1cmiJVS0k1MbWpI
|
|
|
26
26
|
psyke/extraction/trepan/__init__.py,sha256=H8F_wpFLPcfyx2tgOOno8FwUomxfVxVl1vxlb0ClP1g,6931
|
|
27
27
|
psyke/extraction/trepan/utils.py,sha256=iSUJ1ooNQT_VO1KfBZuIUeUsyUbGdQf_pSEE87vMeQg,2320
|
|
28
28
|
psyke/genetic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
|
-
psyke/genetic/
|
|
29
|
+
psyke/genetic/fgin/__init__.py,sha256=iGRJ_7SlC1AwgAhW1_h6V_yp8jjTvAePGp9ftAKkuSQ,3772
|
|
30
|
+
psyke/genetic/gin/__init__.py,sha256=5zQT7iSkBJ9vTre0T2liv4cU1ZcXBZtJx8_fzrQlEvI,5820
|
|
30
31
|
psyke/schema/__init__.py,sha256=axv4ejZY0ItUwrC9IXb_yAhaQL5f1vwvXXmaIAHJmt0,26063
|
|
31
32
|
psyke/tuning/__init__.py,sha256=yd_ForFmHeYbtRXltY1fOa-mPJvpE6ijzg50M_8Sdxw,3649
|
|
32
33
|
psyke/tuning/crash/__init__.py,sha256=zIHEF75EFy_mRIieqzP04qKLG3GLsSc_mYZHpPfkzxU,2623
|
|
@@ -38,8 +39,8 @@ psyke/utils/logic.py,sha256=ioP25WMTYNYEzaRDNDe3kGNWqZ6DA_63t19d-ky_2kM,12227
|
|
|
38
39
|
psyke/utils/metrics.py,sha256=Oo5BOonOSfo0qYsXWT5dmypZ7jiStByFC2MKEU0uMHg,2250
|
|
39
40
|
psyke/utils/plot.py,sha256=dE8JJ6tQ0Ezosid-r2jqAisREjFe5LqExRzsVi5Ns-c,7785
|
|
40
41
|
psyke/utils/sorted.py,sha256=C3CPW2JisND30BRk5c1sAAHs3Lb_wsRB2qZrYFuRnfM,678
|
|
41
|
-
psyke-0.10.
|
|
42
|
-
psyke-0.10.
|
|
43
|
-
psyke-0.10.
|
|
44
|
-
psyke-0.10.
|
|
45
|
-
psyke-0.10.
|
|
42
|
+
psyke-0.10.4.dev1.dist-info/licenses/LICENSE,sha256=G3mPaubObvkBXbsgTTeYGLk_pNEW8tc7HZr4u_wLEpU,11398
|
|
43
|
+
psyke-0.10.4.dev1.dist-info/METADATA,sha256=TzMrtQX0iXkTWLHIViten-8okQ_PK_3Ez1HUdSQsrNk,8395
|
|
44
|
+
psyke-0.10.4.dev1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
45
|
+
psyke-0.10.4.dev1.dist-info/top_level.txt,sha256=q1HglxOqqoIRukFtyis_ZNHczZg4gANRUPWkD7HAUTU,6
|
|
46
|
+
psyke-0.10.4.dev1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|