Part V — Association Rule Learning

Chapter 21 · Apriori

Part V asks retail's oldest question: what goes into baskets together? No distances, no labels, no clusters — just co-occurrence, and three numbers that turn it into decisions: support, confidence and lift, every one computed by hand on a five-basket table before the machine touches 7,501 real transactions. Then the algorithm that makes the search survivable, and the nine real rules it found — led by a cheese most shoppers have never heard of.

📖 Chapter 21 of 33 ⏱ ~85 min read Mostly Core Stable 📝 Quiz · 30 MCQs

🎯 Learning Objectives

1. From Groups to Groceries

Two Parts asked "which customers group together"; this one asks "which items travel together". The unit of data is the transaction — one basket, an unordered set of products — and the deliverable is an association rule: people who buy X tend to also buy Y, written X → Y, scored by numbers a category manager can act on.

The running dataset: Market_Basket_Optimisation.csv — 7,501 baskets over one week of a grocery store, 20 item columns per row, ragged (a basket of 2 leaves 18 blank cells). 120 distinct products, and a familiar long tail: the most popular item, mineral water, appears in 1,788 baskets (support 0.238); eggs, spaghetti and french fries follow; by item #50 you are deep into single-digit-daily territory.

Itemmineral watereggsspaghettifrench frieschocolategreen teamilkground beef
Baskets1,7881,3481,3061,2821,230991972737
Support0.2380.1800.1740.1710.1640.1320.1300.098
📝
Note — remember mineral water's 0.238. One in four baskets contains it — a fact that will sabotage several naive rules in Section 7, and the reason lift exists.

2. Support, Confidence, Lift — by Hand

Five baskets, four products, every number by hand:

Basket12345
Itemsbread, milkbread, milk, eggsbread, eggsmilk, eggsbread, milk, eggs, cheese

Item counts: bread 4 (baskets 1,2,3,5), milk 3 (1,2,5), eggs 4 (2,3,4,5), cheese 1 (5). Now the three metrics on three candidate rules — chosen because together they commit every classic mistake:

Support(X → Y)  =  baskets with both X and Y / all baskets
Confidence(X → Y)  =  both / baskets with X
Lift(X → Y)  =  confidence / support(Y)
Support: how common the pairing is. Confidence: P(Y | X) — the conditional from Chapter 15. Lift: how much better than baseline (Y's raw popularity) the rule performs.

2.1 Rule 1: bread → milk (the plausible one)

supp(bread∧milk) = 3/5 = 0.60  ·  conf = 3/4 = 0.75  ·  lift = 0.75 / 0.60 = 1.25
Confidence looks strong — 75% of bread buyers take milk. But milk is in 60% of baskets anyway: the rule beats random by only 25%. Plausible ≠ interesting.

2.2 Rule 2: eggs → milk (the negative one)

supp(eggs∧milk) = 2/5 = 0.40  ·  conf = 2/4 = 0.50  ·  lift = 0.50 / 0.60 = 0.83
Lift below 1: egg buyers are less likely than average to buy milk. A real anti-association — the two products repel. Confidence alone would never tell you.

2.3 Rule 3: cheese → bread (the trap)

supp(cheese∧bread) = 1/5 = 0.20  ·  conf = 1/1 = 1.00 — perfect!  ·  lift = 1.00 / 0.80 = 1.25
One basket. One co-occurrence. Confidence 100% — and it means exactly nothing: the sample is one event. The final trap: no metric may be read without its support (the sample size underneath).
⚠
Warning — the referee principle: support certifies the rule is worth counting, confidence measures the tendency, lift decides whether the tendency beats the baseline. A rule needs all three read together — high confidence on tiny support is noise; decent confidence with lift ≈ 1 is a popular-item illusion; lift < 1 is a repulsion, not a recommendation.

3. The Combinatorial Wall & the Apriori Principle

Why an algorithm at all — why not count everything? Because "everything" is 2120 itemsets: every subset of the 120 products, a number with 36 digits. Even enumerating at a billion per second outlives the universe. The wall is real, and Apriori's principle (Agrawal & Srikant, 1994) is the door:

Every subset of a frequent itemset is frequent.
Contrapositive: if any subset is infrequent, the whole superset is dead on arrival. If {pasta, escalope} is frequent, then {pasta} and {escalope} must each be frequent — a basket containing both necessarily contains each. So: count single items first, keep only the frequent ones; build candidate pairs only from frequent singles; count those; build triples only from frequent pairs. The pruning is anti-monotone — support only shrinks as itemsets grow — and each level slaughters the search space before it is born.
Level 1: singles 120 items → keep ~50 frequent Level 2: pairs from 50: C(50,2) = 1,225 → few survive Level 3: triples from survivors only — the wall never comes pruned: eggs+milk lift 0.83 dies here pruned: any pair with an infrequent member anti-monotonicity: bigger sets are never more frequent — prune early, never regret
Figure 21.1 — The level-wise search: each level's survivors are the only parents of the next level's candidates.
💡
Pro Tip — where the name comes from: "a priori" = known in advance. The principle lets you reject candidate itemsets before counting, on prior knowledge of their subsets — the algorithm's entire speed is foresight, not faster counting.

4. Choosing the Thresholds

The source's settings, each with its business rationale:

ParameterValueWhy this value
min_support = 0.003 0.3% of baskets ≈ 22–23 baskets of 7,501 ≈ 3 sales per day over the week — the minimum a store would act on. Lower: noise and unicorn pairs; higher: only staples, no discoveries.
min_confidence = 0.2 20% Deliberately low — confidence is the sieve's coarse screen; lift does the real filtering (Section 7 shows why high-confidence/low-lift rules are worthless).
min_lift = 3 3× The referee: a rule must make the consequent three times more likely than baseline to count as a genuine association. Lift 1 = illusion; 3 = worth a meeting.
min_length, max_length = 2 pairs only Rules of exactly two items: readable, placeable, bundlable. Triples (mushroom cream sauce → escalope and what else?) can wait for a bigger dataset.
🌎
Real World — the thresholds ARE the analysis: the same 7,501 baskets yield 3 throwaway rules at lift ≥ 5 and hundreds of popular-item illusions at lift ≥ 1.5. A data scientist who cannot say why min_support is 0.003 has not configured the analysis — they have copied it. Tie every threshold to a decision: what volume would we act on, what lift would justify moving a shelf?

5. The Real Rules

The run — 7,501 transactions, the Section 4 thresholds — returns exactly 9 rules, sorted here by lift (all real):

#Rule (X → Y)SupportConfidenceLiftReading
1fromage blanc → honey0.00330.2455.16Dessert pairing — niche but the strongest association in the store
2light cream → chicken0.00450.2914.84Cooking cluster: cream sauces
3pasta → escalope0.00590.3734.70The Italian dinner basket
4pasta → shrimp0.00510.3224.51Same cluster, second door
5whole wheat pasta → olive oil0.00800.2714.12The health-conscious Italian basket
6tomato sauce → ground beef0.00530.3773.84Bolognese, spotted by the algorithm
7mushroom cream sauce → escalope0.00570.3013.79Recipe-driven purchase
8herb & pepper → ground beef0.01600.3233.29The volume rule — 120 baskets/week, still lift 3.3
9light cream → olive oil0.00320.2053.11Crossing the two cooking clusters

Three readings before acting on any of them:

📋 Stable content — Reviewed: August 2026

6. Implementation in Python

Apriori ships not in sklearn but in apyori — a small third-party package (pip install apyori) that expects transactions as a list of lists of strings:

import pandas as pd

dataset = pd.read_csv('Market_Basket_Optimisation.csv', header=None)

# Ragged rows -> list of lists, blanks stripped (vital: 'nan' is not a product)
transactions = [[str(item) for item in row if pd.notna(item)]
                for row in dataset.values]
# 7,501 transactions; each an unordered basket

from apyori import apriori
rules = apriori(transactions=transactions,
                min_support=0.003, min_confidence=0.2,
                min_lift=3, min_length=2, max_length=2)
results = list(rules)                     # 9 rules

# Unpack the nested result objects into a readable DataFrame
def inspect(results):
    lhs         = [tuple(r[2][0][0])[0] for r in results]
    rhs         = [tuple(r[2][0][1])[0] for r in results]
    supports    = [r[1]              for r in results]
    confidences = [r[2][0][2]        for r in results]
    lifts       = [r[2][0][3]        for r in results]
    return list(zip(lhs, rhs, supports, confidences, lifts))

rules_df = pd.DataFrame(inspect(results),
                        columns=['LHS', 'RHS', 'Support',
                                 'Confidence', 'Lift'])
print(rules_df.sort_values('Lift', ascending=False))

Listing 21.1 — The complete pipeline (source: apriori.py, blank-stripping made explicit).

💡
Pro Tip — the classic bug: the source converts every cell with str(), blanks included, so the string 'nan' becomes the store's 121st "product" — with suspiciously high frequency. Strip missing cells before building transactions (as above), or audit transactions[0] for the ghost item before trusting any rule.

7. Confidence's Trap, Lift's Verdict

One puzzle remains from Section 1: mineral water — 1,788 baskets, the most popular product in the store — appears in none of the nine rules. Not as antecedent, not as consequent. Why did the star of the dataset vanish?

Because lift refuses to be impressed by popularity. For "X → mineral water" to clear lift 3, X-buyers would need to buy water at 3 × 0.238 ≈ 71% — and nothing in a grocery basket predicts water that strongly, because everyone buys water nearly independently of everything else. Water pairs with pasta at roughly its baseline rate; the naive rule "pasta → water" would show healthy confidence (near 0.24+) and lift ≈ 1: the bread → milk illusion of Section 2, at scale. The min_lift = 3 gate quietly deleted every such rule — which is precisely its job.

Rule shapeConfidenceLiftVerdict
fromage blanc → honey (real #1)0.245 — modest5.16Genuine association — honey buyers' rare partner
any-X → mineral water (hypothetical)≈ 0.24+ — looks fine≈ 1.0Popular-item illusion — deleted by the lift gate
eggs → milk (hand table)0.50 — even better0.83Anti-association — the pair repels

The pattern generalises into this Part's central discipline: confidence is P(Y|X); lift is P(Y|X) ÷ P(Y). Only the division removes the popularity subsidy. Every rule you ever present should carry its lift — and its support, the sample size that makes both meaningful.

8. Strengths, Limits & the Road Ahead

StrengthsLimitations
Simple, interpretable, directly actionable rules Combinatorial cost even after pruning — long itemsets explode
Unsupervised — needs no labels, no distances Thresholds are business choices with no automatic setting
Lift separates real associations from popular-item illusions Pair-only view here; multi-item baskets need patience or Eclat
A mature, battle-tested algorithm (since 1994) Rules are correlations — a bundle hint, never a causal claim

The next chapter keeps the data and the support counting, throws away confidence and lift, and asks a leaner question: not "what implies what" but "which itemsets are frequent, full stop". Eclat's vertical transaction-id layout makes that question brutally fast — and the comparison between the two philosophies is Part V's closing lesson.

🛠 Hands-On: Audit the Nine RulesIntermediate

A category manager asks for the rules behind a shelf reshuffle — and wants the traps demonstrated before the meeting. Produce the hand audit, the real run, and the one-slide recommendation.

Steps

  1. By hand: from the five-basket table, compute support/confidence/lift for milk → bread and bread → eggs; explain why direction changes the numbers.
  2. By hand: show cheese → eggs has confidence 1.00; state the two reasons it is worthless.
  3. Run Apriori with the source thresholds; verify 9 rules; sort by lift and by support.
  4. Relax min_lift to 1.0; count the rules and find the first mineral-water illusion; compute its lift by hand from the output.
  5. Identify the volume rule (herb & pepper → ground beef) and convert support 0.0160 into baskets per week.
  6. Write the one-slide recommendation: the two rules to act on (one boutique, one volume), the actions, and the causal caveat.
View Solution / Walkthrough
import pandas as pd
from apyori import apriori

dataset = pd.read_csv('Market_Basket_Optimisation.csv', header=None)
transactions = [[str(v) for v in row if pd.notna(v)] for row in dataset.values]

def run(min_support, min_conf, min_lift):
    rs = list(apriori(transactions=transactions,
                      min_support=min_support, min_confidence=min_conf,
                      min_lift=min_lift, min_length=2, max_length=2))
    rows = [(tuple(r[2][0][0])[0], tuple(r[2][0][1])[0],
             r[1], r[2][0][2], r[2][0][3]) for r in rs]
    return pd.DataFrame(rows, columns=['LHS','RHS','Supp','Conf','Lift'])

df = run(0.003, 0.2, 3)
print(len(df), 'rules'); print(df.sort_values('Lift', ascending=False).head())

loose = run(0.003, 0.2, 1.0)
print(len(loose), 'rules at lift>=1')
print(loose[loose.RHS == 'mineral water'].head())

Expected results (real):

  • Step 1: milk → bread: supp 3/5 = 0.60, conf 3/3 = 1.00, lift = 1.00/0.80 = 1.25; bread → eggs: supp 3/5, conf 3/4 = 0.75, lift = 0.75/0.80 = 0.94. Direction matters: X and Y swap the denominator.
  • Step 2: cheese → eggs: conf 1/1 = 1.00 — worthless because (a) support 1/5 = 0.20 is a single basket (sample of one), and (b) lift = 1.00/0.80 = 1.25 barely beats baseline.
  • Step 3: 9 rules; lift-sorted top = fromage blanc → honey (5.16); support-sorted top = herb & pepper → ground beef (0.0160).
  • Step 4: at lift ≥ 1 the rule count explodes; the water rules enter with lift ≈ 1 — e.g. some-X → mineral water at conf ≈ 0.24–0.35 and lift ≈ 1.0–1.5: the popular-item illusion, now visible in the output.
  • Step 5: 0.0160 × 7,501 ≈ 120 baskets per week (~17/day) — the volume that justifies moving shelf positions.
  • Step 6: act on fromage blanc → honey (co-placement; boutique margin) and herb & pepper → ground beef (bundle + recommender; volume) — with the caveat that association is correlation: test the intervention, don't assume the causal arrow.

🎯 Key Takeaways

1

Support = frequency worth counting, confidence = P(Y|X), lift = confidence ÷ P(Y) — the referee that strips out an item's popularity.

2

The hand table's three traps: conf 0.75/lift 1.25 (popular-item illusion), conf 0.50/lift 0.83 (repulsion), conf 1.00 on one basket (noise).

3

Apriori's principle — subsets of frequent sets are frequent — prunes the 2¹²⁰ wall level by level; the speed is foresight, not faster counting.

4

Thresholds are business decisions: support 0.003 = 3 sales/day; lift 3 = triple the baseline before a rule earns a meeting.

5

Nine real rules, and they are recipes: pasta+escalope, cream+chicken, tomato sauce+ground beef — behaviour decoded from baskets alone.

6

Mineral water — 24% of baskets — appears in no rule: nothing predicts it 3× better than baseline. That absence is lift doing its job.

Test Your Understanding

30 questions across the chapter — the three metrics by hand, the Apriori principle, thresholds, the real rules and the traps. Choose one answer per question, then check your answers.

Q1.An association rule X → Y states:

Q2.Support of a rule is defined as:

Q3.(Hand) In the five-basket table, support(bread ∧ milk) equals:

Q4.(Hand) Confidence(bread → milk) = 0.75 while confidence(milk → bread) equals:

Q5.Lift is computed as:

Q6.(Hand) bread → milk: confidence 0.75, lift 1.25. The verdict:

Q7.(Hand) eggs → milk has lift 0.83. This means:

Q8.(Hand) cheese → bread shows confidence 1.00. It is worthless because:

Q9.The combinatorial wall for 120 items is roughly:

Q10.The Apriori principle states:

Q11."Anti-monotone" support means:

Q12.The name "Apriori" refers to:

Q13.(Real) The source's min_support = 0.003 was rationalised as:

Q14.min_confidence was set deliberately low (0.2) because:

Q15.min_lift = 3 demands the rule make the consequent:

Q16.(Real) The run returned how many rules under the source thresholds?

Q17.(Real) The top rule by lift was:

Q18.(Real) The volume rule — 0.0160 support, ~120 baskets/week — was:

Q19.The nine real rules decode into human behaviour as:

Q20.(Real) Mineral water — the most popular item, 0.238 support — appears in none of the 9 rules because:

Q21.For apyori, transactions must be formatted as:

Q22.The classic 'nan' bug in this pipeline:

Q23.The three standard monetisations of association rules:

Q24.Confidence and lift differ precisely in that lift:

Q25.Direction matters for confidence but not for:

Q26.Relaxing min_lift from 3 to 1.0 would mainly add:

Q27.The Apriori search proceeds:

Q28.A rule with support 0.0033 in this dataset corresponds to about:

Q29.Association rules must never be presented as causal because:

Q30.The next chapter's Eclat differs from Apriori by dropping: