use std::fmt; use crate::stats::Distribution; #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize, Debug)] pub enum Statistic { Mean, Median, MedianAbsDev, Slope, StdDev, Typical, } impl fmt::Display for Statistic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Statistic::Mean => f.pad("mean"), Statistic::Median => f.pad("median"), Statistic::MedianAbsDev => f.pad("MAD"), Statistic::Slope => f.pad("slope"), Statistic::StdDev => f.pad("SD"), Statistic::Typical => f.pad("typical"), } } } #[derive(Clone, PartialEq, Deserialize, Serialize, Debug)] pub struct ConfidenceInterval { pub confidence_level: f64, pub lower_bound: f64, pub upper_bound: f64, } #[derive(Clone, PartialEq, Deserialize, Serialize, Debug)] pub struct Estimate { /// The confidence interval for this estimate pub confidence_interval: ConfidenceInterval, /// pub point_estimate: f64, /// The standard error of this estimate pub standard_error: f64, } pub fn build_estimates( distributions: &Distributions, points: &PointEstimates, cl: f64, ) -> Estimates { let to_estimate = |point_estimate, distribution: &Distribution| { let (lb, ub) = distribution.confidence_interval(cl); Estimate { confidence_interval: ConfidenceInterval { confidence_level: cl, lower_bound: lb, upper_bound: ub, }, point_estimate, standard_error: distribution.std_dev(None), } }; Estimates { mean: to_estimate(points.mean, &distributions.mean), median: to_estimate(points.median, &distributions.median), median_abs_dev: to_estimate(points.median_abs_dev, &distributions.median_abs_dev), slope: None, std_dev: to_estimate(points.std_dev, &distributions.std_dev), } } pub fn build_change_estimates( distributions: &ChangeDistributions, points: &ChangePointEstimates, cl: f64, ) -> ChangeEstimates { let to_estimate = |point_estimate, distribution: &Distribution| { let (lb, ub) = distribution.confidence_interval(cl); Estimate { confidence_interval: ConfidenceInterval { confidence_level: cl, lower_bound: lb, upper_bound: ub, }, point_estimate, standard_error: distribution.std_dev(None), } }; ChangeEstimates { mean: to_estimate(points.mean, &distributions.mean), median: to_estimate(points.median, &distributions.median), } } pub struct PointEstimates { pub mean: f64, pub median: f64, pub median_abs_dev: f64, pub std_dev: f64, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Estimates { pub mean: Estimate, pub median: Estimate, pub median_abs_dev: Estimate, pub slope: Option, pub std_dev: Estimate, } impl Estimates { pub fn typical(&self) -> &Estimate { self.slope.as_ref().unwrap_or(&self.mean) } pub fn get(&self, stat: Statistic) -> Option<&Estimate> { match stat { Statistic::Mean => Some(&self.mean), Statistic::Median => Some(&self.median), Statistic::MedianAbsDev => Some(&self.median_abs_dev), Statistic::Slope => self.slope.as_ref(), Statistic::StdDev => Some(&self.std_dev), Statistic::Typical => Some(self.typical()), } } } pub struct Distributions { pub mean: Distribution, pub median: Distribution, pub median_abs_dev: Distribution, pub slope: Option>, pub std_dev: Distribution, } impl Distributions { pub fn typical(&self) -> &Distribution { self.slope.as_ref().unwrap_or(&self.mean) } pub fn get(&self, stat: Statistic) -> Option<&Distribution> { match stat { Statistic::Mean => Some(&self.mean), Statistic::Median => Some(&self.median), Statistic::MedianAbsDev => Some(&self.median_abs_dev), Statistic::Slope => self.slope.as_ref(), Statistic::StdDev => Some(&self.std_dev), Statistic::Typical => Some(self.typical()), } } } pub struct ChangePointEstimates { pub mean: f64, pub median: f64, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ChangeEstimates { pub mean: Estimate, pub median: Estimate, } impl ChangeEstimates { pub fn get(&self, stat: Statistic) -> &Estimate { match stat { Statistic::Mean => &self.mean, Statistic::Median => &self.median, _ => panic!("Unexpected statistic"), } } } pub struct ChangeDistributions { pub mean: Distribution, pub median: Distribution, } impl ChangeDistributions { pub fn get(&self, stat: Statistic) -> &Distribution { match stat { Statistic::Mean => &self.mean, Statistic::Median => &self.median, _ => panic!("Unexpected statistic"), } } }