Skip to main content

ArrayOfDoublesSketch

Struct ArrayOfDoublesSketch 

Source
pub struct ArrayOfDoublesSketch { /* private fields */ }
Expand description

A mutable, update-only ArrayOfDoubles Tuple sketch: estimates the number of distinct keys added via update_*, and carries a fixed-width array of f64 values per retained key, summed on collision. Build one with ArrayOfDoublesSketchBuilder.

Call Self::compact to produce an immutable, serializable super::CompactArrayOfDoublesSketch snapshot for storage, transmission, or use as input to a set operation.

Implementations§

Source§

impl ArrayOfDoublesSketch

Source

pub fn update_u64( &mut self, key: u64, values: &[f64], ) -> Result<(), SketchError>

Adds a u64 key with its associated values. Returns SketchError::InvalidConfig unless values.len() == self.get_num_values().

Examples found in repository?
examples/tuple.rs (line 22)
14fn main() {
15    // Two sketches of user IDs, each carrying [sessions, revenue] per user.
16    let mut day1 = ArrayOfDoublesSketchBuilder::new()
17        .lg_k(12)
18        .num_values(2)
19        .build()
20        .unwrap();
21    for id in 0..10_000u64 {
22        day1.update_u64(id, &[1.0, 2.50]).unwrap();
23    }
24
25    let mut day2 = ArrayOfDoublesSketchBuilder::new()
26        .lg_k(12)
27        .num_values(2)
28        .build()
29        .unwrap();
30    for id in 5_000..15_000u64 {
31        day2.update_u64(id, &[1.0, 4.00]).unwrap();
32    }
33
34    println!("Day 1 unique users (estimate): {:.0}", day1.get_estimate());
35    println!("Day 2 unique users (estimate): {:.0}", day2.get_estimate());
36    println!("Values per entry: {}", day1.get_num_values());
37
38    // Union: unique users across both days, with per-user values summed for
39    // anyone who appeared on both.
40    let mut union = ArrayOfDoublesUnionBuilder::new()
41        .lg_k(12)
42        .num_values(2)
43        .build()
44        .unwrap();
45    union.update(&day1).unwrap();
46    union.update(&day2).unwrap();
47    let combined = union.get_result(true);
48    println!(
49        "Total unique users (union estimate): {:.0}",
50        combined.get_estimate()
51    );
52
53    // Per-entry access is what distinguishes Tuple sketches from HLL/Theta/CPC:
54    // scale the retained sample's revenue back up by 1/theta to estimate the
55    // full population total.
56    let retained_revenue: f64 = combined.entries().map(|(_, values)| values[1]).sum();
57    println!(
58        "Estimated total revenue: {:.2} (from {} retained entries, theta = {:.4})",
59        retained_revenue / combined.get_theta(),
60        combined.get_num_retained(),
61        combined.get_theta()
62    );
63
64    // Intersection: users who came back on day 2.
65    let mut intersection = ArrayOfDoublesIntersection::new(2).unwrap();
66    intersection.update(&day1).unwrap();
67    intersection.update(&day2).unwrap();
68    match intersection.get_result(true) {
69        Ok(returning) => println!(
70            "Returning users (intersection estimate): {:.0}",
71            returning.get_estimate()
72        ),
73        Err(e) => println!("No intersection result: {e}"),
74    }
75
76    // A-not-b: users who only came on day 1.
77    let a_not_b = ArrayOfDoublesAnotB::new();
78    let day1_only = a_not_b.compute(&day1, &day2, true).unwrap();
79    println!(
80        "Day-1-only users (a-not-b estimate): {:.0}",
81        day1_only.get_estimate()
82    );
83
84    // Jaccard similarity of the two days' audiences.
85    let similarity = array_of_doubles_jaccard_similarity(&day1, &day2).unwrap();
86    println!(
87        "Jaccard similarity: {:.3} (range [{:.3}, {:.3}])",
88        similarity.estimate, similarity.lower_bound, similarity.upper_bound
89    );
90
91    // Serialize a compact sketch for storage/transmission, then restore it.
92    let compact = day1.compact(true);
93    let bytes = compact.serialize();
94    println!("Serialized day-1 sketch: {} bytes", bytes.len());
95    let restored = CompactArrayOfDoublesSketch::deserialize(&bytes).unwrap();
96    println!(
97        "Restored estimate: {:.0} ({} values per entry)",
98        restored.get_estimate(),
99        restored.get_num_values()
100    );
101}
Source

pub fn update_i64( &mut self, key: i64, values: &[f64], ) -> Result<(), SketchError>

Adds an i64 key with its associated values. See Self::update_u64.

Source

pub fn update_u32( &mut self, key: u32, values: &[f64], ) -> Result<(), SketchError>

Adds a u32 key with its associated values. See Self::update_u64.

Source

pub fn update_i32( &mut self, key: i32, values: &[f64], ) -> Result<(), SketchError>

Adds an i32 key with its associated values. See Self::update_u64.

Source

pub fn update_u16( &mut self, key: u16, values: &[f64], ) -> Result<(), SketchError>

Adds a u16 key with its associated values. See Self::update_u64.

Source

pub fn update_i16( &mut self, key: i16, values: &[f64], ) -> Result<(), SketchError>

Adds an i16 key with its associated values. See Self::update_u64.

Source

pub fn update_u8(&mut self, key: u8, values: &[f64]) -> Result<(), SketchError>

Adds a u8 key with its associated values. See Self::update_u64.

Source

pub fn update_i8(&mut self, key: i8, values: &[f64]) -> Result<(), SketchError>

Adds an i8 key with its associated values. See Self::update_u64.

Source

pub fn update_f64( &mut self, key: f64, values: &[f64], ) -> Result<(), SketchError>

Adds an f64 key with its associated values. See Self::update_u64.

Source

pub fn update_str( &mut self, key: &str, values: &[f64], ) -> Result<(), SketchError>

Adds a string key with its associated values. See Self::update_u64.

Source

pub fn update_bytes( &mut self, key: &[u8], values: &[f64], ) -> Result<(), SketchError>

Adds an arbitrary byte-slice key with its associated values. See Self::update_u64.

Source

pub fn trim(&mut self)

Removes retained entries in excess of the nominal size k, lowering the theta threshold to do so.

Note that this does shift Self::get_estimate — trimming lowers theta, and the estimate is derived from the retained count and theta together. Upstream only guarantees the excess entries are dropped.

Source

pub fn reset(&mut self)

Resets this sketch to its initial, empty state. num_values is preserved.

Source

pub fn get_estimate(&self) -> f64

Returns the current estimate of the number of distinct keys added.

Examples found in repository?
examples/tuple.rs (line 34)
14fn main() {
15    // Two sketches of user IDs, each carrying [sessions, revenue] per user.
16    let mut day1 = ArrayOfDoublesSketchBuilder::new()
17        .lg_k(12)
18        .num_values(2)
19        .build()
20        .unwrap();
21    for id in 0..10_000u64 {
22        day1.update_u64(id, &[1.0, 2.50]).unwrap();
23    }
24
25    let mut day2 = ArrayOfDoublesSketchBuilder::new()
26        .lg_k(12)
27        .num_values(2)
28        .build()
29        .unwrap();
30    for id in 5_000..15_000u64 {
31        day2.update_u64(id, &[1.0, 4.00]).unwrap();
32    }
33
34    println!("Day 1 unique users (estimate): {:.0}", day1.get_estimate());
35    println!("Day 2 unique users (estimate): {:.0}", day2.get_estimate());
36    println!("Values per entry: {}", day1.get_num_values());
37
38    // Union: unique users across both days, with per-user values summed for
39    // anyone who appeared on both.
40    let mut union = ArrayOfDoublesUnionBuilder::new()
41        .lg_k(12)
42        .num_values(2)
43        .build()
44        .unwrap();
45    union.update(&day1).unwrap();
46    union.update(&day2).unwrap();
47    let combined = union.get_result(true);
48    println!(
49        "Total unique users (union estimate): {:.0}",
50        combined.get_estimate()
51    );
52
53    // Per-entry access is what distinguishes Tuple sketches from HLL/Theta/CPC:
54    // scale the retained sample's revenue back up by 1/theta to estimate the
55    // full population total.
56    let retained_revenue: f64 = combined.entries().map(|(_, values)| values[1]).sum();
57    println!(
58        "Estimated total revenue: {:.2} (from {} retained entries, theta = {:.4})",
59        retained_revenue / combined.get_theta(),
60        combined.get_num_retained(),
61        combined.get_theta()
62    );
63
64    // Intersection: users who came back on day 2.
65    let mut intersection = ArrayOfDoublesIntersection::new(2).unwrap();
66    intersection.update(&day1).unwrap();
67    intersection.update(&day2).unwrap();
68    match intersection.get_result(true) {
69        Ok(returning) => println!(
70            "Returning users (intersection estimate): {:.0}",
71            returning.get_estimate()
72        ),
73        Err(e) => println!("No intersection result: {e}"),
74    }
75
76    // A-not-b: users who only came on day 1.
77    let a_not_b = ArrayOfDoublesAnotB::new();
78    let day1_only = a_not_b.compute(&day1, &day2, true).unwrap();
79    println!(
80        "Day-1-only users (a-not-b estimate): {:.0}",
81        day1_only.get_estimate()
82    );
83
84    // Jaccard similarity of the two days' audiences.
85    let similarity = array_of_doubles_jaccard_similarity(&day1, &day2).unwrap();
86    println!(
87        "Jaccard similarity: {:.3} (range [{:.3}, {:.3}])",
88        similarity.estimate, similarity.lower_bound, similarity.upper_bound
89    );
90
91    // Serialize a compact sketch for storage/transmission, then restore it.
92    let compact = day1.compact(true);
93    let bytes = compact.serialize();
94    println!("Serialized day-1 sketch: {} bytes", bytes.len());
95    let restored = CompactArrayOfDoublesSketch::deserialize(&bytes).unwrap();
96    println!(
97        "Restored estimate: {:.0} ({} values per entry)",
98        restored.get_estimate(),
99        restored.get_num_values()
100    );
101}
Source

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 the given number of standard deviations (1, 2, or 3, corresponding to roughly 67%, 95%, and 99% confidence). Returns SketchError::InvalidConfig for any other value.

Source

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 for the meaning of num_std_dev.

Source

pub fn is_empty(&self) -> bool

Returns true if no keys have been added to this sketch.

Source

pub fn is_estimation_mode(&self) -> bool

Returns true if this sketch’s theta threshold is below 1.0 (i.e. it has begun sampling and Self::get_estimate is a statistical estimate rather than an exact count).

Source

pub fn is_ordered(&self) -> bool

Returns true if this sketch’s retained entries are sorted by hash value.

Source

pub fn get_theta(&self) -> f64

Returns the current theta threshold (1.0 until sampling begins).

Source

pub fn get_num_retained(&self) -> u32

Returns the number of entries currently retained by this sketch.

Source

pub fn get_num_values(&self) -> u8

Returns the fixed number of f64 values each retained entry carries, as configured at build time.

Examples found in repository?
examples/tuple.rs (line 36)
14fn main() {
15    // Two sketches of user IDs, each carrying [sessions, revenue] per user.
16    let mut day1 = ArrayOfDoublesSketchBuilder::new()
17        .lg_k(12)
18        .num_values(2)
19        .build()
20        .unwrap();
21    for id in 0..10_000u64 {
22        day1.update_u64(id, &[1.0, 2.50]).unwrap();
23    }
24
25    let mut day2 = ArrayOfDoublesSketchBuilder::new()
26        .lg_k(12)
27        .num_values(2)
28        .build()
29        .unwrap();
30    for id in 5_000..15_000u64 {
31        day2.update_u64(id, &[1.0, 4.00]).unwrap();
32    }
33
34    println!("Day 1 unique users (estimate): {:.0}", day1.get_estimate());
35    println!("Day 2 unique users (estimate): {:.0}", day2.get_estimate());
36    println!("Values per entry: {}", day1.get_num_values());
37
38    // Union: unique users across both days, with per-user values summed for
39    // anyone who appeared on both.
40    let mut union = ArrayOfDoublesUnionBuilder::new()
41        .lg_k(12)
42        .num_values(2)
43        .build()
44        .unwrap();
45    union.update(&day1).unwrap();
46    union.update(&day2).unwrap();
47    let combined = union.get_result(true);
48    println!(
49        "Total unique users (union estimate): {:.0}",
50        combined.get_estimate()
51    );
52
53    // Per-entry access is what distinguishes Tuple sketches from HLL/Theta/CPC:
54    // scale the retained sample's revenue back up by 1/theta to estimate the
55    // full population total.
56    let retained_revenue: f64 = combined.entries().map(|(_, values)| values[1]).sum();
57    println!(
58        "Estimated total revenue: {:.2} (from {} retained entries, theta = {:.4})",
59        retained_revenue / combined.get_theta(),
60        combined.get_num_retained(),
61        combined.get_theta()
62    );
63
64    // Intersection: users who came back on day 2.
65    let mut intersection = ArrayOfDoublesIntersection::new(2).unwrap();
66    intersection.update(&day1).unwrap();
67    intersection.update(&day2).unwrap();
68    match intersection.get_result(true) {
69        Ok(returning) => println!(
70            "Returning users (intersection estimate): {:.0}",
71            returning.get_estimate()
72        ),
73        Err(e) => println!("No intersection result: {e}"),
74    }
75
76    // A-not-b: users who only came on day 1.
77    let a_not_b = ArrayOfDoublesAnotB::new();
78    let day1_only = a_not_b.compute(&day1, &day2, true).unwrap();
79    println!(
80        "Day-1-only users (a-not-b estimate): {:.0}",
81        day1_only.get_estimate()
82    );
83
84    // Jaccard similarity of the two days' audiences.
85    let similarity = array_of_doubles_jaccard_similarity(&day1, &day2).unwrap();
86    println!(
87        "Jaccard similarity: {:.3} (range [{:.3}, {:.3}])",
88        similarity.estimate, similarity.lower_bound, similarity.upper_bound
89    );
90
91    // Serialize a compact sketch for storage/transmission, then restore it.
92    let compact = day1.compact(true);
93    let bytes = compact.serialize();
94    println!("Serialized day-1 sketch: {} bytes", bytes.len());
95    let restored = CompactArrayOfDoublesSketch::deserialize(&bytes).unwrap();
96    println!(
97        "Restored estimate: {:.0} ({} values per entry)",
98        restored.get_estimate(),
99        restored.get_num_values()
100    );
101}
Source

pub fn entries(&self) -> impl Iterator<Item = (u64, Vec<f64>)>

Iterates the retained entries as (hash, values) pairs, where values.len() == self.get_num_values().

The entries are copied out of C++ in two FFI calls up front (cxx cannot hand back a live C++ iterator), so each item owns its Vec rather than borrowing from the sketch. Iteration order is unspecified for an update sketch; compact it with ordered = true for hash-ordered iteration.

Source

pub fn compact(&self, ordered: bool) -> CompactArrayOfDoublesSketch

Produces an immutable, serializable super::CompactArrayOfDoublesSketch snapshot of this sketch’s current state. If ordered is true, the snapshot’s entries are sorted by hash value.

Examples found in repository?
examples/tuple.rs (line 92)
14fn main() {
15    // Two sketches of user IDs, each carrying [sessions, revenue] per user.
16    let mut day1 = ArrayOfDoublesSketchBuilder::new()
17        .lg_k(12)
18        .num_values(2)
19        .build()
20        .unwrap();
21    for id in 0..10_000u64 {
22        day1.update_u64(id, &[1.0, 2.50]).unwrap();
23    }
24
25    let mut day2 = ArrayOfDoublesSketchBuilder::new()
26        .lg_k(12)
27        .num_values(2)
28        .build()
29        .unwrap();
30    for id in 5_000..15_000u64 {
31        day2.update_u64(id, &[1.0, 4.00]).unwrap();
32    }
33
34    println!("Day 1 unique users (estimate): {:.0}", day1.get_estimate());
35    println!("Day 2 unique users (estimate): {:.0}", day2.get_estimate());
36    println!("Values per entry: {}", day1.get_num_values());
37
38    // Union: unique users across both days, with per-user values summed for
39    // anyone who appeared on both.
40    let mut union = ArrayOfDoublesUnionBuilder::new()
41        .lg_k(12)
42        .num_values(2)
43        .build()
44        .unwrap();
45    union.update(&day1).unwrap();
46    union.update(&day2).unwrap();
47    let combined = union.get_result(true);
48    println!(
49        "Total unique users (union estimate): {:.0}",
50        combined.get_estimate()
51    );
52
53    // Per-entry access is what distinguishes Tuple sketches from HLL/Theta/CPC:
54    // scale the retained sample's revenue back up by 1/theta to estimate the
55    // full population total.
56    let retained_revenue: f64 = combined.entries().map(|(_, values)| values[1]).sum();
57    println!(
58        "Estimated total revenue: {:.2} (from {} retained entries, theta = {:.4})",
59        retained_revenue / combined.get_theta(),
60        combined.get_num_retained(),
61        combined.get_theta()
62    );
63
64    // Intersection: users who came back on day 2.
65    let mut intersection = ArrayOfDoublesIntersection::new(2).unwrap();
66    intersection.update(&day1).unwrap();
67    intersection.update(&day2).unwrap();
68    match intersection.get_result(true) {
69        Ok(returning) => println!(
70            "Returning users (intersection estimate): {:.0}",
71            returning.get_estimate()
72        ),
73        Err(e) => println!("No intersection result: {e}"),
74    }
75
76    // A-not-b: users who only came on day 1.
77    let a_not_b = ArrayOfDoublesAnotB::new();
78    let day1_only = a_not_b.compute(&day1, &day2, true).unwrap();
79    println!(
80        "Day-1-only users (a-not-b estimate): {:.0}",
81        day1_only.get_estimate()
82    );
83
84    // Jaccard similarity of the two days' audiences.
85    let similarity = array_of_doubles_jaccard_similarity(&day1, &day2).unwrap();
86    println!(
87        "Jaccard similarity: {:.3} (range [{:.3}, {:.3}])",
88        similarity.estimate, similarity.lower_bound, similarity.upper_bound
89    );
90
91    // Serialize a compact sketch for storage/transmission, then restore it.
92    let compact = day1.compact(true);
93    let bytes = compact.serialize();
94    println!("Serialized day-1 sketch: {} bytes", bytes.len());
95    let restored = CompactArrayOfDoublesSketch::deserialize(&bytes).unwrap();
96    println!(
97        "Restored estimate: {:.0} ({} values per entry)",
98        restored.get_estimate(),
99        restored.get_num_values()
100    );
101}

Trait Implementations§

Source§

impl ArrayOfDoublesInput for ArrayOfDoublesSketch

Source§

fn get_num_values(&self) -> u8

The fixed number of f64 values each of this sketch’s retained entries carries. Set operations require all operands to agree.
Source§

impl Send for ArrayOfDoublesSketch

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.