rulelearn-ripper-interactive 0.2.3__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.
- rulelearn_ri/__init__.py +6 -0
- rulelearn_ri/algorithms/__init__.py +0 -0
- rulelearn_ri/algorithms/r2n/__init__.py +0 -0
- rulelearn_ri/algorithms/r2n/base.py +28 -0
- rulelearn_ri/algorithms/r2n/functions.py +25 -0
- rulelearn_ri/algorithms/r2n/mask.py +58 -0
- rulelearn_ri/algorithms/r2n/preprocessing_layers.py +192 -0
- rulelearn_ri/algorithms/r2n/r2n_algo.py +287 -0
- rulelearn_ri/algorithms/r2n/simple_rulenet.py +75 -0
- rulelearn_ri/algorithms/r2n/training.py +34 -0
- rulelearn_ri/algorithms/r2n/utilities.py +117 -0
- rulelearn_ri/algorithms/rbm/__init__.py +0 -0
- rulelearn_ri/algorithms/rbm/boolean_rule_cg.py +65 -0
- rulelearn_ri/algorithms/rbm/rbm/BRCG.py +69 -0
- rulelearn_ri/algorithms/rbm/rbm/GLRM.py +98 -0
- rulelearn_ri/algorithms/rbm/rbm/__init__.py +6 -0
- rulelearn_ri/algorithms/rbm/rbm/beam_search.py +302 -0
- rulelearn_ri/algorithms/rbm/rbm/boolean_rule_cg.py +245 -0
- rulelearn_ri/algorithms/rbm/rbm/features.py +544 -0
- rulelearn_ri/algorithms/rbm/rbm/linear_regression.py +393 -0
- rulelearn_ri/algorithms/rbm/rbm/logistic_regression.py +447 -0
- rulelearn_ri/algorithms/rbm/utils.py +13 -0
- rulelearn_ri/algorithms/ripper/__init__.py +10 -0
- rulelearn_ri/algorithms/ripper/base.py +81 -0
- rulelearn_ri/algorithms/ripper/binding.py +541 -0
- rulelearn_ri/algorithms/ripper/mdl.py +97 -0
- rulelearn_ri/algorithms/ripper/pruning.py +212 -0
- rulelearn_ri/algorithms/ripper/ripper.py +600 -0
- rulelearn_ri/algorithms/ripper/ripper_ruleset_generator.py +26 -0
- rulelearn_ri/algorithms/ripper/single_rule.py +425 -0
- rulelearn_ri/dise.py +64 -0
- rulelearn_ri/trxf/__init__.py +0 -0
- rulelearn_ri/trxf/classifier/__init__.py +0 -0
- rulelearn_ri/trxf/classifier/ruleset_classifier.py +153 -0
- rulelearn_ri/trxf/core/__init__.py +5 -0
- rulelearn_ri/trxf/core/boolean_evaluator.py +15 -0
- rulelearn_ri/trxf/core/conjunction.py +50 -0
- rulelearn_ri/trxf/core/dnf_ruleset.py +64 -0
- rulelearn_ri/trxf/core/feature.py +155 -0
- rulelearn_ri/trxf/core/predicate.py +98 -0
- rulelearn_ri/trxf/core/ruleset_generator.py +25 -0
- rulelearn_ri/trxf/core/utils.py +40 -0
- rulelearn_ri/trxf/metrics.py +98 -0
- rulelearn_ri/trxf/pmml_export/__init__.py +4 -0
- rulelearn_ri/trxf/pmml_export/__version__.py +1 -0
- rulelearn_ri/trxf/pmml_export/models/__init__.py +24 -0
- rulelearn_ri/trxf/pmml_export/models/attribute.py +24 -0
- rulelearn_ri/trxf/pmml_export/models/characteristics.py +20 -0
- rulelearn_ri/trxf/pmml_export/models/data_dictionary.py +53 -0
- rulelearn_ri/trxf/pmml_export/models/mining_schema.py +25 -0
- rulelearn_ri/trxf/pmml_export/models/pmml_ruleset_model.py +16 -0
- rulelearn_ri/trxf/pmml_export/models/predicate.py +42 -0
- rulelearn_ri/trxf/pmml_export/models/rule.py +26 -0
- rulelearn_ri/trxf/pmml_export/models/ruleset.py +28 -0
- rulelearn_ri/trxf/pmml_export/models/ruleset_model.py +16 -0
- rulelearn_ri/trxf/pmml_export/models/scorecard.py +35 -0
- rulelearn_ri/trxf/pmml_export/pmml_exporter.py +18 -0
- rulelearn_ri/trxf/pmml_export/reader/__init__.py +2 -0
- rulelearn_ri/trxf/pmml_export/reader/abstract_reader.py +16 -0
- rulelearn_ri/trxf/pmml_export/reader/trxf_reader.py +113 -0
- rulelearn_ri/trxf/pmml_export/serializer/__init__.py +2 -0
- rulelearn_ri/trxf/pmml_export/serializer/abstract_serializer.py +11 -0
- rulelearn_ri/trxf/pmml_export/serializer/nyoka_serializer.py +162 -0
- rulelearn_ri/trxf/scorecard/__init__.py +3 -0
- rulelearn_ri/trxf/scorecard/bins.py +253 -0
- rulelearn_ri/trxf/scorecard/partition.py +71 -0
- rulelearn_ri/trxf/scorecard/scorecard.py +78 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/METADATA +164 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/RECORD +73 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/WHEEL +5 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/licenses/LICENSE +201 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/licenses/NOTICE +11 -0
- rulelearn_ripper_interactive-0.2.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import numba as nb
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
LE = 0
|
|
6
|
+
GE = 1
|
|
7
|
+
EQ = 2
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# # jitclass is not optimized for interpreter yet
|
|
11
|
+
# @nb.jitclass(
|
|
12
|
+
# [
|
|
13
|
+
# ('name', nb.int64),
|
|
14
|
+
# ('op', nb.uint8),
|
|
15
|
+
# ('num_val', nb.double),
|
|
16
|
+
# ('nom_val', nb.int64)
|
|
17
|
+
# ]
|
|
18
|
+
# )
|
|
19
|
+
class Literal(object):
|
|
20
|
+
"""
|
|
21
|
+
A literal is an atomic formula (atom). In rule induction, the negation of an atom needs
|
|
22
|
+
to be explicitly added. For example, ‘credit score >= 200’ and ‘credit score < 200’ (to solve
|
|
23
|
+
some issues related to float number, the complement of >= is sometimes written as <=) need to
|
|
24
|
+
be added at the same time
|
|
25
|
+
|
|
26
|
+
name : int64_t
|
|
27
|
+
Feature name for literal (encoded)
|
|
28
|
+
op : uint8_t
|
|
29
|
+
Operator for literal (encoded)
|
|
30
|
+
num_val : double
|
|
31
|
+
Numerical feature value for literal (encoded)
|
|
32
|
+
nom_val : int64_t
|
|
33
|
+
Nominal feature value for literal
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
name,
|
|
39
|
+
op,
|
|
40
|
+
num_val,
|
|
41
|
+
nom_val
|
|
42
|
+
):
|
|
43
|
+
self.name = name
|
|
44
|
+
self.op = op
|
|
45
|
+
self.num_val = num_val
|
|
46
|
+
self.nom_val = nom_val
|
|
47
|
+
|
|
48
|
+
def __repr__(self):
|
|
49
|
+
return '_'.join([
|
|
50
|
+
str(self.name),
|
|
51
|
+
str(self.op),
|
|
52
|
+
str(self.num_val),
|
|
53
|
+
str(self.nom_val)])
|
|
54
|
+
|
|
55
|
+
def __str__(self):
|
|
56
|
+
return self.__repr__()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _filter_contradicted_instances(pos, neg):
|
|
60
|
+
"""
|
|
61
|
+
Delete common instances in pos and neg
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
pos: const double[::1, :]
|
|
66
|
+
neg: const double[::1, :]
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
const double[::1, :]
|
|
71
|
+
New neg
|
|
72
|
+
"""
|
|
73
|
+
feature_shape = np.shape(neg)[1]
|
|
74
|
+
pos_set = set(map(tuple, pos))
|
|
75
|
+
neg_set = set(map(tuple, neg))
|
|
76
|
+
filtered_neg_set = neg_set.difference(pos_set)
|
|
77
|
+
return neg if len(filtered_neg_set) == len(neg_set) else np.array(list(map(list, filtered_neg_set))).reshape(-1, feature_shape)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@nb.njit(
|
|
81
|
+
'double(int64, int64, int64, int64)',
|
|
82
|
+
nogil=True,
|
|
83
|
+
cache=True,
|
|
84
|
+
error_model='numpy'
|
|
85
|
+
)
|
|
86
|
+
def _gain(
|
|
87
|
+
p0,
|
|
88
|
+
p1,
|
|
89
|
+
n0,
|
|
90
|
+
n1
|
|
91
|
+
):
|
|
92
|
+
"""
|
|
93
|
+
Calculation of p-foil gain for IREP*
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
p0 : int64
|
|
98
|
+
Total positive instances
|
|
99
|
+
p1 : int64
|
|
100
|
+
Positive instances in the next iteration
|
|
101
|
+
n0 : int64
|
|
102
|
+
Total negative instances
|
|
103
|
+
n1 : int64
|
|
104
|
+
Negative instances in the next iteration
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
double
|
|
109
|
+
The p-foil gian
|
|
110
|
+
"""
|
|
111
|
+
if p1 == 0:
|
|
112
|
+
return 0
|
|
113
|
+
else:
|
|
114
|
+
return p1 * (np.log2(p1 / (p1 + float(n1))) - np.log2(p0 / (p0 + float(n0))))
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@nb.njit(
|
|
118
|
+
[
|
|
119
|
+
'int64(double[::1, :], int64, uint8, double, int64)',
|
|
120
|
+
'int64(double[:, ::1], int64, uint8, double, int64)'
|
|
121
|
+
],
|
|
122
|
+
nogil=True,
|
|
123
|
+
cache=True,
|
|
124
|
+
error_model='numpy'
|
|
125
|
+
)
|
|
126
|
+
def _count_bound_literal(
|
|
127
|
+
data,
|
|
128
|
+
name,
|
|
129
|
+
op,
|
|
130
|
+
num_val,
|
|
131
|
+
nom_val,
|
|
132
|
+
):
|
|
133
|
+
"""
|
|
134
|
+
Count the number of instances that are covered by the literal
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
data : const double[::1, :]
|
|
139
|
+
Input instances
|
|
140
|
+
name : int64_t
|
|
141
|
+
Feature name for literal
|
|
142
|
+
op : uint8_t
|
|
143
|
+
Operator for literal
|
|
144
|
+
num_val : double
|
|
145
|
+
Numerical feature value for literal
|
|
146
|
+
nom_val : int64_t
|
|
147
|
+
Nominal feature value for literal
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
uint64_t
|
|
152
|
+
The number of instances which are covered by the literal
|
|
153
|
+
"""
|
|
154
|
+
result_count = 0
|
|
155
|
+
if op == LE:
|
|
156
|
+
for i in range(len(data)):
|
|
157
|
+
result_count += data[i, name] <= num_val
|
|
158
|
+
elif op == GE:
|
|
159
|
+
for i in range(len(data)):
|
|
160
|
+
result_count += data[i, name] >= num_val
|
|
161
|
+
elif op == EQ:
|
|
162
|
+
for i in range(len(data)):
|
|
163
|
+
result_count += int(data[i, name]) == nom_val
|
|
164
|
+
else:
|
|
165
|
+
raise RuntimeError('Unknown operator')
|
|
166
|
+
return result_count
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@nb.njit(
|
|
170
|
+
[
|
|
171
|
+
'int64(double[::1, :], double[::1, :], int64[::1], uint8[::1], double[::1], int64[::1])',
|
|
172
|
+
'int64(double[::1, :], double[:, ::1], int64[::1], uint8[::1], double[::1], int64[::1])',
|
|
173
|
+
'int64(double[:, ::1], double[::1, :], int64[::1], uint8[::1], double[::1], int64[::1])',
|
|
174
|
+
'int64(double[:, ::1], double[:, ::1], int64[::1], uint8[::1], double[::1], int64[::1])',
|
|
175
|
+
],
|
|
176
|
+
nogil=True,
|
|
177
|
+
parallel=True,
|
|
178
|
+
error_model='numpy'
|
|
179
|
+
)
|
|
180
|
+
def _find_best_literal(
|
|
181
|
+
pos,
|
|
182
|
+
neg,
|
|
183
|
+
name_vec,
|
|
184
|
+
op_vec,
|
|
185
|
+
num_val_vec,
|
|
186
|
+
nom_val_vec
|
|
187
|
+
):
|
|
188
|
+
"""
|
|
189
|
+
Find the best literal for OpenMP based IREP*
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
pos : const double[::1, :]
|
|
194
|
+
Positive instances
|
|
195
|
+
neg : const double[::1, :]
|
|
196
|
+
Negative instances
|
|
197
|
+
name_vec : const int64_t[::1]
|
|
198
|
+
Feature names for literals
|
|
199
|
+
op_vec : const uint8_t[::1]
|
|
200
|
+
Operators for literals
|
|
201
|
+
num_val_vec : const double[::1]
|
|
202
|
+
Numerical feature values for literals
|
|
203
|
+
nom_val_vec : const int64_t[::1]
|
|
204
|
+
Nominal feature values for literals
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
uint64_t
|
|
209
|
+
The index of the best literal
|
|
210
|
+
"""
|
|
211
|
+
p = len(pos)
|
|
212
|
+
n = len(neg)
|
|
213
|
+
gain_list = np.empty(len(name_vec), dtype=np.float64)
|
|
214
|
+
|
|
215
|
+
for i in nb.prange(len(name_vec)):
|
|
216
|
+
|
|
217
|
+
name = name_vec[i]
|
|
218
|
+
op = op_vec[i]
|
|
219
|
+
num_val = num_val_vec[i]
|
|
220
|
+
nom_val = nom_val_vec[i]
|
|
221
|
+
|
|
222
|
+
gain_list[i] = _gain(
|
|
223
|
+
p,
|
|
224
|
+
_count_bound_literal(pos, name, op, num_val, nom_val),
|
|
225
|
+
n,
|
|
226
|
+
_count_bound_literal(neg, name, op, num_val, nom_val)
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
return np.argmax(gain_list)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@nb.njit(
|
|
233
|
+
[
|
|
234
|
+
'boolean[::1](double[::1, :], int64, uint8, double, int64)',
|
|
235
|
+
'boolean[::1](double[:, ::1], int64, uint8, double, int64)',
|
|
236
|
+
],
|
|
237
|
+
nogil=True,
|
|
238
|
+
cache=True,
|
|
239
|
+
error_model='numpy'
|
|
240
|
+
)
|
|
241
|
+
def bind_literal_index(
|
|
242
|
+
data,
|
|
243
|
+
name,
|
|
244
|
+
op,
|
|
245
|
+
num_val,
|
|
246
|
+
nom_val,
|
|
247
|
+
):
|
|
248
|
+
"""
|
|
249
|
+
Find instances that are covered by the literal
|
|
250
|
+
|
|
251
|
+
Parameters
|
|
252
|
+
----------
|
|
253
|
+
data : const double[::1, :]
|
|
254
|
+
Input instances
|
|
255
|
+
name : int64_t
|
|
256
|
+
Feature name for literal
|
|
257
|
+
op : uint8_t
|
|
258
|
+
Operator for literal
|
|
259
|
+
num_val : double
|
|
260
|
+
Numerical feature value for literal
|
|
261
|
+
nom_val : int64_t
|
|
262
|
+
Nominal feature value for literal
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
uint64_t
|
|
267
|
+
Instances which are covered by the literal
|
|
268
|
+
"""
|
|
269
|
+
result = np.empty(len(data), dtype=np.bool_)
|
|
270
|
+
if op == LE:
|
|
271
|
+
for i in range(len(data)):
|
|
272
|
+
result[i] = data[i, name] <= num_val
|
|
273
|
+
elif op == GE:
|
|
274
|
+
for i in range(len(data)):
|
|
275
|
+
result[i] = data[i, name] >= num_val
|
|
276
|
+
elif op == EQ:
|
|
277
|
+
for i in range(len(data)):
|
|
278
|
+
result[i] = int(data[i, name]) == nom_val
|
|
279
|
+
else:
|
|
280
|
+
raise RuntimeError('Unknown operator')
|
|
281
|
+
return result
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@nb.njit(
|
|
285
|
+
[
|
|
286
|
+
'double[:,::1](double[::1, :], int64, uint8, double, int64)',
|
|
287
|
+
'double[:,::1](double[:, ::1], int64, uint8, double, int64)'
|
|
288
|
+
],
|
|
289
|
+
nogil=True,
|
|
290
|
+
cache=True,
|
|
291
|
+
error_model='numpy'
|
|
292
|
+
)
|
|
293
|
+
def _bind_literal(
|
|
294
|
+
data,
|
|
295
|
+
name,
|
|
296
|
+
op,
|
|
297
|
+
num_val,
|
|
298
|
+
nom_val,
|
|
299
|
+
):
|
|
300
|
+
"""
|
|
301
|
+
Find instances that are covered by the literal
|
|
302
|
+
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
data : const double[::1, :]
|
|
306
|
+
Input instances
|
|
307
|
+
name : int64_t
|
|
308
|
+
Feature name for literal
|
|
309
|
+
op : uint8_t
|
|
310
|
+
Operator for literal
|
|
311
|
+
num_val : double
|
|
312
|
+
Numerical feature value for literal
|
|
313
|
+
nom_val : int64_t
|
|
314
|
+
Nominal feature value for literal
|
|
315
|
+
|
|
316
|
+
Returns
|
|
317
|
+
-------
|
|
318
|
+
ndarray
|
|
319
|
+
Instances that are covered by the literal
|
|
320
|
+
"""
|
|
321
|
+
return data[bind_literal_index(data, name, op, num_val, nom_val)]
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _bind_rule_index(
|
|
325
|
+
data,
|
|
326
|
+
rule
|
|
327
|
+
):
|
|
328
|
+
"""
|
|
329
|
+
Find instances that are covered by the rule
|
|
330
|
+
|
|
331
|
+
Parameters
|
|
332
|
+
----------
|
|
333
|
+
data : const double[::1, :]
|
|
334
|
+
Input instances
|
|
335
|
+
rule : list
|
|
336
|
+
Input rule
|
|
337
|
+
|
|
338
|
+
Returns
|
|
339
|
+
-------
|
|
340
|
+
ndarray
|
|
341
|
+
Boolean index array for instances that are covered by the rule
|
|
342
|
+
"""
|
|
343
|
+
index = np.full(len(data), True)
|
|
344
|
+
for literal in rule:
|
|
345
|
+
index = index & bind_literal_index(data, literal.name, literal.op, literal.num_val, literal.nom_val)
|
|
346
|
+
return index
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def __unbound_rule_index(
|
|
350
|
+
data,
|
|
351
|
+
rule,
|
|
352
|
+
):
|
|
353
|
+
"""
|
|
354
|
+
Find instances that are not covered by the rule
|
|
355
|
+
|
|
356
|
+
Parameters
|
|
357
|
+
----------
|
|
358
|
+
data : const double[::1, :]
|
|
359
|
+
Input instances
|
|
360
|
+
rule : list
|
|
361
|
+
Input rule
|
|
362
|
+
|
|
363
|
+
Returns
|
|
364
|
+
-------
|
|
365
|
+
ndarray
|
|
366
|
+
Boolean index array for instances that are not covered by the rule
|
|
367
|
+
"""
|
|
368
|
+
return ~_bind_rule_index(data, rule)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def unbound_rule_list_index(
|
|
372
|
+
data,
|
|
373
|
+
rules
|
|
374
|
+
):
|
|
375
|
+
"""
|
|
376
|
+
Find instances that are not covered by the rule list
|
|
377
|
+
|
|
378
|
+
Parameters
|
|
379
|
+
----------
|
|
380
|
+
data : const double[::1, :]
|
|
381
|
+
Input instances
|
|
382
|
+
rules : list
|
|
383
|
+
Input rule list
|
|
384
|
+
|
|
385
|
+
Returns
|
|
386
|
+
-------
|
|
387
|
+
ndarray
|
|
388
|
+
Boolean index array for instances that are not covered by the rule list
|
|
389
|
+
"""
|
|
390
|
+
index = np.full(len(data), True)
|
|
391
|
+
for rule in rules:
|
|
392
|
+
index = index & __unbound_rule_index(data, rule)
|
|
393
|
+
return index
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _bind_rule(
|
|
397
|
+
data,
|
|
398
|
+
rule
|
|
399
|
+
):
|
|
400
|
+
"""
|
|
401
|
+
Find instances that are covered by the rule
|
|
402
|
+
|
|
403
|
+
Parameters
|
|
404
|
+
----------
|
|
405
|
+
data : const double[::1, :]
|
|
406
|
+
Input instances
|
|
407
|
+
rule : list
|
|
408
|
+
Input rule
|
|
409
|
+
|
|
410
|
+
Returns
|
|
411
|
+
-------
|
|
412
|
+
ndarray
|
|
413
|
+
Instances that are covered by the rule
|
|
414
|
+
"""
|
|
415
|
+
return data[_bind_rule_index(data, rule)]
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _unbound(
|
|
419
|
+
data,
|
|
420
|
+
rule
|
|
421
|
+
):
|
|
422
|
+
"""
|
|
423
|
+
Find instances that are not covered by the rule
|
|
424
|
+
|
|
425
|
+
Parameters
|
|
426
|
+
----------
|
|
427
|
+
data : const double[::1, :]
|
|
428
|
+
Input instances
|
|
429
|
+
rule : list
|
|
430
|
+
Input rule
|
|
431
|
+
|
|
432
|
+
Returns
|
|
433
|
+
-------
|
|
434
|
+
ndarray
|
|
435
|
+
Instances that are not covered by the rule
|
|
436
|
+
"""
|
|
437
|
+
return data[__unbound_rule_index(data, rule)]
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def _unbound_rule_list(
|
|
441
|
+
data,
|
|
442
|
+
rules
|
|
443
|
+
):
|
|
444
|
+
"""
|
|
445
|
+
Find instances that are not covered by the rule list
|
|
446
|
+
|
|
447
|
+
Parameters
|
|
448
|
+
----------
|
|
449
|
+
data : const double[::1, :]
|
|
450
|
+
Input instances
|
|
451
|
+
rules : list
|
|
452
|
+
Input rule list
|
|
453
|
+
|
|
454
|
+
Returns
|
|
455
|
+
-------
|
|
456
|
+
ndarray
|
|
457
|
+
Instances that are not covered by the rule list
|
|
458
|
+
"""
|
|
459
|
+
return data[unbound_rule_list_index(data, rules)]
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _rule_predict(
|
|
463
|
+
data,
|
|
464
|
+
rule
|
|
465
|
+
):
|
|
466
|
+
"""
|
|
467
|
+
Make a decision using input data and rule
|
|
468
|
+
|
|
469
|
+
Parameters
|
|
470
|
+
----------
|
|
471
|
+
data : const double[::1, :]
|
|
472
|
+
Input instances
|
|
473
|
+
rule : list
|
|
474
|
+
Input rule
|
|
475
|
+
|
|
476
|
+
Returns
|
|
477
|
+
-------
|
|
478
|
+
ndarray
|
|
479
|
+
Predictions
|
|
480
|
+
"""
|
|
481
|
+
if len(rule) == 0:
|
|
482
|
+
return np.full(len(data), False)
|
|
483
|
+
else:
|
|
484
|
+
return _bind_rule_index(data, rule)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _rule_list_predict(
|
|
488
|
+
data,
|
|
489
|
+
rules
|
|
490
|
+
):
|
|
491
|
+
"""
|
|
492
|
+
Make a decision based on input data and rule list
|
|
493
|
+
|
|
494
|
+
Parameters
|
|
495
|
+
----------
|
|
496
|
+
data : const double[::1, :]
|
|
497
|
+
Input instances
|
|
498
|
+
rules : list
|
|
499
|
+
Input rule list
|
|
500
|
+
|
|
501
|
+
Returns
|
|
502
|
+
-------
|
|
503
|
+
ndarray
|
|
504
|
+
Predictions
|
|
505
|
+
"""
|
|
506
|
+
if len(rules) == 0:
|
|
507
|
+
return np.full(len(data), False)
|
|
508
|
+
else:
|
|
509
|
+
return ~unbound_rule_list_index(data, rules)
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def append_literal(
|
|
513
|
+
rule,
|
|
514
|
+
literal
|
|
515
|
+
):
|
|
516
|
+
"""
|
|
517
|
+
Append a literal to a rule. It will delete unnecessary literals in that rule after append
|
|
518
|
+
|
|
519
|
+
Parameters
|
|
520
|
+
----------
|
|
521
|
+
rule : list
|
|
522
|
+
Target rule
|
|
523
|
+
literal : Literal
|
|
524
|
+
New literal
|
|
525
|
+
"""
|
|
526
|
+
# nominal variable will not be added twice
|
|
527
|
+
if literal.op != EQ:
|
|
528
|
+
# start from the last condition
|
|
529
|
+
for i in range(len(rule) - 1, -1, -1):
|
|
530
|
+
# delete redundant conditions
|
|
531
|
+
if rule[i].name == literal.name:
|
|
532
|
+
if rule[i].op == literal.op:
|
|
533
|
+
if literal.op == LE:
|
|
534
|
+
if literal.num_val < rule[i].num_val:
|
|
535
|
+
del rule[i]
|
|
536
|
+
else:
|
|
537
|
+
if literal.num_val > rule[i].num_val:
|
|
538
|
+
del rule[i]
|
|
539
|
+
# add condition to the rule
|
|
540
|
+
rule.append(literal)
|
|
541
|
+
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import numba as nb
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from rulelearn_ri.algorithms.ripper.binding import _rule_list_predict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@nb.njit(
|
|
8
|
+
'double(int64, int64)',
|
|
9
|
+
nogil=True,
|
|
10
|
+
cache=True,
|
|
11
|
+
error_model='numpy'
|
|
12
|
+
)
|
|
13
|
+
def __theorem_length(
|
|
14
|
+
k,
|
|
15
|
+
n
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Calculate the MDL for a rule
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
k: uint64_t
|
|
23
|
+
Literal count for theorem
|
|
24
|
+
n : uint64_t
|
|
25
|
+
Total literal count
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
double
|
|
30
|
+
The MDL for a rule
|
|
31
|
+
"""
|
|
32
|
+
if k == 0:
|
|
33
|
+
return 0.0
|
|
34
|
+
p = k / float(n)
|
|
35
|
+
return np.log2(k) + k * np.log2(1 / p) + (n - k) * np.log2(1 / (1 - p))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@nb.njit(
|
|
39
|
+
'double(int64, int64)',
|
|
40
|
+
nogil=True,
|
|
41
|
+
cache=True,
|
|
42
|
+
error_model='numpy'
|
|
43
|
+
)
|
|
44
|
+
def __msg_length_uniform(
|
|
45
|
+
e,
|
|
46
|
+
d
|
|
47
|
+
):
|
|
48
|
+
"""
|
|
49
|
+
Calculate the MDL a data set
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
e: uint64_t
|
|
54
|
+
Error count
|
|
55
|
+
d : uint64_t
|
|
56
|
+
Total number of instances
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
double
|
|
61
|
+
The MDL for data set
|
|
62
|
+
"""
|
|
63
|
+
if e == 0 or e == d:
|
|
64
|
+
return np.log2(d + 1)
|
|
65
|
+
return np.log2(d + 1) + e * (-np.log2(e / float(d))) + (d - e) * (-np.log2(1 - (e / float(d))))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _mdl(
|
|
69
|
+
pos,
|
|
70
|
+
neg,
|
|
71
|
+
rule_list,
|
|
72
|
+
n
|
|
73
|
+
):
|
|
74
|
+
"""
|
|
75
|
+
Calculate the MDL for a rule list and the training data from which the rule list is learned
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
pos : const double[::1, :]
|
|
80
|
+
Positive instances of training data
|
|
81
|
+
neg : const double[::1, :]
|
|
82
|
+
Negative instances of training data
|
|
83
|
+
rule_list : list
|
|
84
|
+
Learned rule list
|
|
85
|
+
n : uint64_t
|
|
86
|
+
Total literal count
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
double
|
|
91
|
+
The MDL for the rule list and the training data from which the rule list is learned
|
|
92
|
+
"""
|
|
93
|
+
e = np.count_nonzero(_rule_list_predict(neg, rule_list)) + \
|
|
94
|
+
len(pos) - np.count_nonzero(_rule_list_predict(pos, rule_list))
|
|
95
|
+
|
|
96
|
+
return 0.5 * np.sum([__theorem_length(len(rule), n) for rule in rule_list]) + \
|
|
97
|
+
__msg_length_uniform(e, len(pos) + len(neg))
|