action-rules 0.0.1__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.
action_rules/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import itertools
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Reduce candidates to required minimum number of stable or flexible attributes.
|
|
9
|
+
def reduce_candidates_min_attributes(K, actionable_attributes, stable_items_binding, min_stable_attributes,
|
|
10
|
+
flexible_items_binding, min_flexible_attributes):
|
|
11
|
+
# Reduce by min stable and flexible
|
|
12
|
+
number_of_stable_attributes = len(stable_items_binding) - (min_stable_attributes - K)
|
|
13
|
+
if K > min_stable_attributes:
|
|
14
|
+
number_of_flexible_attributes = len(flexible_items_binding) - (
|
|
15
|
+
min_flexible_attributes - actionable_attributes - 1)
|
|
16
|
+
else:
|
|
17
|
+
number_of_flexible_attributes = 0
|
|
18
|
+
reduced_stable_items_binding = {k: stable_items_binding[k] for k in
|
|
19
|
+
list(stable_items_binding.keys())[:number_of_stable_attributes]}
|
|
20
|
+
reduced_flexible_items_binding = {k: flexible_items_binding[k] for k in
|
|
21
|
+
list(flexible_items_binding.keys())[:number_of_flexible_attributes]}
|
|
22
|
+
return reduced_stable_items_binding, reduced_flexible_items_binding
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Check if itemset is in the stop list.
|
|
26
|
+
def in_stop_list(ar_prefix, stop_list):
|
|
27
|
+
if ar_prefix[-2:] in stop_list:
|
|
28
|
+
return True
|
|
29
|
+
if ar_prefix[1:] in stop_list:
|
|
30
|
+
stop_list.append(ar_prefix)
|
|
31
|
+
return True
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Generate candidates and count their support
|
|
36
|
+
# Node derived from stable attribute must simultaneously have sufficient support for the desired and undesired classes in the consequent.
|
|
37
|
+
# Group of nodes representing flexible attributes must simultaneously have required min. support for the desired and undesired classes.
|
|
38
|
+
def generate_candidates(ar_prefix, itemset_prefix, stable_items_binding, flexible_items_binding, undesired_mask,
|
|
39
|
+
desired_mask, actionable_attributes=0, item=0, stop_list=[], frames=None, undesired_state=0,
|
|
40
|
+
desired_state=1, stop_list_itemset=[], classification_rules=[], verbose=False):
|
|
41
|
+
K = len(itemset_prefix) + 1
|
|
42
|
+
reduced_stable_items_binding, reduced_flexible_items_binding = reduce_candidates_min_attributes(K,
|
|
43
|
+
actionable_attributes,
|
|
44
|
+
stable_items_binding,
|
|
45
|
+
min_stable_attributes,
|
|
46
|
+
flexible_items_binding,
|
|
47
|
+
min_flexible_attributes)
|
|
48
|
+
|
|
49
|
+
if undesired_mask is None:
|
|
50
|
+
undesired_frame = frames[undesired_state]
|
|
51
|
+
desired_frame = frames[desired_state]
|
|
52
|
+
else:
|
|
53
|
+
undesired_frame = frames[undesired_state].multiply(undesired_mask, axis="index")
|
|
54
|
+
desired_frame = frames[desired_state].multiply(desired_mask, axis="index")
|
|
55
|
+
|
|
56
|
+
stable_candidates = copy.deepcopy(stable_items_binding)
|
|
57
|
+
flexible_candidates = copy.deepcopy(flexible_items_binding)
|
|
58
|
+
|
|
59
|
+
new_branches = []
|
|
60
|
+
|
|
61
|
+
for attribute, items in reduced_stable_items_binding.items():
|
|
62
|
+
for item in items:
|
|
63
|
+
|
|
64
|
+
new_ar_prefix = ar_prefix + (item,)
|
|
65
|
+
if in_stop_list(new_ar_prefix, stop_list):
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
undesired_support = undesired_frame[item].sum()
|
|
69
|
+
desired_support = desired_frame[item].sum()
|
|
70
|
+
|
|
71
|
+
if verbose:
|
|
72
|
+
print('SUPPORT')
|
|
73
|
+
print(itemset_prefix + (item,))
|
|
74
|
+
print((undesired_support, desired_support))
|
|
75
|
+
|
|
76
|
+
if undesired_support < min_undesired_support or desired_support < min_desired_support:
|
|
77
|
+
stable_candidates[attribute].remove(item)
|
|
78
|
+
stop_list.append(new_ar_prefix)
|
|
79
|
+
else:
|
|
80
|
+
new_branches.append({'ar_prefix': new_ar_prefix,
|
|
81
|
+
'itemset_prefix': new_ar_prefix,
|
|
82
|
+
'item': item,
|
|
83
|
+
'undesired_mask': undesired_frame[item],
|
|
84
|
+
'desired_mask': desired_frame[item],
|
|
85
|
+
'actionable_attributes': 0,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
for attribute, items in reduced_flexible_items_binding.items():
|
|
89
|
+
|
|
90
|
+
new_ar_prefix = ar_prefix + (attribute,)
|
|
91
|
+
if in_stop_list(new_ar_prefix, stop_list):
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
undesired_states = []
|
|
95
|
+
desired_states = []
|
|
96
|
+
undesired_count = 0
|
|
97
|
+
desired_count = 0
|
|
98
|
+
for item in items:
|
|
99
|
+
|
|
100
|
+
if in_stop_list(itemset_prefix + (item,), stop_list_itemset):
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
undesired_support = undesired_frame[item].sum()
|
|
104
|
+
desired_support = desired_frame[item].sum()
|
|
105
|
+
|
|
106
|
+
if verbose:
|
|
107
|
+
print('SUPPORT')
|
|
108
|
+
print(itemset_prefix + (item,))
|
|
109
|
+
print((undesired_support, desired_support))
|
|
110
|
+
|
|
111
|
+
# is undesired
|
|
112
|
+
if desired_support + undesired_support == 0:
|
|
113
|
+
undesired_conf = 0
|
|
114
|
+
else:
|
|
115
|
+
undesired_conf = undesired_support / (desired_support + undesired_support)
|
|
116
|
+
if undesired_support >= min_undesired_support:
|
|
117
|
+
undesired_count += 1
|
|
118
|
+
if undesired_conf >= min_undesired_confidence:
|
|
119
|
+
undesired_states.append({'item': item, 'support': undesired_support, 'confidence': undesired_conf})
|
|
120
|
+
# is desired
|
|
121
|
+
if desired_support + undesired_support == 0:
|
|
122
|
+
desired_conf = 0
|
|
123
|
+
else:
|
|
124
|
+
desired_conf = desired_support / (desired_support + undesired_support)
|
|
125
|
+
if desired_support >= min_desired_support:
|
|
126
|
+
desired_count += 1
|
|
127
|
+
if desired_conf >= min_desired_confidence:
|
|
128
|
+
desired_states.append({'item': item, 'support': desired_support, 'confidence': desired_conf})
|
|
129
|
+
if desired_support < min_desired_support and undesired_support < min_undesired_support:
|
|
130
|
+
flexible_candidates[attribute].remove(item)
|
|
131
|
+
stop_list_itemset.append(itemset_prefix + (item,))
|
|
132
|
+
|
|
133
|
+
if actionable_attributes == 0 and (undesired_count == 0 or desired_count == 0): # just for first flexible level
|
|
134
|
+
del flexible_candidates[attribute]
|
|
135
|
+
stop_list.append(ar_prefix + (attribute,))
|
|
136
|
+
else:
|
|
137
|
+
for item in items:
|
|
138
|
+
new_branches.append({'ar_prefix': new_ar_prefix,
|
|
139
|
+
'itemset_prefix': itemset_prefix + (item,),
|
|
140
|
+
'item': item,
|
|
141
|
+
'undesired_mask': undesired_frame[item],
|
|
142
|
+
'desired_mask': desired_frame[item],
|
|
143
|
+
'actionable_attributes': actionable_attributes + 1,
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
if actionable_attributes + 1 >= min_flexible_attributes:
|
|
147
|
+
for undesired_item in undesired_states:
|
|
148
|
+
new_itemset_prefix = itemset_prefix + (undesired_item['item'],)
|
|
149
|
+
classification_rules[new_ar_prefix]['undesired'].append({
|
|
150
|
+
'itemset': new_itemset_prefix,
|
|
151
|
+
'support': undesired_item['support'],
|
|
152
|
+
'confidence': undesired_item['confidence'],
|
|
153
|
+
'target': desired_change_in_target[0]
|
|
154
|
+
})
|
|
155
|
+
for desired_item in desired_states:
|
|
156
|
+
new_itemset_prefix = itemset_prefix + (desired_item['item'],)
|
|
157
|
+
classification_rules[new_ar_prefix]['desired'].append({
|
|
158
|
+
'itemset': new_itemset_prefix,
|
|
159
|
+
'support': desired_item['support'],
|
|
160
|
+
'confidence': desired_item['confidence'],
|
|
161
|
+
'target': desired_change_in_target[1]
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
for new_branch in new_branches:
|
|
165
|
+
adding = False
|
|
166
|
+
new_stable = {}
|
|
167
|
+
new_flexible = {}
|
|
168
|
+
|
|
169
|
+
for attribute, items in stable_candidates.items():
|
|
170
|
+
for item in items:
|
|
171
|
+
if adding:
|
|
172
|
+
if attribute not in new_stable:
|
|
173
|
+
new_stable[attribute] = []
|
|
174
|
+
new_stable[attribute].append(item)
|
|
175
|
+
if item == new_branch['item']:
|
|
176
|
+
adding = True
|
|
177
|
+
|
|
178
|
+
for attribute, items in flexible_candidates.items():
|
|
179
|
+
for item in items:
|
|
180
|
+
if adding:
|
|
181
|
+
if attribute not in new_flexible:
|
|
182
|
+
new_flexible[attribute] = []
|
|
183
|
+
new_flexible[attribute].append(item)
|
|
184
|
+
if item == new_branch['item']:
|
|
185
|
+
adding = True
|
|
186
|
+
|
|
187
|
+
new_branch['stable_items_binding'] = new_stable
|
|
188
|
+
new_branch['flexible_items_binding'] = new_flexible
|
|
189
|
+
|
|
190
|
+
return new_branches
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# Generate action rules from classification rules
|
|
194
|
+
def generate_action_rules(classification_rules, action_rules):
|
|
195
|
+
for attribute_prefix, rules in classification_rules.items():
|
|
196
|
+
for desired_rule in rules['desired']:
|
|
197
|
+
for undesired_rule in rules['undesired']:
|
|
198
|
+
action_rules.append({'undesired': undesired_rule, 'desired': desired_rule})
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
# Prune tree
|
|
202
|
+
def prune_tree(K, classification_rules, stop_list):
|
|
203
|
+
for attribute_prefix, rules in classification_rules.items():
|
|
204
|
+
if K == len(attribute_prefix):
|
|
205
|
+
if len(rules['desired']) < 0 or len(rules['undesired']) < 0:
|
|
206
|
+
stop_list.append(attribute_prefix)
|
|
207
|
+
del classification_rules[attribute_prefix]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# Get the dictionaries of attributes and their values
|
|
211
|
+
def get_bindings(data, stable_attributes, flexible_attributes, target):
|
|
212
|
+
stable_items_binding = defaultdict(lambda: [])
|
|
213
|
+
flexible_items_binding = defaultdict(lambda: [])
|
|
214
|
+
target_items_binding = defaultdict(lambda: [])
|
|
215
|
+
|
|
216
|
+
for col in data.columns:
|
|
217
|
+
is_continue = False
|
|
218
|
+
# stable
|
|
219
|
+
for attribute in stable_attributes:
|
|
220
|
+
if col.startswith(attribute + '_<item>_'):
|
|
221
|
+
stable_items_binding[attribute].append(col)
|
|
222
|
+
is_continue = True
|
|
223
|
+
break
|
|
224
|
+
if is_continue is True:
|
|
225
|
+
continue
|
|
226
|
+
# flexible
|
|
227
|
+
for attribute in flexible_attributes:
|
|
228
|
+
if col.startswith(attribute + '_<item>_'):
|
|
229
|
+
flexible_items_binding[attribute].append(col)
|
|
230
|
+
is_continue = True
|
|
231
|
+
break
|
|
232
|
+
if is_continue is True:
|
|
233
|
+
continue
|
|
234
|
+
# target
|
|
235
|
+
if col.startswith(target + '_<item>_'):
|
|
236
|
+
target_items_binding[target].append(col)
|
|
237
|
+
return stable_items_binding, flexible_items_binding, target_items_binding
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# Create default stop list
|
|
241
|
+
def get_stop_list(stable_items_binding, flexible_items_binding):
|
|
242
|
+
stop_list = []
|
|
243
|
+
for items in stable_items_binding.values():
|
|
244
|
+
for stop_couple in itertools.product(items, repeat=2):
|
|
245
|
+
stop_list.append(tuple(stop_couple))
|
|
246
|
+
for item in flexible_items_binding.keys():
|
|
247
|
+
stop_list.append(tuple([item, item]))
|
|
248
|
+
return stop_list
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# Split data to desired and undesired
|
|
252
|
+
def get_split_tables(data, target_items_binding, target):
|
|
253
|
+
frames = {}
|
|
254
|
+
for item in target_items_binding[target]:
|
|
255
|
+
mask = data[item] == 1
|
|
256
|
+
frames[item] = data[mask]
|
|
257
|
+
return frames
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
# Action-Apriori main function
|
|
261
|
+
def action_apriori(data, stable_attributes, flexible_attributes, target, desired_change_in_target_l,
|
|
262
|
+
min_stable_attributes_l, min_flexible_attributes_l, min_undesired_support_l,
|
|
263
|
+
min_undesired_confidence_l, min_desired_support_l, min_desired_confidence_l, verbose=False):
|
|
264
|
+
# Global variables
|
|
265
|
+
global min_stable_attributes
|
|
266
|
+
global min_flexible_attributes
|
|
267
|
+
global min_undesired_support
|
|
268
|
+
global min_desired_support
|
|
269
|
+
global min_undesired_confidence
|
|
270
|
+
global min_desired_confidence
|
|
271
|
+
global desired_change_in_target
|
|
272
|
+
min_stable_attributes = min_stable_attributes_l
|
|
273
|
+
min_flexible_attributes = min_flexible_attributes_l
|
|
274
|
+
min_undesired_support = min_undesired_support_l
|
|
275
|
+
min_desired_support = min_desired_support_l
|
|
276
|
+
min_undesired_confidence = min_undesired_confidence_l
|
|
277
|
+
min_desired_confidence = min_desired_confidence_l
|
|
278
|
+
desired_change_in_target = desired_change_in_target_l
|
|
279
|
+
|
|
280
|
+
data = pd.get_dummies(data, sparse=False, columns=data.columns, prefix_sep='_<item>_')
|
|
281
|
+
stable_items_binding, flexible_items_binding, target_items_binding = get_bindings(data, stable_attributes,
|
|
282
|
+
flexible_attributes, target)
|
|
283
|
+
stop_list = get_stop_list(stable_items_binding, flexible_items_binding)
|
|
284
|
+
frames = get_split_tables(data, target_items_binding, target)
|
|
285
|
+
undesired_state = target + '_<item>_' + str(desired_change_in_target[0])
|
|
286
|
+
desired_state = target + '_<item>_' + str(desired_change_in_target[1])
|
|
287
|
+
action_rules = []
|
|
288
|
+
classification_rules = defaultdict(lambda: {'desired': [], 'undesired': []})
|
|
289
|
+
stop_list_itemset = []
|
|
290
|
+
|
|
291
|
+
candidates_queue = [{
|
|
292
|
+
'ar_prefix': tuple(),
|
|
293
|
+
'itemset_prefix': tuple(),
|
|
294
|
+
'stable_items_binding': stable_items_binding,
|
|
295
|
+
'flexible_items_binding': flexible_items_binding,
|
|
296
|
+
'undesired_mask': None,
|
|
297
|
+
'desired_mask': None,
|
|
298
|
+
'actionable_attributes': 0
|
|
299
|
+
}]
|
|
300
|
+
K = 0
|
|
301
|
+
while len(candidates_queue) > 0:
|
|
302
|
+
candidate = candidates_queue.pop(0)
|
|
303
|
+
if len(candidate['ar_prefix']) > K:
|
|
304
|
+
K += 1
|
|
305
|
+
prune_tree(K, classification_rules, stop_list)
|
|
306
|
+
new_candidates = generate_candidates(**candidate, stop_list=stop_list, frames=frames,
|
|
307
|
+
undesired_state=undesired_state, desired_state=desired_state,
|
|
308
|
+
stop_list_itemset=stop_list_itemset,
|
|
309
|
+
classification_rules=classification_rules, verbose=verbose)
|
|
310
|
+
candidates_queue += new_candidates
|
|
311
|
+
generate_action_rules(classification_rules, action_rules)
|
|
312
|
+
return action_rules
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def get_ar_notation(ar_dict, target):
|
|
316
|
+
rule = '['
|
|
317
|
+
for i, item in enumerate(ar_dict['undesired']['itemset']):
|
|
318
|
+
if i > 0:
|
|
319
|
+
rule += ' ∧ '
|
|
320
|
+
rule += '('
|
|
321
|
+
if item == ar_dict['desired']['itemset'][i]:
|
|
322
|
+
val = item.split('_<item>_')
|
|
323
|
+
rule += str(val[0]) + ': ' + str(val[1])
|
|
324
|
+
else:
|
|
325
|
+
val = item.split('_<item>_')
|
|
326
|
+
val_desired = ar_dict['desired']['itemset'][i].split('_<item>_')
|
|
327
|
+
rule += str(val[0]) + ': ' + str(val[1]) + ' → ' + str(val_desired[1])
|
|
328
|
+
rule += ')'
|
|
329
|
+
rule += '] ⇒ [' + str(target) + ': ' + str(ar_dict['undesired']['target']) + ' → ' + str(
|
|
330
|
+
ar_dict['desired']['target']) + ']'
|
|
331
|
+
rule += ', support of undesired part: ' + str(
|
|
332
|
+
ar_dict['undesired']['support']) + ', confidence of undesired part: ' + str(ar_dict['undesired']['confidence'])
|
|
333
|
+
rule += ', support of desired part: ' + str(ar_dict['desired']['support']) + ', confidence of desired part: ' + str(
|
|
334
|
+
ar_dict['desired']['confidence'])
|
|
335
|
+
return rule
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def get_export_notation(action_rules, target):
|
|
339
|
+
rules = []
|
|
340
|
+
for ar_dict in action_rules:
|
|
341
|
+
rule = {'stable': [], 'flexible': []}
|
|
342
|
+
for i, item in enumerate(ar_dict['undesired']['itemset']):
|
|
343
|
+
if item == ar_dict['desired']['itemset'][i]:
|
|
344
|
+
val = item.split('_<item>_')
|
|
345
|
+
rule['stable'].append({'attribute': val[0], 'value': val[1]})
|
|
346
|
+
else:
|
|
347
|
+
val = item.split('_<item>_')
|
|
348
|
+
val_desired = ar_dict['desired']['itemset'][i].split('_<item>_')
|
|
349
|
+
rule['flexible'].append({'attribute': val[0], 'undesired': val[1], 'desired': val_desired[1]})
|
|
350
|
+
rule['target'] = {'attribute': target, 'undesired': ar_dict['undesired']['target'],
|
|
351
|
+
'desired': ar_dict['desired']['target']}
|
|
352
|
+
rule['support of undesired part'] = ar_dict['undesired']['support']
|
|
353
|
+
rule['confidence of undesired part'] = ar_dict['undesired']['confidence']
|
|
354
|
+
rule['support of desired part'] = ar_dict['desired']['support']
|
|
355
|
+
rule['confidence of desired part'] = ar_dict['desired']['confidence']
|
|
356
|
+
rules.append(rule)
|
|
357
|
+
return rules
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Lukas Sykora
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: action-rules
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Action Rules Mining Tool.
|
|
5
|
+
Home-page: https://github.com/lukassykora/actionrules
|
|
6
|
+
Author: Lukas Sykora
|
|
7
|
+
Author-email: lukas.sykora@vse.cz
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Platform: UNKNOWN
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE.txt
|
|
12
|
+
Requires-Dist: pandas (~=2.2.2)
|
|
13
|
+
|
|
14
|
+
# Action-Apriori (Apriori Modified for Action Rules Mining)
|
|
15
|
+
|
|
16
|
+
[](https://opensource.org/licenses/MIT)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
The Action-Apriori script needs the following libraries:
|
|
21
|
+
- itertools (built-in module in python)
|
|
22
|
+
- copy (built-in module in python)
|
|
23
|
+
- collections (built-in module in python)
|
|
24
|
+
- pandas (1.3.4)
|
|
25
|
+
|
|
26
|
+
The tested Python version is: 3.9.7
|
|
27
|
+
|
|
28
|
+
The action_apriori function can be called:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import action_rules as ar
|
|
32
|
+
import pandas as pd
|
|
33
|
+
# Data
|
|
34
|
+
transactions = {'Sex': ['M', 'F', 'M', 'M', 'F', 'M', 'F'],
|
|
35
|
+
'Age': ['Y', 'Y', 'O', 'Y', 'Y', 'O', 'Y'],
|
|
36
|
+
'Class': [1, 1, 2, 2, 1, 1, 2],
|
|
37
|
+
'Embarked': ['S', 'C', 'S', 'C', 'S', 'C', 'C'],
|
|
38
|
+
'Survived': [1, 1, 0, 0, 1, 1, 0],
|
|
39
|
+
}
|
|
40
|
+
data = pd.DataFrame.from_dict(transactions)
|
|
41
|
+
# Parameters
|
|
42
|
+
stable_attributes = ['Sex','Age']
|
|
43
|
+
flexible_attributes = ['Class','Embarked']
|
|
44
|
+
target = 'Survived'
|
|
45
|
+
wanted_change_in_target = [0, 1]
|
|
46
|
+
min_stable_attributes = 2
|
|
47
|
+
min_flexible_attributes = 1 #min 1
|
|
48
|
+
min_unwanted_support = 1
|
|
49
|
+
min_unwanted_confidence = 0.5 #min 0.5
|
|
50
|
+
min_wanted_support = 2
|
|
51
|
+
min_wanted_confidence = 0.5 #min 0.5
|
|
52
|
+
# Action Rules Mining
|
|
53
|
+
action_rules = ar.action_apriori(
|
|
54
|
+
data,
|
|
55
|
+
stable_attributes,
|
|
56
|
+
flexible_attributes,
|
|
57
|
+
target,
|
|
58
|
+
wanted_change_in_target,
|
|
59
|
+
min_stable_attributes ,
|
|
60
|
+
min_flexible_attributes,
|
|
61
|
+
min_unwanted_support,
|
|
62
|
+
min_unwanted_confidence,
|
|
63
|
+
min_wanted_support,
|
|
64
|
+
min_wanted_confidence,
|
|
65
|
+
True) #verbose
|
|
66
|
+
# Print rules
|
|
67
|
+
for action_rule in action_rules:
|
|
68
|
+
print(action_rule)
|
|
69
|
+
# Print rules with action rules notation
|
|
70
|
+
for action_rule in action_rules:
|
|
71
|
+
print(ar.get_ar_notation(action_rule, target))
|
|
72
|
+
# Print rules with export notation
|
|
73
|
+
print(ar.get_export_notation(action_rules, target))
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The output: ation rule with notation:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
[(Sex: F) ∧ (Age: Y) ∧ (Class: 2 → 1)] ⇒ [Survived: 0 → 1], support of undesired part: 1, confidence of undesired part: 1.0, support of desired part: 2, confidence of desired part: 1.0
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
action_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
action_rules/action_rules.py,sha256=BSVQ5CiQp3TimnqGLp_lQTskX9kmafG9gCwxJkHyyqw,16926
|
|
3
|
+
action_rules-0.0.1.dist-info/LICENSE.txt,sha256=gEr-nYwCX9aGVHiZSsOVYQb4TFlbKfQVmmd0-Gun2t4,1088
|
|
4
|
+
action_rules-0.0.1.dist-info/METADATA,sha256=9vL4OiGIp9pp6geT2ErXVI3wmTGg_WypwXT0zNkFiK0,2419
|
|
5
|
+
action_rules-0.0.1.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
|
|
6
|
+
action_rules-0.0.1.dist-info/top_level.txt,sha256=4uSeKPOWLOUAFeSknZThyYBY1Ra9pQb_wNWm8GRCxcE,13
|
|
7
|
+
action_rules-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
action_rules
|