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