←Back to the bookChapter 120 · Take the quiz & aim for 100%
▶ Open in Colab⬇ Download notebookView on GitHub
Chapter 120 · Solutions
Association Rule Mining · Solutions
Five challenges, each verified in code.

Chapter 120 · Practice Challenges, Solved¶

Five market-basket exercises, worked in mlxtend on the Chapter 120 grocery transactions.

In [1]:
import numpy as np, pandas as pd, matplotlib.pyplot as plt
import seaborn as sns   # seaborn = high-level statistical plots (heatmaps, pairplots, count/bar plots)
from matplotlib.colors import ListedColormap
EM="#4338ca"; DEEP="#3730a3"; LIGHT="#c7d2fe"; INK="#1a2138"; GRID="#e6e9f2"; RED="#ef4444"; AMBER="#d97706"; GREEN="#059669"; BLUE="#2563eb"; PUR="#9333ea"; GREY="#94a3b8"; SLATE="#475569"; ORG="#4338ca"; CYAN="#0891b2"
plt.rcParams.update({"figure.facecolor":"white","axes.facecolor":"white","figure.dpi":110,"font.size":11,
   "axes.edgecolor":GRID,"axes.grid":True,"grid.color":GRID,"axes.axisbelow":True,"axes.spines.top":False,
   "axes.spines.right":False,"axes.titlesize":12,"axes.titleweight":"bold","legend.frameon":False})
sns.set_style("whitegrid")
BASE="https://raw.githubusercontent.com/johnfisher-ai/Statistics-Data-Science-AI-Visual-Book/main/data/"
In [2]:
from mlxtend.frequent_patterns import apriori, fpgrowth, association_rules
try: df = pd.read_excel('../../data/association-rule-mining--baskets.xlsx', sheet_name='Data')
except FileNotFoundError: df = pd.read_excel(BASE + 'association-rule-mining--baskets.xlsx', sheet_name='Data')
basket = df.assign(v=1).pivot_table(index='transaction_id', columns='item', values='v', fill_value=0).astype(bool)
print('basket matrix:', basket.shape)
basket matrix: (900, 25)
CHALLENGE 1
Most frequent item
Build the basket matrix and report the item with the highest support.
In [3]:
support = basket.mean().sort_values(ascending=False)
print(support.head(5).round(3).to_string())
print('most frequent item:', support.idxmax(), '(support %.2f)' % support.max())
item
bread     0.334
jam       0.187
cereal    0.172
butter    0.170
bacon     0.167
most frequent item: bread (support 0.33)

Solution. Bread has the highest support (in about a third of baskets), the popular staple every rule must be compared against.

CHALLENGE 2
Frequent itemsets
Run Apriori at min_support = 0.05 and count the frequent itemsets.
In [4]:
freq = apriori(basket, min_support=0.05, use_colnames=True)
print('frequent itemsets at 5% support:', len(freq))
print(freq['itemsets'].apply(len).value_counts().sort_index().to_string())
frequent itemsets at 5% support: 54
itemsets
1    25
2    20
3     8
4     1

Solution. A higher support threshold keeps fewer, more reliable itemsets, tune it to trade coverage against trustworthiness.

CHALLENGE 3
Top rule by lift
Mine rules and report the highest-lift rule (lift is symmetric, so expect a tie).
In [5]:
freq = apriori(basket, min_support=0.03, use_colnames=True)
rules = association_rules(freq, metric='lift', min_threshold=1.0)
rules['A'] = rules['antecedents'].apply(lambda s: ', '.join(sorted(s)))
rules['B'] = rules['consequents'].apply(lambda s: ', '.join(sorted(s)))
best = rules.sort_values(['lift', 'A', 'B'], ascending=[False, True, True]).iloc[0]   # break the A -> B / B -> A tie by name
print('top rule: %s -> %s  | support %.3f, confidence %.3f, lift %.2f' % (best.A, best.B, best.support, best.confidence, best.lift))
top rule: chips, soda -> salsa  | support 0.092, confidence 0.856, lift 5.70

Solution. The strongest rules link the cookout items (chips, salsa, soda) with lift above 5, so baskets holding two of them contain the third far more often than average. Lift is symmetric, so a rule and its reverse tie, and chips, soda -> salsa and salsa -> chips, soda share the 5.70, though their confidences differ (0.856 here, and 0.615 for the reverse in the main notebook).

CHALLENGE 4
Confidence vs lift
Find the lift of bananas -> bread and explain why its high-ish confidence is misleading.
In [6]:
r = rules[(rules.A=='bananas') & (rules.B=='bread')][['support','confidence','lift']]
print(r.round(3).to_string(index=False))
print('bread baseline support = %.2f' % basket['bread'].mean())
print('confidence 0.37 barely beats the 0.33 baseline -> lift ~1.1 -> essentially independent')
 support  confidence  lift
   0.047       0.368 1.102
bread baseline support = 0.33
confidence 0.37 barely beats the 0.33 baseline -> lift ~1.1 -> essentially independent

Solution. Bananas do not really drive bread; bread is simply in a third of all baskets, so 37% confidence is almost the baseline. Lift near 1 exposes the coincidence, always judge a rule by lift, not confidence.

CHALLENGE 5
FP-Growth agreement
Confirm FP-Growth returns the same frequent itemsets as Apriori.
In [7]:
a = apriori(basket, min_support=0.03, use_colnames=True)
f = fpgrowth(basket, min_support=0.03, use_colnames=True)
sa = set(frozenset(x) for x in a['itemsets']); sf = set(frozenset(x) for x in f['itemsets'])
print('Apriori itemsets:', len(sa), '| FP-Growth itemsets:', len(sf))
print('identical set of itemsets:', sa == sf)
Apriori itemsets: 61 | FP-Growth itemsets: 61
identical set of itemsets: True

Solution. FP-Growth finds exactly the same frequent itemsets as Apriori but with a compact tree instead of repeated scans, so on large catalogs it is much faster while giving identical rules.


Statistics, Data Science and AI: A Visual Handbook · © 2026 John Fisher
←Back to the bookChapter 120 · Take the quiz & aim for 100%