Ever stood in a supermarket checkout line, looked at the conveyor belt, and noticed an odd combination of items? Maybe diapers and beer? This isn’t just a coincidence; it’s a famous, though perhaps legendary, example of “market basket analysis.” Businesses are obsessed with finding these hidden patterns, and for decades, one of the most fundamental tools they’ve used is a clever algorithm that acts like a data detective. Itโs called the Apriori algorithm, and itโs a classic method for finding interesting relationships hidden within mountains of transaction data.
This algorithm is a cornerstone of association rule learning. It was proposed in 1994 by R. Agrawal and R. Srikant, and its genius lies in its efficiency. Instead of checking every single possible combination of items (which would be computationally impossible), it uses a simple, common-sense principle to intelligently “prune” the search, focusing only on item combinations that have a chance of being important.
Table of Contents
- What are we even looking for? Understanding association rules
- The core idea: The Apriori principle
- Generating frequent itemsets in a level-wise manner
- A closer look: The join and prune operations
- 1. The join step
- 2. The prune step
- Case study: A mini-market walkthrough
- Iteration 1: Finding L1
- Iteration 2: Finding L2
- Iteration 3: Finding L3
- Generating strong association rules
What are we even looking for? Understanding association rules
Before we unleash our detective, we need to know what “clues” we’re looking for. The goal is to generate “association rules,” which are simple “if-then” statements. The classic example is:
If {Diapers}, then {Beer}
To decide if a rule is “strong” or just a random fluke, we use two main metrics:
- Support: This measures how popular an itemset is. It’s the percentage of total transactions that contain all items in that set. For example, if {Diapers, Beer} appears in 100 out of 1,000 transactions, its support is 10%.
- Confidence: This measures the rule’s predictive power. It answers, “When a customer buys {Diapers}, what’s the probability they *also* buy {Beer}?” If 150 customers buy diapers (Support of {Diapers}), and 100 of them also buy beer (Support of {Diapers, Beer}), the confidence is 100 / 150, or 66.7%.
Our goal is to find all rules that meet a minimum support threshold (e.g., “I only care about itemsets that appear in at least 5% of transactions”) and a minimum confidence threshold (e.g., “I only want rules that are at least 60% reliable”).
The core idea: The Apriori principle
This is where the magic happens. Trying to find the support for every single combination-{Milk, Bread}, {Milk, Eggs}, {Milk, Cereal}, {Milk, Bread, Eggs}, {Milk, Bread, Cereal}, {Milk, Bread, Eggs, Cereal}, and so on-would take an eternity.
The Apriori algorithm is built on a simple, brilliant observation known as the Apriori Principle:
If an itemset is infrequent, then all of its supersets must also be infrequent.
Think about it. If {Ketchup} is only bought by 1% of customers (making it “infrequent” by our 5% minimum support threshold), is there any point in checking the support for {Ketchup, Buns, Mustard}? No. It’s impossible for that *more specific* combination to appear *more frequently* than {Ketchup} alone. This principle allows the algorithm to “prune” millions of potential combinations without ever bothering to count them, saving an enormous amount of computational power.
Generating frequent itemsets in a level-wise manner
The Apriori algorithm works in “levels,” or iterations. It starts small and builds its way up, pruning at every single step.
Level 1 (C1 โ L1): Find all frequent 1-itemsets
- Candidate Generation (C1): The algorithm first scans the entire database and makes a list of *every single unique item* sold. This is the candidate list, C1.
- Support Counting: It counts the support for each of those individual items.
- Pruning (L1): It compares each item’s support to the `min_support` threshold. Any item that doesn’t meet the threshold is discarded. The remaining list of frequent 1-itemsets is called L1.
Level 2 (L1 โ C2 โ L2): Find all frequent 2-itemsets
- Join Step (C2): It takes the L1 list and “joins” every item with every other item to create a candidate list of 2-itemsets (C2). For example, if L1 = {{Milk}, {Bread}, {Diapers}}, C2 = {{Milk, Bread}, {Milk, Diapers}, {Bread, Diapers}}.
- Support Counting: It scans the database a *second time* to count the support for every candidate pair in C2.
- Pruning (L2): It discards all pairs from C2 that don’t meet `min_support`. The remaining list is L2.
Level 3 (L2 โ C3 โ L3): Find all frequent 3-itemsets
- Join Step (C3): It takes the L2 list and joins it with itself to create 3-itemset candidates.
- Prune Step (Apriori): THIS is the crucial step. Before it wastes time scanning the database again, it applies the Apriori principle.
- Support Counting: It scans the database a *third time* to count the support for the *surviving* candidates in C3.
- Pruning (L3): It discards all triplets from C3 that don’t meet `min_support`, creating L3.
This process repeats (L3 โ C4 โ L4…) until a level is reached where no new frequent itemsets can be found (the L list becomes empty). The final result is a complete collection of all itemsets that meet our minimum support threshold.
A closer look: The join and prune operations
The “Level 3” step above is the most important part to understand. It has two distinct operations: the Join Step and the Prune Step. Let’s say, after Level 2, our list of frequent 2-itemsets (L2) is:
L2 = {{Bread, Milk}, {Bread, Diapers}, {Milk, Diapers}, {Milk, Beer}}
Now we want to generate C3, our 3-itemset candidates.
1. The join step
The algorithm joins L2 with itself. A rule for joining is that two itemsets are joined only if they share all items except the last one (in lexicographical order).
- {Bread, Milk} joins with {Bread, Diapers} โ Candidate: {Bread, Milk, Diapers}
- {Milk, Diapers} joins with {Milk, Beer} โ Candidate: {Milk, Diapers, Beer}
So, our initial candidate list (C3) is {{Bread, Milk, Diapers}, {Milk, Diapers, Beer}}.
2. The prune step
Now, *before* we scan the whole database, we apply the Apriori principle to this C3 list.
Candidate 1: {Bread, Milk, Diapers}
- The algorithm checks all its 2-item subsets: {Bread, Milk}, {Bread, Diapers}, and {Milk, Diapers}.
- Are all three of these present in our L2 list?
- {Bread, Milk} โ Yes.
- {Bread, Diapers} โ Yes.
- {Milk, Diapers} โ Yes.
- Result: We keep {Bread, Milk, Diapers}. It has a *chance* of being frequent.
Candidate 2: {Milk, Diapers, Beer}
- The algorithm checks all its 2-item subsets: {Milk, Diapers}, {Milk, Beer}, and {Diapers, Beer}.
- Are all three of these present in our L2 list?
- {Milk, Diapers} โ Yes.
- {Milk, Beer} โ Yes.
- {Diapers, Beer} โ No! This pair was *not* in our L2 list, meaning it was infrequent.
- Result: Because one of its subsets is infrequent, the Apriori principle guarantees that {Milk, Diapers, Beer} *cannot* be frequent. We prune it from the C3 list *without ever counting it*.
Thanks to this pruning step, the only 3-itemset we actually need to count in the database is {Bread, Milk, Diapers}. This is what makes Apriori so much faster than a brute-force approach.
Case study: A mini-market walkthrough
Let’s walk through a tiny example. Here is our database of 5 transactions. We will set our Minimum Support = 3 (meaning it must appear in at least 3 transactions).
[Image: A simple table showing 5 transactions and the items in each]
- T1: {Milk, Bread, Diapers}
- T2: {Milk, Bread, Beer, Eggs}
- T3: {Milk, Diapers, Beer}
- T4: {Bread, Diapers, Eggs}
- T5: {Milk, Bread, Diapers, Beer}
Iteration 1: Finding L1
We scan the database to count 1-itemsets (C1).
- {Milk}: 4
- {Bread}: 4
- {Diapers}: 4
- {Beer}: 3
- {Eggs}: 2
We prune anyone with support < 3. {Eggs} is removed.
L1 = {{Milk}, {Bread}, {Diapers}, {Beer}}
Iteration 2: Finding L2
We join L1 with itself to create C2.
- Candidates (C2): {Milk, Bread}, {Milk, Diapers}, {Milk, Beer}, {Bread, Diapers}, {Bread, Beer}, {Diapers, Beer}
We scan the database again to count these pairs.
- {Milk, Bread}: 3 (in T1, T2, T5)
- {Milk, Diapers}: 3 (in T1, T3, T5)
- {Milk, Beer}: 3 (in T2, T3, T5)
- {Bread, Diapers}: 3 (in T1, T4, T5)
- {Bread, Beer}: 2 (in T2, T5)
- {Diapers, Beer}: 3 (in T3, T5)
We prune anyone with support < 3. {Bread, Beer} is removed.
L2 = {{Milk, Bread}, {Milk, Diapers}, {Milk, Beer}, {Bread, Diapers}, {Diapers, Beer}}
Iteration 3: Finding L3
We join L2 with itself to create C3.
- Join Step: Based on our join rules, the candidates are {Milk, Bread, Diapers}, {Milk, Diapers, Beer}, and {Bread, Diapers, Beer}. (Note: {Milk, Bread, Beer} is not a candidate because {Bread, Beer} is not in L2).
- Prune Step: Let’s check the subsets of our candidates.
- {Milk, Bread, Diapers}: Subsets are {Milk, Bread}, {Milk, Diapers}, {Bread, Diapers}. All are in L2. Keep.
- {Milk, Diapers, Beer}: Subsets are {Milk, Diapers}, {Milk, Beer}, {Diapers, Beer}. All are in L2. Keep.
- {Bread, Diapers, Beer}: Subsets are {Bread, Diapers}, {Bread, Beer}, {Diapers, Beer}. Wait! {Bread, Beer} is *not* in L2. Prune.
Our final C3 list is just {{Milk, Bread, Diapers}, {Milk, Diapers, Beer}}. Now we scan the database a final time to count just these.
- {Milk, Bread, Diapers}: 2 (in T1, T5)
- {Milk, Diapers, Beer}: 2 (in T3, T5)
Neither candidate meets our `min_support` of 3. Both are removed.
L3 = {} (Empty set)
The algorithm stops. Our final collection of all frequent itemsets is the union of L1 and L2.
Generating strong association rules
The hard work is over. We now have our list of frequent itemsets. The final step is to generate rules from them. We take every frequent itemset and generate all possible rules, checking if they meet our Minimum Confidence threshold. Let’s set Min Confidence = 75%.
Let’s use our frequent itemset {Milk, Diapers} (Support = 3).
Two rules are possible:
- Rule: If {Milk} โ then {Diapers}
- Confidence = Support({Milk, Diapers}) / Support({Milk})
- Confidence = 3 / 4 = 75%
- This meets our 75% threshold. This is a strong rule!
- Rule: If {Diapers} โ then {Milk}
- Confidence = Support({Milk, Diapers}) / Support({Diapers})
- Confidence = 3 / 4 = 75%
- This *also* meets our 75% threshold. This is also a strong rule!
Let’s try another one: {Milk, Beer} (Support = 3).
- Rule: If {Milk} โ then {Beer}
- Confidence = Support({Milk, Beer}) / Support({Milk})
- Confidence = 3 / 4 = 75%. Strong rule.
- Rule: If {Beer} โ then {Milk}
- Confidence = Support({Milk, Beer}) / Support({Beer})
- Confidence = 3 / 3 = 100%. Very strong rule! (In our tiny dataset, everyone who bought beer *also* bought milk).
We would repeat this process for all frequent itemsets found, giving the business a final list of actionable, data-backed rules for promotions, store layout, or product recommendations.
What do you think? We saw that {Beer} โ {Milk} had 100% confidence, but {Milk} โ {Beer} only had 75%. Why do you think this difference is so important for a store manager to understand?
Leave a Reply