Theta Sketch
Estimates cardinality like HyperLogLog, but also supports exact set operations (union, intersection, difference) directly between two sketches, without ever bringing the original sets back into memory.
How it works
Every item is hashed to a pseudo-uniform value in [0, 1). The sketch retains the k smallest hashes it has ever seen, below a shrinking threshold θ (theta) that starts at 1. Once more than k distinct hashes have been admitted, the largest retained hash is evicted and θ shrinks to that evicted value: this is the "KMV" (K-Minimum-Values) or "bottom-k" sketch technique.
Since hashes are uniformly distributed over [0, 1), the retained set is really a uniform random sample of all distinct items restricted to the region below θ. That gives an unbiased cardinality estimator: estimate = |retained| / θ. Long runs of small hashes below a tiny θ imply a huge number of distinct items were needed to produce them, exactly the same style of reasoning HyperLogLog uses with leading-zero runs instead.
The key extra trick: because the retained set is just "the k smallest hashes below θ", two sketches can be combined by taking the smaller of their two thetas, restricting both retained sets to that shared region, and then doing ordinary set union/intersection/ difference on the (small) retained hash sets. No decompression back to the original items is ever needed: this is what Apache DataSketches and systems like Apache Druid use for "how many distinct users matched filter A and filter B" queries over huge datasets.
Interactive demo
Sketch A
Sketch B
API surface
| Operation | Description | Complexity |
|---|---|---|
| new ThetaSketch(k) | Allocate a sketch retaining up to k hashes, θ starts at 1. | O(k) space |
| update(item): ThetaStep | Hash item; admit it if below θ, evicting the max hash (shrinking θ) once over k entries. | O(k) time |
| estimate(): number | Unbiased cardinality estimate: |retained| / θ. | O(k) time |
| union(other): ThetaSketch | Combine retained sets restricted to the smaller θ, re-trimming to k if needed. | O(k log k) time |
| intersect(other): ThetaSketch | Hashes retained by both sketches, restricted to the smaller θ. | O(k) time |
| difference(other): ThetaSketch | "A-not-B": hashes retained by this sketch but not the other. | O(k) time |
References
Original papers
Library implementations
Real-world use cases
- Apache Druid theta sketch aggregators for exact set expressions (union/intersection/difference) over huge distinct-count queries, e.g. "users who did A AND B but NOT C"
- Apache Pig, Hive, and Spark adaptors for approximate distinct counts with set operations at data-warehouse scale
- PostgreSQL and BigQuery approximate-distinct-count extensions built on the DataSketches theta sketch