1use std::fmt;
2
3use crate::stats::Distribution;
4
5#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize, Debug)]
6pub enum Statistic {
7 Mean,
8 Median,
9 MedianAbsDev,
10 Slope,
11 StdDev,
12 Typical,
13}
14
15impl fmt::Display for Statistic {
16 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)]
29pub 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)]
36pub 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
45pub 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
73pub 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
98pub 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)]
106pub 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}
113impl Estimates {
114 pub fn typical(&self) -> &Estimate {
115 self.slope.as_ref().unwrap_or(&self.mean)
116 }
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
129pub 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}
136impl Distributions {
137 pub fn typical(&self) -> &Distribution<f64> {
138 self.slope.as_ref().unwrap_or(&self.mean)
139 }
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
152pub struct ChangePointEstimates {
153 pub mean: f64,
154 pub median: f64,
155}
156
157#[derive(Debug, Serialize, Deserialize, Clone)]
158pub struct ChangeEstimates {
159 pub mean: Estimate,
160 pub median: Estimate,
161}
162impl ChangeEstimates {
163 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
172pub struct ChangeDistributions {
173 pub mean: Distribution<f64>,
174 pub median: Distribution<f64>,
175}
176impl ChangeDistributions {
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