10
10
11
11
from collections import defaultdict
12
12
from itertools import combinations
13
- from typing import List , Dict , Tuple , Set
14
13
15
14
16
- def load_data () -> List [ List [str ]]:
15
+ def load_data () -> list [ list [str ]]:
17
16
"""
18
17
Returns a sample transaction dataset.
19
18
20
- >>> data = load_data()
21
- >>> len(data)
22
- 4
23
- >>> 'milk' in data[0]
24
- True
19
+ >>> load_data()
20
+ [['milk'], ['milk', 'butter'], ['milk', 'bread'], ['milk', 'bread', 'chips']]
25
21
"""
26
22
return [["milk" ], ["milk" , "butter" ], ["milk" , "bread" ], ["milk" , "bread" , "chips" ]]
27
23
@@ -31,17 +27,17 @@ class Apriori:
31
27
32
28
def __init__ (
33
29
self ,
34
- transactions : List [ List [str ]],
30
+ transactions : list [ list [str ]],
35
31
min_support : float = 0.25 ,
36
32
min_confidence : float = 0.5 ,
37
33
min_lift : float = 1.0 ,
38
34
) -> None :
39
- self .transactions : List [ Set [str ]] = [set (t ) for t in transactions ]
35
+ self .transactions : list [ set [str ]] = [set (t ) for t in transactions ]
40
36
self .min_support : float = min_support
41
37
self .min_confidence : float = min_confidence
42
38
self .min_lift : float = min_lift
43
- self .itemsets : List [ Dict [frozenset , float ]] = []
44
- self .rules : List [ Tuple [frozenset , frozenset , float , float ]] = []
39
+ self .itemsets : list [ dict [frozenset , float ]] = []
40
+ self .rules : list [ tuple [frozenset , frozenset , float , float ]] = []
45
41
46
42
self .find_frequent_itemsets ()
47
43
self .generate_association_rules ()
@@ -54,34 +50,36 @@ def _get_support(self, itemset: frozenset) -> float:
54
50
55
51
def confidence (self , antecedent : frozenset , consequent : frozenset ) -> float :
56
52
"""Calculate confidence of a rule A -> B."""
57
- support_antecedent : float = self ._get_support (antecedent )
58
- support_both : float = self ._get_support (antecedent | consequent )
53
+ support_antecedent = self ._get_support (antecedent )
54
+ support_both = self ._get_support (antecedent | consequent )
59
55
return support_both / support_antecedent if support_antecedent > 0 else 0.0
60
56
61
57
def lift (self , antecedent : frozenset , consequent : frozenset ) -> float :
62
58
"""Calculate lift of a rule A -> B."""
63
- support_consequent : float = self ._get_support (consequent )
64
- conf : float = self .confidence (antecedent , consequent )
59
+ support_consequent = self ._get_support (consequent )
60
+ conf = self .confidence (antecedent , consequent )
65
61
return conf / support_consequent if support_consequent > 0 else 0.0
66
62
67
- def find_frequent_itemsets (self ) -> List [ Dict [frozenset , float ]]:
63
+ def find_frequent_itemsets (self ) -> list [ dict [frozenset , float ]]:
68
64
"""Generate all frequent itemsets."""
69
- item_counts : Dict [frozenset , int ] = defaultdict (int )
65
+ item_counts : dict [frozenset , int ] = defaultdict (int )
70
66
for t in self .transactions :
71
67
for item in t :
72
68
item_counts [frozenset ([item ])] += 1
73
69
74
70
total : int = len (self .transactions )
75
- current_itemsets : Dict [frozenset , float ] = {
76
- k : v / total for k , v in item_counts .items () if v / total >= self .min_support
71
+ current_itemsets : dict [frozenset , float ] = {
72
+ k : v / total
73
+ for k , v in item_counts .items ()
74
+ if v / total >= self .min_support
77
75
}
78
76
if current_itemsets :
79
77
self .itemsets .append (current_itemsets )
80
78
81
79
k : int = 2
82
80
while current_itemsets :
83
- candidates : Set [frozenset ] = set ()
84
- keys : List [frozenset ] = list (current_itemsets .keys ())
81
+ candidates : set [frozenset ] = set ()
82
+ keys : list [frozenset ] = list (current_itemsets .keys ())
85
83
for i in range (len (keys )):
86
84
for j in range (i + 1 , len (keys )):
87
85
union = keys [i ] | keys [j ]
@@ -91,8 +89,10 @@ def find_frequent_itemsets(self) -> List[Dict[frozenset, float]]:
91
89
):
92
90
candidates .add (union )
93
91
94
- freq_candidates : Dict [frozenset , float ] = {
95
- c : self ._get_support (c ) for c in candidates if self ._get_support (c ) >= self .min_support
92
+ freq_candidates : dict [frozenset , float ] = {
93
+ c : self ._get_support (c )
94
+ for c in candidates
95
+ if self ._get_support (c ) >= self .min_support
96
96
}
97
97
if not freq_candidates :
98
98
break
@@ -103,26 +103,24 @@ def find_frequent_itemsets(self) -> List[Dict[frozenset, float]]:
103
103
104
104
return self .itemsets
105
105
106
- def generate_association_rules (self ) -> List [Tuple [frozenset , frozenset , float , float ]]:
106
+ def generate_association_rules (
107
+ self ,
108
+ ) -> list [tuple [frozenset , frozenset , float , float ]]:
107
109
"""Generate association rules with min confidence and lift."""
108
110
for level in self .itemsets :
109
111
for itemset in level :
110
112
if len (itemset ) < 2 :
111
113
continue
112
114
for i in range (1 , len (itemset )):
113
115
for antecedent in combinations (itemset , i ):
114
- antecedent_set : frozenset = frozenset (antecedent )
115
- consequent_set : frozenset = itemset - antecedent_set
116
- conf : float = self .confidence (antecedent_set , consequent_set )
117
- lft : float = self .lift (antecedent_set , consequent_set )
118
- rule : Tuple [frozenset , frozenset , float , float ] = (
119
- antecedent_set ,
120
- consequent_set ,
121
- conf ,
122
- lft ,
123
- )
124
- if rule not in self .rules and conf >= self .min_confidence and lft >= self .min_lift :
125
- self .rules .append (rule )
116
+ antecedent_set = frozenset (antecedent )
117
+ consequent_set = itemset - antecedent_set
118
+ conf = self .confidence (antecedent_set , consequent_set )
119
+ lft = self .lift (antecedent_set , consequent_set )
120
+ if conf >= self .min_confidence and lft >= self .min_lift :
121
+ self .rules .append (
122
+ (antecedent_set , consequent_set , conf , lft )
123
+ )
126
124
return self .rules
127
125
128
126
@@ -131,10 +129,8 @@ def generate_association_rules(self) -> List[Tuple[frozenset, frozenset, float,
131
129
132
130
doctest .testmod ()
133
131
134
- transactions : List [List [str ]] = load_data ()
135
- model : Apriori = Apriori (
136
- transactions , min_support = 0.25 , min_confidence = 0.1 , min_lift = 0.0
137
- )
132
+ transactions = load_data ()
133
+ model = Apriori (transactions , min_support = 0.25 , min_confidence = 0.1 , min_lift = 0.0 )
138
134
139
135
print ("Frequent itemsets:" )
140
136
for level in model .itemsets :
@@ -143,8 +139,7 @@ def generate_association_rules(self) -> List[Tuple[frozenset, frozenset, float,
143
139
144
140
print ("\n Association Rules:" )
145
141
for rule in model .rules :
146
- antecedent , consequent , conf , lift_value = rule
142
+ antecedent , consequent , conf , lift = rule
147
143
print (
148
- f"{ set (antecedent )} -> { set (consequent )} , "
149
- f"conf={ conf :.2f} , lift={ lift_value :.2f} "
144
+ f"{ set (antecedent )} -> { set (consequent )} , conf={ conf :.2f} , lift={ lift :.2f} "
150
145
)
0 commit comments