pub struct TupleSketch<S: TupleSummary> { /* private fields */ }Expand description
A mutable, update-only Tuple sketch carrying a user-defined summary S
per distinct key. Build one with
TupleSketchBuilder.
Implementations§
Source§impl<S: TupleSummary> TupleSketch<S>
impl<S: TupleSummary> TupleSketch<S>
Sourcepub fn update_u64(&mut self, key: u64, value: &S::Update)
pub fn update_u64(&mut self, key: u64, value: &S::Update)
Adds a u64 key. S::create runs first, entirely in Rust; the
resulting summary is combined into an existing entry or cloned into a
new one.
Examples found in repository?
71fn bench_distinct(items: u64) {
72 let mut sketch = build();
73 let start = Instant::now();
74 for key in 0..items {
75 sketch.update_u64(key, &());
76 }
77 let elapsed = start.elapsed();
78 // Reading the estimate keeps the loop above from being optimised out.
79 report("distinct", items, elapsed, sketch.get_estimate());
80}
81
82fn bench_hot(items: u64) {
83 let mut sketch = build();
84 let start = Instant::now();
85 for i in 0..items {
86 sketch.update_u64(i % HOT_KEY_SPACE, &());
87 }
88 let elapsed = start.elapsed();
89 report("hot", items, elapsed, sketch.get_estimate());
90}More examples
63fn main() {
64 let mut january: TupleSketch<Activity> = TupleSketchBuilder::new().lg_k(12).build().unwrap();
65 for user in 0..10_000u64 {
66 january.update_u64(
67 user,
68 &Event {
69 revenue_cents: 250 + (user % 100),
70 country: if user % 2 == 0 { "GB" } else { "US" },
71 },
72 );
73 }
74
75 let mut february: TupleSketch<Activity> = TupleSketchBuilder::new().lg_k(12).build().unwrap();
76 for user in 5_000..15_000u64 {
77 february.update_u64(
78 user,
79 &Event {
80 revenue_cents: 400,
81 country: "US",
82 },
83 );
84 }
85
86 println!("January unique users: {:.0}", january.get_estimate());
87 println!("February unique users: {:.0}", february.get_estimate());
88
89 // Union: everyone who appeared in either month, with their activity merged.
90 let mut union = TupleUnionBuilder::<Activity>::new()
91 .lg_k(12)
92 .build()
93 .unwrap();
94 union.update(&january);
95 union.update(&february);
96 let combined = union.get_result(true);
97 println!("Users across both months: {:.0}", combined.get_estimate());
98
99 // Per-entry summaries are the point of a Tuple sketch. Scale the retained
100 // sample back up by 1/theta to estimate population totals.
101 let retained_revenue: u64 = combined.entries().map(|(_, a)| a.revenue_cents).sum();
102 let biggest_order = combined
103 .entries()
104 .map(|(_, a)| a.largest_order_cents)
105 .max()
106 .unwrap_or(0);
107 println!(
108 "Estimated total revenue: {:.2} (from {} retained entries, theta = {:.4})",
109 (retained_revenue as f64 / combined.get_theta()) / 100.0,
110 combined.get_num_retained(),
111 combined.get_theta()
112 );
113 println!(
114 "Largest single order seen: {:.2}",
115 biggest_order as f64 / 100.0
116 );
117
118 // Intersection: users active in both months.
119 let mut intersection = TupleIntersection::<Activity>::new();
120 intersection.update(&january);
121 intersection.update(&february);
122 match intersection.get_result(true) {
123 Ok(returning) => {
124 println!("Returning users: {:.0}", returning.get_estimate());
125 // `intersection_combine`'s `min` semantics at work: a returning
126 // user's sessions/countries reflect only what showed up in BOTH
127 // months, not the union of the two.
128 if let Some((_, activity)) = returning.entries().next() {
129 println!(
130 " e.g. one returning user: {} session(s), countries seen in both months: {:?}",
131 activity.sessions, activity.countries
132 );
133 }
134 }
135 Err(e) => println!("No intersection result: {e}"),
136 }
137
138 // A-not-b: users who churned after January.
139 let churned = TupleAnotB::<Activity>::new().compute(&january, &february, true);
140 println!("Churned after January: {:.0}", churned.get_estimate());
141
142 // Jaccard similarity of the two months' audiences.
143 let similarity = tuple_jaccard_similarity(&january, &february);
144 println!(
145 "Audience overlap (Jaccard): {:.3} (range [{:.3}, {:.3}])",
146 similarity.estimate, similarity.lower_bound, similarity.upper_bound
147 );
148}Sourcepub fn update_i64(&mut self, key: i64, value: &S::Update)
pub fn update_i64(&mut self, key: i64, value: &S::Update)
Adds an i64 key. See Self::update_u64.
Sourcepub fn update_u32(&mut self, key: u32, value: &S::Update)
pub fn update_u32(&mut self, key: u32, value: &S::Update)
Adds a u32 key. See Self::update_u64.
Sourcepub fn update_i32(&mut self, key: i32, value: &S::Update)
pub fn update_i32(&mut self, key: i32, value: &S::Update)
Adds an i32 key. See Self::update_u64.
Sourcepub fn update_u16(&mut self, key: u16, value: &S::Update)
pub fn update_u16(&mut self, key: u16, value: &S::Update)
Adds a u16 key. See Self::update_u64.
Sourcepub fn update_i16(&mut self, key: i16, value: &S::Update)
pub fn update_i16(&mut self, key: i16, value: &S::Update)
Adds an i16 key. See Self::update_u64.
Sourcepub fn update_u8(&mut self, key: u8, value: &S::Update)
pub fn update_u8(&mut self, key: u8, value: &S::Update)
Adds a u8 key. See Self::update_u64.
Sourcepub fn update_i8(&mut self, key: i8, value: &S::Update)
pub fn update_i8(&mut self, key: i8, value: &S::Update)
Adds an i8 key. See Self::update_u64.
Sourcepub fn update_f64(&mut self, key: f64, value: &S::Update)
pub fn update_f64(&mut self, key: f64, value: &S::Update)
Adds an f64 key. See Self::update_u64.
Sourcepub fn update_str(&mut self, key: &str, value: &S::Update)
pub fn update_str(&mut self, key: &str, value: &S::Update)
Adds a string key. See Self::update_u64.
Sourcepub fn update_bytes(&mut self, key: &[u8], value: &S::Update)
pub fn update_bytes(&mut self, key: &[u8], value: &S::Update)
Adds an arbitrary byte-slice key. See Self::update_u64.
Sourcepub fn trim(&mut self)
pub fn trim(&mut self)
Removes retained entries in excess of the nominal size k, lowering
theta to do so. Note this shifts Self::get_estimate.
Sourcepub fn get_estimate(&self) -> f64
pub fn get_estimate(&self) -> f64
Returns the current estimate of the number of distinct keys added.
Examples found in repository?
71fn bench_distinct(items: u64) {
72 let mut sketch = build();
73 let start = Instant::now();
74 for key in 0..items {
75 sketch.update_u64(key, &());
76 }
77 let elapsed = start.elapsed();
78 // Reading the estimate keeps the loop above from being optimised out.
79 report("distinct", items, elapsed, sketch.get_estimate());
80}
81
82fn bench_hot(items: u64) {
83 let mut sketch = build();
84 let start = Instant::now();
85 for i in 0..items {
86 sketch.update_u64(i % HOT_KEY_SPACE, &());
87 }
88 let elapsed = start.elapsed();
89 report("hot", items, elapsed, sketch.get_estimate());
90}More examples
63fn main() {
64 let mut january: TupleSketch<Activity> = TupleSketchBuilder::new().lg_k(12).build().unwrap();
65 for user in 0..10_000u64 {
66 january.update_u64(
67 user,
68 &Event {
69 revenue_cents: 250 + (user % 100),
70 country: if user % 2 == 0 { "GB" } else { "US" },
71 },
72 );
73 }
74
75 let mut february: TupleSketch<Activity> = TupleSketchBuilder::new().lg_k(12).build().unwrap();
76 for user in 5_000..15_000u64 {
77 february.update_u64(
78 user,
79 &Event {
80 revenue_cents: 400,
81 country: "US",
82 },
83 );
84 }
85
86 println!("January unique users: {:.0}", january.get_estimate());
87 println!("February unique users: {:.0}", february.get_estimate());
88
89 // Union: everyone who appeared in either month, with their activity merged.
90 let mut union = TupleUnionBuilder::<Activity>::new()
91 .lg_k(12)
92 .build()
93 .unwrap();
94 union.update(&january);
95 union.update(&february);
96 let combined = union.get_result(true);
97 println!("Users across both months: {:.0}", combined.get_estimate());
98
99 // Per-entry summaries are the point of a Tuple sketch. Scale the retained
100 // sample back up by 1/theta to estimate population totals.
101 let retained_revenue: u64 = combined.entries().map(|(_, a)| a.revenue_cents).sum();
102 let biggest_order = combined
103 .entries()
104 .map(|(_, a)| a.largest_order_cents)
105 .max()
106 .unwrap_or(0);
107 println!(
108 "Estimated total revenue: {:.2} (from {} retained entries, theta = {:.4})",
109 (retained_revenue as f64 / combined.get_theta()) / 100.0,
110 combined.get_num_retained(),
111 combined.get_theta()
112 );
113 println!(
114 "Largest single order seen: {:.2}",
115 biggest_order as f64 / 100.0
116 );
117
118 // Intersection: users active in both months.
119 let mut intersection = TupleIntersection::<Activity>::new();
120 intersection.update(&january);
121 intersection.update(&february);
122 match intersection.get_result(true) {
123 Ok(returning) => {
124 println!("Returning users: {:.0}", returning.get_estimate());
125 // `intersection_combine`'s `min` semantics at work: a returning
126 // user's sessions/countries reflect only what showed up in BOTH
127 // months, not the union of the two.
128 if let Some((_, activity)) = returning.entries().next() {
129 println!(
130 " e.g. one returning user: {} session(s), countries seen in both months: {:?}",
131 activity.sessions, activity.countries
132 );
133 }
134 }
135 Err(e) => println!("No intersection result: {e}"),
136 }
137
138 // A-not-b: users who churned after January.
139 let churned = TupleAnotB::<Activity>::new().compute(&january, &february, true);
140 println!("Churned after January: {:.0}", churned.get_estimate());
141
142 // Jaccard similarity of the two months' audiences.
143 let similarity = tuple_jaccard_similarity(&january, &february);
144 println!(
145 "Audience overlap (Jaccard): {:.3} (range [{:.3}, {:.3}])",
146 similarity.estimate, similarity.lower_bound, similarity.upper_bound
147 );
148}Sourcepub fn get_lower_bound(&self, num_std_dev: u8) -> Result<f64, SketchError>
pub fn get_lower_bound(&self, num_std_dev: u8) -> Result<f64, SketchError>
Returns the lower bound of the confidence interval around
Self::get_estimate, for num_std_dev of 1, 2, or 3.
Sourcepub fn get_upper_bound(&self, num_std_dev: u8) -> Result<f64, SketchError>
pub fn get_upper_bound(&self, num_std_dev: u8) -> Result<f64, SketchError>
Returns the upper bound of the confidence interval around
Self::get_estimate. See Self::get_lower_bound.
Sourcepub fn is_estimation_mode(&self) -> bool
pub fn is_estimation_mode(&self) -> bool
Returns true if the sketch has begun sampling.
Sourcepub fn is_ordered(&self) -> bool
pub fn is_ordered(&self) -> bool
Returns true if retained entries are sorted by hash value.
Sourcepub fn get_num_retained(&self) -> u32
pub fn get_num_retained(&self) -> u32
Returns the number of retained entries.
Sourcepub fn compact(&self, ordered: bool) -> CompactTupleSketch<S>
pub fn compact(&self, ordered: bool) -> CompactTupleSketch<S>
Produces an immutable
CompactTupleSketch snapshot. If
ordered is true, its entries are sorted by hash value.