1pub struct Stats {
7 pub mean: f64,
8 pub median: f64,
9 pub variance: f64,
10 pub max: f64,
11 pub p99: f64,
12 pub n: usize,
13}
14
15impl Stats {
16 pub fn nan() -> Self {
17 return Stats {
18 mean: f64::NAN,
19 median: f64::NAN,
20 variance: f64::NAN,
21 max: f64::NAN,
22 p99: f64::NAN,
23 n: 0,
24 };
25 }
26
27 pub fn from_vec(v: &Vec<f64>) -> Self {
28 let mut valids: Vec<f64> = v
29 .iter()
30 .filter(|x| !x.is_nan() && x.is_finite())
31 .cloned()
32 .collect();
33
34 if valids.is_empty() {
35 return Self::nan();
36 }
37
38 valids.sort_by(|a, b| a.partial_cmp(b).unwrap());
39
40 let sum: f64 = valids.iter().sum();
41 let n = valids.len();
42 let mean = sum / n as f64;
43
44 let median = if n % 2 == 0 {
46 (valids[n / 2 - 1] + valids[n / 2]) / 2.0
47 } else {
48 valids[n / 2]
49 };
50
51 let p99_pos = (n - 1) as f64 * 0.99;
53 let p99_pos_low = p99_pos.floor() as usize;
54 let p99_frac = p99_pos - p99_pos_low as f64;
55
56 let p99 = if p99_pos_low + 1 < n {
57 valids[p99_pos_low] * (1.0 - p99_frac) + valids[p99_pos_low + 1] * p99_frac
58 } else {
59 valids[n - 1]
60 };
61
62 let variance = valids
64 .iter()
65 .map(|x| {
66 let diff = mean - x;
67 diff * diff
68 })
69 .sum::<f64>()
70 / n as f64;
71
72 let max = *valids
73 .iter()
74 .max_by(|a, b| a.partial_cmp(b).unwrap())
75 .unwrap_or(&f64::NAN);
76
77 Stats {
78 mean,
79 median,
80 variance,
81 max,
82 p99,
83 n,
84 }
85 }
86}