criterion/
estimate.rs

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}