5050 "jaccard" ,
5151 "certainty" ,
5252 "kulczynski" ,
53+ "mutual_information" ,
5354]
5455
5556
@@ -65,15 +66,15 @@ def test_default():
6566
6667 expect = pd .DataFrame (
6768 [
68- [(8 ,), (5 ,), 0.6 , 1.0 , 0.6 , 1.0 , 1.0 , 1.0 , 0.0 , np .inf , 0 , 0.6 , 0.0 , 0.8 ],
69- [(6 ,), (5 ,), 0.6 , 1.0 , 0.6 , 1.0 , 1.0 , 1.0 , 0.0 , np .inf , 0 , 0.6 , 0.0 , 0.8 ],
70- [(8 , 3 ), (5 ,), 0.6 , 1.0 , 0.6 , 1.0 , 1.0 , 1.0 , 0.0 , np .inf , 0 , 0.6 , 0.0 , 0.8 ],
71- [(8 , 5 ), (3 ,), 0.6 , 0.8 , 0.6 , 1.0 , 1.25 , 1.0 , 0.12 , np .inf , 0.5 , 0.75 , 1.0 , 0.875 ],
72- [(8 ,), (3 , 5 ), 0.6 , 0.8 , 0.6 , 1.0 , 1.25 , 1.0 , 0.12 , np .inf , 0.5 , 0.75 , 1.0 , 0.875 ],
73- [(3 ,), (5 ,), 0.8 , 1.0 , 0.8 , 1.0 , 1.0 , 1.0 , 0.0 , np .inf , 0 , 0.8 , 0.0 , 0.9 ],
74- [(5 ,), (3 ,), 1.0 , 0.8 , 0.8 , 0.8 , 1.0 , 1.0 , 0.0 , 1.0 , 0.0 , 0.8 , 0.0 , 0.9 ],
75- [(10 ,), (5 ,), 0.6 , 1.0 , 0.6 , 1.0 , 1.0 , 1.0 , 0.0 , np .inf , 0 , 0.6 , 0.0 , 0.8 ],
76- [(8 ,), (3 ,), 0.6 , 0.8 , 0.6 , 1.0 , 1.25 , 1.0 , 0.12 , np .inf , 0.5 , 0.75 , 1.0 , 0.875 ],
69+ [(8 ,), (5 ,), 0.6 , 1.0 , 0.6 , 1.0 , 1.0 , 1.0 , 0.0 , np .inf , 0 , 0.6 , 0.0 , 0.8 , 0.0 ],
70+ [(6 ,), (5 ,), 0.6 , 1.0 , 0.6 , 1.0 , 1.0 , 1.0 , 0.0 , np .inf , 0 , 0.6 , 0.0 , 0.8 , 0.0 ],
71+ [(8 , 3 ), (5 ,), 0.6 , 1.0 , 0.6 , 1.0 , 1.0 , 1.0 , 0.0 , np .inf , 0 , 0.6 , 0.0 , 0.8 , 0.0 ],
72+ [(8 , 5 ), (3 ,), 0.6 , 0.8 , 0.6 , 1.0 , 1.25 , 1.0 , 0.12 , np .inf , 0.5 , 0.75 , 1.0 , 0.875 , 0.0 ],
73+ [(8 ,), (3 , 5 ), 0.6 , 0.8 , 0.6 , 1.0 , 1.25 , 1.0 , 0.12 , np .inf , 0.5 , 0.75 , 1.0 , 0.875 , 0.0 ],
74+ [(3 ,), (5 ,), 0.8 , 1.0 , 0.8 , 1.0 , 1.0 , 1.0 , 0.0 , np .inf , 0 , 0.8 , 0.0 , 0.9 , 0.0 ],
75+ [(5 ,), (3 ,), 1.0 , 0.8 , 0.8 , 0.8 , 1.0 , 1.0 , 0.0 , 1.0 , 0.0 , 0.8 , 0.0 , 0.9 , 0.0 ],
76+ [(10 ,), (5 ,), 0.6 , 1.0 , 0.6 , 1.0 , 1.0 , 1.0 , 0.0 , np .inf , 0 , 0.6 , 0.0 , 0.8 , 0.0 ],
77+ [(8 ,), (3 ,), 0.6 , 0.8 , 0.6 , 1.0 , 1.25 , 1.0 , 0.12 , np .inf , 0.5 , 0.75 , 1.0 , 0.875 , 0.0 ],
7778 ],
7879
7980 columns = columns_ordered ,
@@ -120,6 +121,7 @@ def test_nullability():
120121 0.667 ,
121122 0 ,
122123 0.833 ,
124+ 0.0 ,
123125 ],
124126 [
125127 (10 , 5 ),
@@ -136,6 +138,7 @@ def test_nullability():
136138 0.667 ,
137139 0.0 ,
138140 0.833 ,
141+ 0.0 ,
139142 ],
140143 [
141144 (10 ,),
@@ -152,6 +155,7 @@ def test_nullability():
152155 0.615 ,
153156 0.0 ,
154157 0.833 ,
158+ 0.415 ,
155159 ],
156160 [
157161 (10 ,),
@@ -168,6 +172,7 @@ def test_nullability():
168172 0.615 ,
169173 0.0 ,
170174 0.833 ,
175+ 0.415 ,
171176 ],
172177 [
173178 (10 ,),
@@ -184,6 +189,7 @@ def test_nullability():
184189 0.615 ,
185190 0 ,
186191 0.833 ,
192+ - 0.169 ,
187193 ],
188194 [
189195 (3 , 5 ),
@@ -200,6 +206,7 @@ def test_nullability():
200206 0.615 ,
201207 - 0.333 ,
202208 0.833 ,
209+ - 0.169 ,
203210 ],
204211 [
205212 (3 ,),
@@ -216,6 +223,7 @@ def test_nullability():
216223 0.667 ,
217224 0.0 ,
218225 0.833 ,
226+ 0.0 ,
219227 ],
220228 [
221229 (3 ,),
@@ -232,6 +240,7 @@ def test_nullability():
232240 0.615 ,
233241 - 0.333 ,
234242 0.833 ,
243+ - 0.169 ,
235244 ],
236245 [(3 ,), (5 ,), 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , 0.8 , 0.0 , np .inf , 0 , 1.0 , 0 , 1.0 ],
237246 [
@@ -249,6 +258,7 @@ def test_nullability():
249258 0.667 ,
250259 0 ,
251260 0.833 ,
261+ 0.0 ,
252262 ],
253263 [
254264 (5 ,),
@@ -265,8 +275,25 @@ def test_nullability():
265275 0.615 ,
266276 - 0.333 ,
267277 0.833 ,
278+ - 0.169 ,
279+ ],
280+ [
281+ (5 ,),
282+ (3 ,),
283+ 1.0 ,
284+ 1.0 ,
285+ 1.0 ,
286+ 1.0 ,
287+ 1.0 ,
288+ 0.8 ,
289+ 0.0 ,
290+ np .inf ,
291+ 0 ,
292+ 1.0 ,
293+ 0.0 ,
294+ 1.0 ,
295+ 0.0 ,
268296 ],
269- [(5 ,), (3 ,), 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , 0.8 , 0.0 , np .inf , 0 , 1.0 , 0.0 , 1.0 ],
270297 ],
271298 columns = columns_ordered ,
272299 )
@@ -335,6 +362,7 @@ def test_empty_result():
335362 "jaccard" ,
336363 "certainty" ,
337364 "kulczynski" ,
365+ "mutual_information" ,
338366 ]
339367 )
340368 res_df = association_rules (df_freq_items , len (df ), min_threshold = 2 )
@@ -563,3 +591,35 @@ def test_mutual_information_metric():
563591
564592 # Non-existent itemsets (support=0) should give -inf
565593 assert res_df ["mutual_information" ].notna ().any ()
594+
595+
596+ def test_mutual_information_metric ():
597+ """Test mutual_information metric returns correct values."""
598+ import math
599+
600+ res_df = association_rules (
601+ df_freq_items_with_colnames ,
602+ len (df ),
603+ metric = "mutual_information" ,
604+ min_threshold = - 100 ,
605+ )
606+ assert "mutual_information" in res_df .columns
607+
608+ # Eggs -> Kidney Beans: sAC=0.6, sA=0.8, sC=1.0
609+ # MI = log2(0.6 / (0.8 * 1.0)) = log2(0.75) approx -0.415
610+ rule = res_df [
611+ res_df ["antecedents" ].apply (lambda x : x == frozenset ({"Eggs" }))
612+ & res_df ["consequents" ].apply (lambda x : x == frozenset ({"Kidney Beans" }))
613+ ]
614+ assert len (rule ) == 1
615+ expected_mi = math .log2 (0.6 / (0.8 * 1.0 ))
616+ assert abs (rule ["mutual_information" ].values [0 ] - expected_mi ) < 1e-6
617+
618+ # Milk -> Kidney Beans: sAC=0.6, sA=0.6, sC=1.0
619+ # MI = log2(0.6 / (0.6 * 1.0)) = log2(1) = 0
620+ milk_rule = res_df [
621+ res_df ["antecedents" ].apply (lambda x : x == frozenset ({"Milk" }))
622+ & res_df ["consequents" ].apply (lambda x : x == frozenset ({"Kidney Beans" }))
623+ ]
624+ assert len (milk_rule ) == 1
625+ assert abs (milk_rule ["mutual_information" ].values [0 ]) < 1e-6
0 commit comments