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 as f64 * 0.99;
53 let p99_pos_low = p99_pos as usize;
54 let p99_frac = p99_pos - p99_pos_low as f64;
55 let p99 = if p99_pos_low + 1 < n {
56 valids[p99_pos_low] * (1.0 - p99_frac) + valids[p99_pos_low + 1] * p99_frac
57 } else {
58 valids[p99_pos_low]
59 };
60
61 let variance = valids
63 .iter()
64 .map(|x| {
65 let diff = mean - x;
66 diff * diff
67 })
68 .sum::<f64>()
69 / n as f64;
70
71 let max = *valids
72 .iter()
73 .max_by(|a, b| a.partial_cmp(b).unwrap())
74 .unwrap_or(&f64::NAN);
75
76 Stats {
77 mean,
78 median,
79 variance,
80 max,
81 p99,
82 n,
83 }
84 }
85}