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