Lesson 10 / 10
Association Rules
Measuring the 166 rules that pass the support floor in the 1,329 baskets of the fictional subscriber table: all six of the top six rules by confidence name the same frequent item as the consequent and their lifts are between 1.0139 and 1.0489; of the 97 rules with confidence above 0.50, 75 have a lift closer to 1 than 0.05. The baseline is the support that random matching produces — when the item columns are shuffled separately, the support of 0.1279 for delay and installment plan does not exceed 0.0467 in any of sixty shuffles, but the support of a rule with a confidence of 0.9246 stays inside the random range. The course wrap-up gathers the baseline, the method's number and the difference of eleven lessons in a single table.
Published Updated
In this lesson
- Applies the support, confidence and lift measures to the same rule set and counts how many of the high-confidence rules come only from a frequent item
- Builds the support that random matching produces as the baseline and shows that lift is the closed form of this baseline
- Measures that the support floor is a decision and that a real dependency that stays under the floor never enters the rule list
Contents
The previous lesson asked whether a row stands alone and looked for the answer in five numeric columns. Subscriber records have one more side that is not a number: which events occur together in that subscriber within the period. The question keeps its form: is the joint occurrence of two events a finding, or can it already be explained by how frequent the two are separately.
Association rules answer this question with three measures, and one of them misleads when taken alone. The number of the lesson is how many of the rules chosen by confidence come only from a frequent item; its baseline is the support that comes out when the items are matched to each other at random.
- DR36. The set is the fictional subscriber table of the Introduction to Machine Learning, Data Preparation and Feature Engineering, and Supervised Learning courses, seed 20260218, 1,329 subscribers. Each subscriber is read as an itemset: the service records that occur in that subscriber within the period.
- DR37. The dependencies are deliberately placed in the generator, and which of them is real is known only for measurement; no measure sees this information.
- DR38. Three measures: support, the share of the baskets that contain the itemset; confidence, the share of the baskets containing the antecedent that also contain the consequent; lift, the confidence divided by the support of the consequent. Lift is read as a departure from 1; 1 is the absence of association.
- DR39. The baseline is the support that random matching produces: the column of each item is shuffled separately across the baskets, the frequencies are kept, and the bond of occurring together is broken.
- DR40. This lesson builds only the measures of extracting rules; which rule will be shown to whom is left to the Reinforcement Learning and Recommender Systems course.
Baskets and Item Supports
# COMMON — FICTIONAL. The subscriber table of the Introduction to Machine Learning, Data Preparation and Feature Engineering, and Supervised Learning courses is rebuilt with the same seed; this time the # service records of each subscriber within the period are kept as an ITEMSET. # The dependencies are PLACED in the generator ON PURPOSE and are known only for measurement. SEED, RAW, M32 = 20260218, 1400, 0xFFFFFFFF def rng(t): x = ((t ^ (t >> 16)) * 2246822507) & M32 x = ((x ^ (x >> 13)) * 3266489909) & M32 s = [(x ^ (x >> 16)) & M32] def next_value(): s[0] = (s[0] * 1664525 + 1013904223) & M32 return s[0] / 4294967296 return next_value SUBSCRIBERS = [] for i in range(RAW): r = rng(SEED + i) r(), r(), r() # region, household and satisfaction are not read in this lesson if r() >= 0.046: SUBSCRIBERS.append(10001 + i) # (item, condition item, probability when the condition holds, probability when it does not) TABLE = [("mobile_notification", None, 0.91, 0.91), # independent, very frequent ("outage_notification", None, 0.55, 0.55), # independent, very frequent ("delay", None, 0.22, 0.22), ("installment_plan", "delay", 0.58, 0.04), # placed dependency ("auto_payment", "delay", 0.13, 0.47), # placed inverse dependency ("objection", None, 0.16, 0.16), ("call_center", "objection", 0.90, 0.58), # weak dependency, frequent item ("address_change", None, 0.08, 0.08), ("meter_replacement", "address_change", 0.36, 0.09), ("field_visit", "meter_replacement", 0.86, 0.19), ("tariff_change", None, 0.12, 0.12), ("illicit_use_report", None, 0.05, 0.05)] ITEMS = [t[0] for t in TABLE] BASKETS = [] for id in SUBSCRIBERS: r, s = rng(SEED + 37000 + id), set() for item, cond, p1, p0 in TABLE: if r() < (p1 if cond is None or cond in s else p0): s.add(item) BASKETS.append(s) N = len(BASKETS) D1 = {o: sum(1 for s in BASKETS if o in s) / N for o in ITEMS} print(f"fictional: {N} subscriber baskets, {len(ITEMS)} items; " f"mean {sum(len(s) for s in BASKETS) / N:.2f} items per basket") for o in sorted(ITEMS, key=lambda x: -D1[x]): print(f" {o:<20} support {D1[o]:.4f}")
fictional: 1329 subscriber baskets, 12 items; mean 3.68 items per basket mobile_notification support 0.9120 call_center support 0.6388 outage_notification support 0.5403 auto_payment support 0.3973 field_visit support 0.2671 delay support 0.2220 installment_plan support 0.1670 objection support 0.1497 meter_replacement support 0.1332 tariff_change support 0.1151 address_change support 0.0835 illicit_use_report support 0.0512
The supports are spread over a wide range: the most frequent item is in 0.9120 of the baskets, the rarest in 0.0512. This distribution sets the trap of the next section.
Confidence Alone
# Extracting rules: one or two items in the antecedent, one item in the consequent. The support floor # is a decision and is fixed before measuring. from itertools import combinations SUPPORT_FLOOR, CONFIDENCE_FLOOR = 0.05, 0.50 def support(itemset): return sum(1 for s in BASKETS if itemset <= s) / N RULES = [] antecedents = [frozenset([o]) for o in ITEMS] + \ [frozenset(c) for c in combinations(ITEMS, 2)] for X in antecedents: dx = support(X) if dx < SUPPORT_FLOOR: continue for y in ITEMS: if y in X: continue d = support(X | {y}) if d < SUPPORT_FLOOR: continue g = d / dx RULES.append({"X": X, "y": y, "support": d, "confidence": g, "lift": g / D1[y]}) SINGLE = sum(1 for k in RULES if len(k["X"]) == 1) near = [k for k in RULES if abs(k["lift"] - 1) <= 0.05] print(f"support floor {SUPPORT_FLOOR:.2f} and above: {len(RULES)} rules " f"({SINGLE} single-item antecedents, {len(RULES) - SINGLE} two-item)") print(f"rules with lift within 0.05 of 1: {len(near)} " f"({len(near) / len(RULES):.4f})\n") def show(title, ranked, count=8): print(title) print(f" {'rule':<46} {'support':>7} {'confidence':>10} {'lift':>9}") for k in ranked[:count]: name = " + ".join(sorted(k["X"])) + " -> " + k["y"] print(f" {name:<46} {k['support']:>7.4f} {k['confidence']:>10.4f} " f"{k['lift']:>9.4f}") by_confidence = sorted(RULES, key=lambda k: (-k["confidence"], sorted(k["X"]), k["y"])) show("top six rules by confidence", by_confidence, 6) top = [k for k in by_confidence if k["confidence"] >= CONFIDENCE_FLOOR] frequent = sum(1 for k in top if abs(k["lift"] - 1) <= 0.05) print(f"\nthere are {len(top)} rules with confidence {CONFIDENCE_FLOOR:.2f} and above; " f"{frequent} of them have a lift within 0.05 of 1 ({frequent / len(top):.4f})") last = {} for k in top: last[k["y"]] = last.get(k["y"], 0) + 1 print(" by consequent item: " + ", ".join( f"{o} {last[o]} (support {D1[o]:.4f})" for o in sorted(last, key=lambda x: -last[x]))) single = [k for k in RULES if len(k["X"]) == 1] show("\ntop four rules by lift (single-item antecedent)", sorted(single, key=lambda k: (-k["lift"], sorted(k["X"]), k["y"])), 4) lowest = min(single, key=lambda k: (k["lift"], k["confidence"], sorted(k["X"]), k["y"])) print(f" lowest-lift rule: {' + '.join(sorted(lowest['X']))} -> " f"{lowest['y']}, support {lowest['support']:.4f}, " f"confidence {lowest['confidence']:.4f}, lift {lowest['lift']:.4f}")
support floor 0.05 and above: 166 rules (64 single-item antecedents, 102 two-item) rules with lift within 0.05 of 1: 115 (0.6928) top six rules by confidence rule support confidence lift outage_notification + tariff_change -> mobile_notification 0.0662 0.9565 1.0489 tariff_change -> mobile_notification 0.1076 0.9346 1.0249 auto_payment + objection -> mobile_notification 0.0602 0.9302 1.0200 call_center + tariff_change -> mobile_notification 0.0700 0.9300 1.0198 address_change -> mobile_notification 0.0775 0.9279 1.0175 objection -> mobile_notification 0.1384 0.9246 1.0139 there are 97 rules with confidence 0.50 and above; 75 of them have a lift within 0.05 of 1 (0.7732) by consequent item: mobile_notification 32 (support 0.9120), call_center 28 (support 0.6388), outage_notification 25 (support 0.5403), field_visit 4 (support 0.2671), delay 4 (support 0.2220), installment_plan 4 (support 0.1670) top four rules by lift (single-item antecedent) rule support confidence lift installment_plan -> delay 0.1279 0.7658 3.4498 delay -> installment_plan 0.1279 0.5763 3.4498 field_visit -> meter_replacement 0.1159 0.4338 3.2572 meter_replacement -> field_visit 0.1159 0.8701 3.2572 lowest-lift rule: outage_notification -> field_visit, support 0.1362, confidence 0.2521, lift 0.9437
All six of the top six rules by confidence have the same item as the consequent: the record that occurs in 0.9120 of the baskets. Their confidences are 0.9246–0.9565 and their antecedents have nothing to do with each other. Whichever antecedent is chosen, the confidence settles around 0.91; the lift column says this, between 1.0139 and 1.0489.
There are 97 rules with confidence above 0.50 and 75 of them have a lift closer to 1 than 0.05: 0.7732. By consequent item, 32, 28 and 25 rules point to the three most frequent items — 85 of the 97. Raising the threshold does not fix this; as the threshold rises, only frequent items can pass it.
Sorting by lift gives another list: two pairs in the top four, each pair in both directions — installment plan and delay 3.4498, meter replacement and field visit 3.2572. In the same rows confidence is read as asymmetric and lift as symmetric: the confidence from delay to installment plan is 0.5763, in the opposite direction 0.7658, and the lift is the same in both. The lowest lift is 0.9437; a lift that falls below 1 says that occurring together is rarer than chance.
The Random Matching Baseline
# BASELINE: the support that random matching produces. The column of each item is # shuffled separately across the baskets; item frequencies are kept exactly, # the bond of occurring together is broken. Lift is the closed form of this baseline. BIT = {o: 1 << b for b, o in enumerate(ITEMS)} COLUMNS = [[1 if o in s else 0 for s in BASKETS] for o in ITEMS] REPEATS = 60 def superset_sums(masks): # For each pattern, the number of baskets that cover it: a single 12-bit pass. h = [0] * (1 << len(ITEMS)) for m in masks: h[m] += 1 for b in range(len(ITEMS)): for m in range(len(h)): if not m & (1 << b): h[m] += h[m | (1 << b)] return h def shuffled(seed): rr, new = rng(seed), [0] * N for b, col in enumerate(COLUMNS): c = list(col) for i in range(N - 1, 0, -1): j = int(rr() * (i + 1)) c[i], c[j] = c[j], c[i] for i in range(N): if c[i]: new[i] |= 1 << b return new RANDOM_RUNS = [superset_sums(shuffled(SEED + 41000 + t)) for t in range(REPEATS)] EXAMPLES = (("delay", "installment_plan"), ("meter_replacement", "field_visit"), ("tariff_change", "mobile_notification"), ("objection", "mobile_notification")) pattern = lambda k: sum(BIT[o] for o in k["X"]) | BIT[k["y"]] random_support = lambda k: [R[pattern(k)] / N for R in RANDOM_RUNS] print(f"{REPEATS} shuffles: the support random matching produces\n") print(f" {'rule':<38} {'real':>7} {'random':>9} {'highest':>7} {'lift':>9}") for k in sorted((k for k in RULES if len(k["X"]) == 1 and (sorted(k["X"])[0], k["y"]) in EXAMPLES), key=lambda k: -k["lift"]): r = random_support(k) print(f" {sorted(k['X'])[0] + ' -> ' + k['y']:<38} {k['support']:>7.4f} " f"{sum(r) / len(r):>9.4f} {max(r):>7.4f} {k['lift']:>9.4f}") OUTSIDE = {i: k["support"] > max(random_support(k)) or k["support"] < min(random_support(k)) for i, k in enumerate(RULES)} print(f"\n{len(RULES)} rules separated by two criteria at once") print(f" {'':<22} {'inside range':>14} {'outside range':>15}") for name, cond in (("lift near 1", True), ("lift far from 1", False)): g = [i for i, k in enumerate(RULES) if (abs(k["lift"] - 1) <= 0.05) == cond] inside = sum(1 for i in g if not OUTSIDE[i]) print(f" {name:<22} {inside:>14} {len(g) - inside:>15}") dt = support(frozenset(["delay", "auto_payment"])) print(f"\na dependency that stays under the support floor: delay + auto_payment " f"support {dt:.4f}, lift {dt / (D1['delay'] * D1['auto_payment']):.4f}; " f"since the floor is {SUPPORT_FLOOR:.2f} it never enters the rule list")
60 shuffles: the support random matching produces
rule real random highest lift
delay -> installment_plan 0.1279 0.0362 0.0467 3.4498
meter_replacement -> field_visit 0.1159 0.0354 0.0451 3.2572
tariff_change -> mobile_notification 0.1076 0.1043 0.1099 1.0249
objection -> mobile_notification 0.1384 0.1363 0.1407 1.0139
166 rules separated by two criteria at once
inside range outside range
lift near 1 107 8
lift far from 1 26 25
a dependency that stays under the support floor: delay + auto_payment support 0.0316, lift 0.3584; since the floor is 0.05 it never enters the rule list
The baseline is in the first two rows. Delay and installment plan occur together in 0.1279 of the baskets; when the columns are shuffled, the same pair falls to a mean of 0.0362 and the highest of sixty shuffles is 0.0467. The difference is 0.0917 and it comes from the data.
The last two rows show the opposite. The rule with a confidence of 0.9246 has a support of 0.1384; random matching carries the same pair to a mean of 0.1363 and a highest of 0.1407. The real support is inside the random range; the rule takes its high confidence entirely from the frequency of the consequent itself. Lift is the closed form of this baseline: dividing by the product of the supports is dividing by what shuffling produces on average.
When the two criteria are crossed, the 166 rules are spread over four cells: of the 115 rules with a lift near 1, 107 are inside the random range, and of the 51 rules with a lift far from 1, 26 are as well. In total 133 rules cannot be told apart from what random matching produces. The 8 rows in the opposite direction are meaningful too: as the support grows the range narrows, and even a lift of 1.04 falls outside. A departure of the lift is not enough; how often the departure is produced by chance is needed as well.
The last row opens another face. The fictional dependency between delay and auto payment has a lift of 0.3584, farther from 1 than anything in the list; but since the two occur together in only 0.0316 of the baskets, it stays under the support floor and never enters the list. If the floor were 0.03, this rule would be at the top of the list.
Summary
- All six of the top six rules by confidence have as the consequent the same item that occurs in 0.9120 of the baskets; their lifts are 1.0139–1.0489.
- Of the 97 rules with confidence above 0.50, 75 have a lift closer to 1 than 0.05, and 85 have one of the three most frequent items as the consequent.
- The baseline is the support that random matching produces: the support of 0.1279 for delay and installment plan does not exceed 0.0467 in any of sixty shuffles, but the support of the rule with a confidence of 0.9246 stays inside the random range.
- 133 of the 166 rules cannot be told apart from what random matching produces.
- The support floor is a decision: a real dependency with a lift of 0.3584 stays under the floor of 0.05 because its support is 0.0316, and never enters the list.
Course Wrap-Up
The account of eleven lessons is in a single table, and each number was read from its own lesson. The triple is the same in every row, but the rows do not carry a common unit: when there are no labels, each lesson chooses its own metric. The last column writes this — in the distance and error rows the smaller is better, in the rate and score rows the larger. The difference is the number of the method minus the baseline.
| Lesson | Baseline | Method’s number | Difference | Metric |
|---|---|---|---|---|
| Clustering 01 | random assignment 5.9790 | 3.8910 | −2.0880 | within-cluster distance |
| Clustering 02 | random assignment 5.9181 | 2.2570 | −3.6611 | within-cluster distance |
| Clustering 03 | random assignment 6.2340 | 4.5419 | −1.6922 | within-cluster distance |
| Clustering 04 | random flagging 1.0200 | 1.7551 | +0.7351 | neighbor distance |
| Clustering 05 | random assignment −4.2424 | −3.7755 | +0.4669 | fit score |
| Clustering 06 | single cluster 0.6539 | 0.5235 | −0.1304 | agreement index |
| Dimensionality reduction 01 | column mean 3.3411 | 2.7762 | −0.5650 | reconstruction error |
| Dimensionality reduction 02 | column mean 3.3411 | 2.7464 | −0.5947 | reconstruction error |
| Dimensionality reduction 03 | random layout 0.0390 | 0.5032 | +0.4642 | neighborhood preservation rate |
| Dimensionality reduction 04 | random flagging 0.0488 | 0.6190 | +0.5702 | precision |
| Dimensionality reduction 05 | random matching 0.0362 | 0.1279 | +0.0917 | support of the rule |
The second column was produced in each lesson by a separate procedure: random assignment, single cluster, random flagging, reconstruction with the column mean, random layout, random matching. In the supervised course the baseline was ready — saying the most frequent label — here each lesson built its baseline itself. This is the rule of the course: an unlabeled result exists only as much as it departs from a random result; a clustering whose baseline is not built counts as unmeasured. Three rows cannot be read without a footnote: in Clustering 06 the 0.5235 is below its baseline, the +0.7351 of Clustering 04 comes not from the data but from the metric, the 0.5032 of Dimensionality reduction 03 is the best of six candidates and the honest range is 0.3940–0.5032.
The second claim of the course is read from the same table: in an unsupervised result there is no right answer, the choice of metric determines the answer. The number of lessons in which the answer changed when the metric was changed is ten — three definitions of similarity gave three partitions, three linkage criteria chose 3, 37 and 56 clusters at the same height, three internal validity metrics chose 3, 6 and 10; in clustering 04 two metrics gave opposite rankings, in dimensionality reduction 03 what one preserved the other threw away, and here confidence and lift produced two separate lists.
In twenty-one lessons — eleven of them unlabeled — every method worked on the assumption that the features were given. Scaling, encoding, generating and reducing were always decided by hand, and the cost of each decision was measured separately. What a procedure that also learns these decisions from the data itself gains, and what it makes invisible, was never asked. The next course, Deep Learning, opens with this question.
To keep your progress and take notes, Log in
My notes
Log in to take notes.