Part V — Association Rule Learning · Final Chapter

Chapter 22 · Eclat

Same 7,501 baskets, leaner question. Eclat — Equivalence Class Transformation of Lattice-orderings — drops arrows, confidence and lift entirely, and asks only: which itemsets are frequent? Its trick is to flip the data sideways, from baskets-listing-items to items-listing-baskets, so every support is one set intersection — computed by hand here, run for real at 951 itemsets, and timed against Apriori. The chapter, and Part V, close with the honest comparison of the two philosophies.

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

🎯 Learning Objectives

1. The Leaner Question

Chapter 21 produced rules: directional, lift-refereed statements — fromage blanc → honey — with three metrics to read together. Eclat asks for less and gets it faster: which itemsets (unordered sets of items) clear a minimum support? No arrow, no confidence, no lift. {mineral water, spaghetti} is frequent — full stop — with no claim about which causes which or whether the pairing beats baseline.

What does the leaner question cost and buy? It costs insight machinery: from frequent itemsets you can always compute Chapter 21's rules afterwards (confidence and lift are simple ratios over supports), but Eclat itself ranks nothing and referees nothing — its raw output, sorted by support, systematically promotes popular items. It buys speed and simplicity: fewer metrics, one counting operation, and — as Section 6 measures — a substantially faster search, which is exactly what you want when the itemset length grows or the basket count explodes.

📝
Note — same data, same week: all 7,501 baskets and 120 products return, thresholds match Chapter 21's spirit (min_support 0.004 ≈ 30 baskets ≈ 4+ sales/day), so every comparison between the chapters is apples-to-apples.

2. The Vertical Layout, by Hand

Apriori scanned baskets (rows) to count itemsets. Eclat flips the table sideways — the vertical (TID) layout: for each item, the list of transaction IDs containing it. Chapter 21's five baskets, transposed:

ItemTID list (baskets containing it)Count
bread1235—4
milk125——3
eggs2345—4
cheese5————1

And now the entire algorithm is one operation — set intersection:

supp(bread ∧ milk)  =  |tid(bread) ∩ tid(milk)| / 5  =  |{1,2,3,5} ∩ {1,2,5}| / 5  =  3/5 = 0.60
supp(milk ∧ eggs)  =  |{1,2,5} ∩ {2,3,4,5}| / 5  =  2/5 = 0.40
supp(bread ∧ milk ∧ eggs)  =  |{1,2,3,5} ∩ {1,2,5} ∩ {2,3,4,5}| / 5  =  |{2,5}| / 5 = 0.40
The same supports Chapter 21 extracted by scanning rows — here they fall out of intersections, and a triple costs one more intersection on top of its pair (intersect the pair's TID list with the third item's). Notice anti-monotonicity visually: the triple's TID list {2,5} is a subset of the pair's {1,2,5} — support can only shrink as sets grow, so Chapter 21's pruning principle applies unchanged.
Horizontal (Apriori scans) B1 B2 B3 B4 B5 bread, milk bread, milk, eggs bread, eggs milk, eggs bread, milk, eggs, cheese Vertical (Eclat intersects) bread → {1,2,3,5} milk → {1,2,5} eggs → {2,3,4,5} cheese → {5} {1,2,3,5} ∩ {1,2,5} = {1,2,5} supp(bread ∧ milk) = 3/5 = 0.60 — one intersection
Figure 22.1 — The transposition: rows of items become columns of basket IDs, and counting becomes set intersection.
💡
Pro Tip — why the name is terrifying and the idea isn't: "Equivalence Class Transformation of Lattice-orderings" describes grouping itemsets that share a prefix (the equivalence classes) and walking the lattice of prefixes depth-first. In practice: fix a first item, intersect onward, recurse — tidy bookkeeping over the intersections you just did by hand.

4. The Real Itemsets

FP-growth, min_support = 0.004 (≈ 30 baskets ≈ 4+ per day), itemsets of up to three items, on the real 7,501 baskets:

Itemset size1 (singles)2 (pairs)3 (triples)Total
Count114578259951

The top of each size class, by support (all real):

RankTop pairsSupportTop triplesSupport
1{mineral water, spaghetti}0.0597{ground beef, mineral water, spaghetti}0.0171
2{chocolate, mineral water}0.0527{chocolate, mineral water, spaghetti}0.0159
3{eggs, mineral water}0.0509{milk, mineral water, spaghetti}0.0157
4{milk, mineral water}0.0480{eggs, mineral water, spaghetti}0.0143
5{ground beef, mineral water}0.0409{chocolate, milk, mineral water}0.0140
6{ground beef, spaghetti}0.0392{chocolate, eggs, mineral water}0.0135

Three readings, each a Part V lesson in miniature:

🌎
Real World — where support-only mining earns its keep: inventory positioning, planogram design and demand forecasting want exactly this output: which item sets move together in volume, no arrows needed. Cross-sell recommenders and shelf-pair discoveries want lift. Mature retail analytics runs both on the same baskets — the question picks the tool.
📋 Stable content — Reviewed: August 2026

5. Implementation in Python

The modern vertical-family pipeline is mlxtend (pip install mlxtend): encode, mine, sort:

import pandas as pd
from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import fpgrowth

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]          # blanks stripped (Ch 21's bug)

# 1. One-hot encode: rows = baskets, columns = 120 items, cells = True/False
te = TransactionEncoder()
df = pd.DataFrame(te.fit(transactions).transform(transactions),
                  columns=te.columns_)              # shape (7501, 120)

# 2. Mine frequent itemsets — FP-growth, the vertical family's industrial engine
fis = fpgrowth(df, min_support=0.004, use_colnames=True, max_len=3)
fis['size'] = fis['itemsets'].apply(len)            # 951 rows: 114 / 578 / 259
print(fis[fis['size'] == 2]
        .sort_values('support', ascending=False)
        .head())                                    # top pairs, water-led

# 3. Attach lift when insight is needed — supports are all you need for the ratios
#    conf(X->Y) = supp(X,Y)/supp(X);  lift = supp(X,Y)/(supp(X)·supp(Y))

Listing 22.1 — The mlxtend pipeline (source's apyori variant noted in Section 3).

💡
Pro Tip: mlxtend also ships frequent_patterns.apriori on the same one-hot frame — one line to A/B the two engines on identical data, which is exactly how Section 6's timing was measured. Note both expect the boolean one-hot matrix, not lists of lists (that was apyori's format).

6. Speed: the Real Motivation

Same baskets, same min_support = 0.003, same one-hot matrix — timed (real):

EngineLayout & strategyTime (real)
FP-growth (vertical family)Compressed prefix tree; database read twice, then memory arithmetic0.067 s
Apriori (mlxtend, horizontal)Level-wise candidate generation, repeated scans0.238 s

A 3.5× gap on a toy-sized dataset, and the gap widens with data: Apriori's cost scales with candidate counts per level (each level re-derives supports from scratch), while the vertical family reuses every intersection's result downward through the prefix tree — support of {water, spaghetti, ground beef} is computed from {water, spaghetti}'s list intersected with ground beef's, never from the raw database. On million-basket retail data this is the difference between an interactive tool and an overnight job.

📝
Note — the honest footnote: strict original Eclat can exhaust memory on dense data (TID lists get long), which is why its compressed-tree descendants (FP-growth, and eclat-style bitset engines) are what production actually runs. The family name survives; the exact data structure evolved.

7. Apriori vs Eclat — Settled

Apriori (Ch 21)Eclat / vertical family (Ch 22)
Question"What implies what?" — directional rules"What is frequent?" — unordered itemsets
MetricsSupport, confidence, liftSupport only (lift computable afterwards from supports)
Data layoutHorizontal — baskets listing itemsVertical — items listing basket IDs; counting = intersection
SearchBreadth-first, level-wise, repeated scansDepth-first through prefix groups; database touched ~once
PruningAnti-monotone subset ruleSame rule — layout changes, mathematics doesn't
Speed (real, this data)0.238 s0.067 s — and the gap widens with scale
Raw-output biasLift-refereed — insights by association strengthSupport-ranked — a popularity contest (water everywhere)
Reach for it whenCross-sell discovery, shelf pairs, recommender hintsVolume reporting, inventory, planograms, big baskets, long itemsets

The professional's summary, one sentence each: Apriori answers "why would a customer buy Y" (with lift as the lie detector); Eclat answers "what moves together in bulk" (with speed as the enabler). Same baskets, complementary reports — and the rule mining journey of this course ends with both on the table.

8. Part V Retrospective & the Road Ahead

Two chapters, one dataset, two philosophies of co-occurrence: rules with a referee (fromage blanc → honey, lift 5.16 — discovery) and itemsets with speed (water + spaghetti, support 0.0597 — volume). Together they close the unsupervised arc that Part IV opened: clustering asked who groups, association asked what groups, and neither needed a single label.

Part VI changes the shape of time itself. So far every dataset was static — a table to be studied. Reinforcement learning makes data arrive as consequences of actions: an ad shown, a reward observed, a strategy updated — the exploration-versus-exploitation dilemma, the multi-armed bandit, and Upper Confidence Bound, where an algorithm must gamble wisely rather than analyse quietly.

🛠 Hands-On: Flip the TableIntermediate

The inventory team wants the volume report the lift-based analysis never showed them. Build the vertical layout by hand, run the real itemset search, attach lift after the fact, and deliver the two-report summary.

Steps

  1. By hand: build the TID lists for the five-basket table; verify supp(bread ∧ milk) = 0.60 and supp(bread ∧ milk ∧ eggs) = 0.40 by intersection.
  2. By hand: compute supp(cheese ∧ eggs) via {5} ∩ {2,3,4,5}; explain why cheese joins nothing.
  3. One-hot encode the real dataset with TransactionEncoder; report the matrix shape and spot-check row 1.
  4. Run fpgrowth at min_support = 0.004, max_len = 3; report the 114/578/259 distribution; list the top 3 pairs and triples.
  5. For {mineral water, spaghetti} and {ground beef, spaghetti}, compute lift from supports alone; compare with the hand expectation from Chapter 21.
  6. Time fpgrowth vs mlxtend's apriori at 0.003; write the two-report recommendation (volume report + insight report) in four sentences.
View Solution / Walkthrough
import pandas as pd, time
from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import fpgrowth, 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]
te = TransactionEncoder()
df = pd.DataFrame(te.fit(transactions).transform(transactions), columns=te.columns_)

fis = fpgrowth(df, min_support=0.004, use_colnames=True, max_len=3)
fis['size'] = fis['itemsets'].apply(len)
print(fis['size'].value_counts().sort_index())     # 114 / 578 / 259
print(fis[fis['size']==2].nlargest(3, 'support'))

# Step 5: lift from supports
sup = lambda items: fis[fis.itemsets == frozenset(items)].support.iloc[0]
sw, ss, sg = sup(['mineral water']), sup(['spaghetti']), sup(['ground beef'])
print('lift(water,spag) = %.2f' % (sup(['mineral water','spaghetti'])/(sw*ss)))
print('lift(beef,spag)  = %.2f' % (sup(['ground beef','spaghetti'])/(sg*ss)))

t0 = time.time(); fpgrowth(df, min_support=0.003, use_colnames=True); t1 = time.time()
apriori(df, min_support=0.003, use_colnames=True); t2 = time.time()
print('fpgrowth %.3fs  apriori %.3fs' % (t1-t0, t2-t1))

Expected results (real):

  • Steps 1–2: tid lists as in Section 2; {5} ∩ {2,3,4,5} = {5} → supp 0.20 — cheese is a one-basket item whose intersections can only shrink; anti-monotonicity in action.
  • Steps 3–4: matrix (7501, 120); 951 itemsets; top pairs water-led — {water, spaghetti} 0.0597, {chocolate, water} 0.0527, {eggs, water} 0.0509; top triple {ground beef, water, spaghetti} 0.0171.
  • Step 5: lift(water, spaghetti) = 0.0597 / (0.2384 × 0.1741) ≈ 1.44 — positive but far below Chapter 21's gate; lift(ground beef, spaghetti) = 0.0392 / (0.0983 × 0.1741) ≈ 2.29 — the same pair that carried rule 8's 3.29 with herb & pepper. Volume pairs are weakly associated; insight lives elsewhere.
  • Step 6: fpgrowth ≈ 0.07 s vs apriori ≈ 0.24 s; the recommendation: hand inventory the support-ranked itemset report, hand marketing the lift-gated rule report — one dataset, two questions, two tools.

🎯 Key Takeaways

1

Eclat's question is leaner: which itemsets are frequent — no arrows, no confidence, no lift in the raw output.

2

The vertical flip: items list basket IDs, and every support is one set intersection — supp(bread ∧ milk) = |{1,2,3,5} ∩ {1,2,5}|/5 = 0.60.

3

Anti-monotonicity survives the transposition — triple TID lists are subsets of their pairs'; Apriori's pruning applies unchanged.

4

951 real itemsets (114/578/259) — and every top pair holds mineral water: support rankings are popularity contests; volume ≠ insight.

5

Water + spaghetti: support 0.0597 (massive) but lift 1.44 (modest) — the two metrics crown different champions; run both reports.

6

Speed is the honest motivation: 0.067 s vs 0.238 s here, widening with scale — the prefix-tree family is what production runs.

Test Your Understanding

30 questions across the chapter — the vertical layout, intersections by hand, the real itemsets, the speed comparison and the two philosophies. Choose one answer per question, then check your answers.

Q1.Eclat's question, compared with Apriori's, drops:

Q2.The vertical (TID) layout stores, for each item:

Q3.(Hand) With tid(bread) = {1,2,3,5} and tid(milk) = {1,2,5}, supp(bread ∧ milk) over 5 baskets is:

Q4.(Hand) The triple's TID list {2,5} is a subset of the pair's {1,2,5}. This demonstrates:

Q5.A triple's support in Eclat is computed from:

Q6.(Hand) supp(cheese ∧ eggs) = |{5} ∩ {2,3,4,5}|/5 equals:

Q7."Equivalence Class Transformation of Lattice-orderings" in practice means:

Q8.The source code's honest confession (Section 3):

Q9.(Real) At min_support 0.004, max_len 3, the search returned:

Q10.(Real) The #1 pair by support was:

Q11.(Real) Every top pair and every top triple contains mineral water because:

Q12.(Real) Chapter 21's insight champion {fromage blanc, honey} — lift 5.16 — is absent here because:

Q13.(Real) lift(mineral water → spaghetti), computed from supports alone, is about:

Q14.That lift can be computed after mining from supports alone shows:

Q15.(Real) The timing run gave fpgrowth 0.067 s vs Apriori 0.238 s. The ratio and its future:

Q16.The one-hot matrix that mlxtend expects has:

Q17.FP-growth, the engine this chapter actually runs, belongs to Eclat's family because both:

Q18.The honest footnote about strict original Eclat at scale:

Q19.Anti-monotonicity (pruning) in Eclat:

Q20.The business question that picks Eclat over Apriori:

Q21.Apriori's search versus Eclat's, in one line each:

Q22.(Real) The top triples were all {mineral water, spaghetti, X} shapes. The inventory-team reading:

Q23.min_support = 0.004 on 7,501 baskets corresponds to roughly:

Q24.The TransactionEncoder's role in the pipeline:

Q25.What Eclat's raw output — support-ranked itemsets — systematically promotes:

Q26.(Real) lift(ground beef → spaghetti) ≈ 2.29 versus lift(water → spaghetti) ≈ 1.44. Together they illustrate:

Q27.The two-report recommendation this chapter lands on:

Q28.Part V's arc, in one sentence:

Q29.Part VI's fundamental change from every preceding part:

Q30.The next chapter's first algorithm, facing the exploration-exploitation dilemma, is: