Count-Min Sketch
What does Count-Min guarantee, and why is it accurate for the head and meaningless for the tail?
A Count-Min Sketch estimates how many times each item appeared in a stream, in fixed memory.
The structure. A table of d rows and w columns of counters. Hash each item once per row (each row has its own hash function) and increment the one counter it lands on in every row. To estimate an item's count, take the minimum across its d counters. Collisions with other items can only add to a counter, so the minimum is the tightest available overestimate.
class CountMin:
def __init__(self, w, d):
self.w, self.d = w, d
self.t = [[0] * w for _ in range(d)]
def add(self, x, c=1):
for r in range(self.d):
self.t[r][hash((r, x)) % self.w] += c
def estimate(self, x):
return min(self.t[r][hash((r, x)) % self.w] for r in range(self.d))The guarantee, stated precisely, because the imprecise version causes real damage. Choose w = ⌈e/ε⌉ and d = ⌈ln(1/δ)⌉. Then the estimate exceeds the true count by at most ε · N with probability 1 − δ, where N is the total number of events in the stream (the L1 norm of the frequency vector), not the item's own frequency.
- error as a fraction of N
- chance the bound is exceeded
- the total number of events in the stream
- counters per row and rows
Data table
| Error fraction ε | Counters (w × d) |
|---|---|
| 0 | 1.36M |
| 0 | 1.21M |
| 0 | 1.08M |
| 0 | 962K |
| 0 | 858K |
| 0 | 764K |
| 0 | 681K |
| 0 | 607K |
| 0 | 541K |
| 0 | 482K |
| 0 | 430K |
| 0 | 383K |
| 0 | 341K |
| 0 | 304K |
| 0 | 271K |
| 0 | 242K |
| 0 | 215K |
| 0 | 192K |
| 0 | 171K |
| 0 | 153K |
| 0 | 136K |
| 0 | 121K |
| 0 | 108K |
| 0 | 96.2K |
| 0 | 85.8K |
| 0 | 76.4K |
| 0 | 68.1K |
| 0 | 60.7K |
| 0 | 54.1K |
| 0 | 48.2K |
| 0 | 43K |
| 0 | 38.3K |
| 0 | 34.1K |
| 0 | 30.4K |
| 0.001 | 27.1K |
| 0.001 | 24.2K |
| 0.001 | 21.5K |
| 0.001 | 19.2K |
| 0.001 | 17.1K |
| 0.001 | 15.3K |
| 0.001 | 13.6K |
| 0.001 | 12.1K |
| 0.001 | 10.8K |
| 0.001 | 9.63K |
| 0.002 | 8.58K |
| 0.002 | 7.64K |
| 0.002 | 6.82K |
| 0.002 | 6.08K |
| 0.003 | 5.42K |
| 0.003 | 4.83K |
| 0.003 | 4.3K |
| 0.004 | 3.83K |
| 0.004 | 3.42K |
| 0.004 | 3.04K |
| 0.005 | 2.71K |
| 0.006 | 2.42K |
| 0.006 | 2.15K |
| 0.007 | 1.92K |
| 0.008 | 1.72K |
| 0.009 | 1.52K |
| 0.01 | 1.36K |
| 0.011 | 1.22K |
| 0.013 | 1.08K |
| 0.014 | 965 |
| 0.016 | 860 |
| 0.018 | 765 |
| 0.02 | 685 |
| 0.022 | 610 |
| 0.025 | 545 |
| 0.028 | 485 |
| 0.032 | 430 |
| 0.036 | 385 |
| 0.04 | 345 |
| 0.045 | 305 |
| 0.05 | 275 |
| 0.056 | 245 |
| 0.063 | 220 |
| 0.071 | 195 |
| 0.079 | 175 |
| 0.089 | 155 |
| 0.1 | 140 |
Count-Min is a heavy-hitter structure that happens to accept point queries. It is not a frequency oracle. Anyone using it to answer "how many times did this specific rare key appear" is reading random numbers with confidence.
Memory for this example. ε = 0.001 gives w = ⌈2.718 / 0.001⌉ = 2,719. With δ = 1% (so d = ⌈ln 100⌉ = 5) the table has 5 × 2,719 ≈ 13,600 counters, about 54 KB with 4-byte counters, whatever the stream size.
It is a linear sketch, and linearity buys mergeability. The sketch is a fixed random projection matrix applied to the frequency vector. So sketch(A) + sketch(B) = sketch(A ⊎ B) exactly: add the two tables cell by cell and you have the sketch of the combined stream. This is the general rule for the whole article and it is worth stating as a principle, which we call the Homomorphism law:
Count-Sketch, the signed variant (each item adds +1 or −1 per row, chosen by a second hash), extends this to subtraction, and therefore to deletions and to estimates of the second moment.
Count-Min is not idempotent. Duplicate delivery inflates it directly.
Count-Mean-Min deserves one line: subtract each row's expected noise (N/w) before taking the median, rather than the minimum. On skewed data this substantially reduces the bias that the minimum estimator carries.