1 use std::fmt;
2 
3 use crate::stats::Distribution;
4 
5 #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize, Debug)]
6 pub enum Statistic {
7     Mean,
8     Median,
9     MedianAbsDev,
10     Slope,
11     StdDev,
12     Typical,
13 }
14 
15 impl fmt::Display for Statistic {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result16     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17         match *self {
18             Statistic::Mean => f.pad("mean"),
19             Statistic::Median => f.pad("median"),
20             Statistic::MedianAbsDev => f.pad("MAD"),
21             Statistic::Slope => f.pad("slope"),
22             Statistic::StdDev => f.pad("SD"),
23             Statistic::Typical => f.pad("typical"),
24         }
25     }
26 }
27 
28 #[derive(Clone, PartialEq, Deserialize, Serialize, Debug)]
29 pub struct ConfidenceInterval {
30     pub confidence_level: f64,
31     pub lower_bound: f64,
32     pub upper_bound: f64,
33 }
34 
35 #[derive(Clone, PartialEq, Deserialize, Serialize, Debug)]
36 pub struct Estimate {
37     /// The confidence interval for this estimate
38     pub confidence_interval: ConfidenceInterval,
39     ///
40     pub point_estimate: f64,
41     /// The standard error of this estimate
42     pub standard_error: f64,
43 }
44 
build_estimates( distributions: &Distributions, points: &PointEstimates, cl: f64, ) -> Estimates45 pub fn build_estimates(
46     distributions: &Distributions,
47     points: &PointEstimates,
48     cl: f64,
49 ) -> Estimates {
50     let to_estimate = |point_estimate, distribution: &Distribution<f64>| {
51         let (lb, ub) = distribution.confidence_interval(cl);
52 
53         Estimate {
54             confidence_interval: ConfidenceInterval {
55                 confidence_level: cl,
56                 lower_bound: lb,
57                 upper_bound: ub,
58             },
59             point_estimate,
60             standard_error: distribution.std_dev(None),
61         }
62     };
63 
64     Estimates {
65         mean: to_estimate(points.mean, &distributions.mean),
66         median: to_estimate(points.median, &distributions.median),
67         median_abs_dev: to_estimate(points.median_abs_dev, &distributions.median_abs_dev),
68         slope: None,
69         std_dev: to_estimate(points.std_dev, &distributions.std_dev),
70     }
71 }
72 
build_change_estimates( distributions: &ChangeDistributions, points: &ChangePointEstimates, cl: f64, ) -> ChangeEstimates73 pub fn build_change_estimates(
74     distributions: &ChangeDistributions,
75     points: &ChangePointEstimates,
76     cl: f64,
77 ) -> ChangeEstimates {
78     let to_estimate = |point_estimate, distribution: &Distribution<f64>| {
79         let (lb, ub) = distribution.confidence_interval(cl);
80 
81         Estimate {
82             confidence_interval: ConfidenceInterval {
83                 confidence_level: cl,
84                 lower_bound: lb,
85                 upper_bound: ub,
86             },
87             point_estimate,
88             standard_error: distribution.std_dev(None),
89         }
90     };
91 
92     ChangeEstimates {
93         mean: to_estimate(points.mean, &distributions.mean),
94         median: to_estimate(points.median, &distributions.median),
95     }
96 }
97 
98 pub struct PointEstimates {
99     pub mean: f64,
100     pub median: f64,
101     pub median_abs_dev: f64,
102     pub std_dev: f64,
103 }
104 
105 #[derive(Debug, Serialize, Deserialize, Clone)]
106 pub struct Estimates {
107     pub mean: Estimate,
108     pub median: Estimate,
109     pub median_abs_dev: Estimate,
110     pub slope: Option<Estimate>,
111     pub std_dev: Estimate,
112 }
113 impl Estimates {
typical(&self) -> &Estimate114     pub fn typical(&self) -> &Estimate {
115         self.slope.as_ref().unwrap_or(&self.mean)
116     }
get(&self, stat: Statistic) -> Option<&Estimate>117     pub fn get(&self, stat: Statistic) -> Option<&Estimate> {
118         match stat {
119             Statistic::Mean => Some(&self.mean),
120             Statistic::Median => Some(&self.median),
121             Statistic::MedianAbsDev => Some(&self.median_abs_dev),
122             Statistic::Slope => self.slope.as_ref(),
123             Statistic::StdDev => Some(&self.std_dev),
124             Statistic::Typical => Some(self.typical()),
125         }
126     }
127 }
128 
129 pub struct Distributions {
130     pub mean: Distribution<f64>,
131     pub median: Distribution<f64>,
132     pub median_abs_dev: Distribution<f64>,
133     pub slope: Option<Distribution<f64>>,
134     pub std_dev: Distribution<f64>,
135 }
136 impl Distributions {
typical(&self) -> &Distribution<f64>137     pub fn typical(&self) -> &Distribution<f64> {
138         self.slope.as_ref().unwrap_or(&self.mean)
139     }
get(&self, stat: Statistic) -> Option<&Distribution<f64>>140     pub fn get(&self, stat: Statistic) -> Option<&Distribution<f64>> {
141         match stat {
142             Statistic::Mean => Some(&self.mean),
143             Statistic::Median => Some(&self.median),
144             Statistic::MedianAbsDev => Some(&self.median_abs_dev),
145             Statistic::Slope => self.slope.as_ref(),
146             Statistic::StdDev => Some(&self.std_dev),
147             Statistic::Typical => Some(self.typical()),
148         }
149     }
150 }
151 
152 pub struct ChangePointEstimates {
153     pub mean: f64,
154     pub median: f64,
155 }
156 
157 #[derive(Debug, Serialize, Deserialize, Clone)]
158 pub struct ChangeEstimates {
159     pub mean: Estimate,
160     pub median: Estimate,
161 }
162 impl ChangeEstimates {
get(&self, stat: Statistic) -> &Estimate163     pub fn get(&self, stat: Statistic) -> &Estimate {
164         match stat {
165             Statistic::Mean => &self.mean,
166             Statistic::Median => &self.median,
167             _ => panic!("Unexpected statistic"),
168         }
169     }
170 }
171 
172 pub struct ChangeDistributions {
173     pub mean: Distribution<f64>,
174     pub median: Distribution<f64>,
175 }
176 impl ChangeDistributions {
get(&self, stat: Statistic) -> &Distribution<f64>177     pub fn get(&self, stat: Statistic) -> &Distribution<f64> {
178         match stat {
179             Statistic::Mean => &self.mean,
180             Statistic::Median => &self.median,
181             _ => panic!("Unexpected statistic"),
182         }
183     }
184 }
185