plotters_statistical/stats/
mod.rs1pub mod calibration;
9pub mod correlation;
10pub mod ecdf;
11pub mod gain;
12pub mod histogram;
13pub mod kde;
14pub mod normal;
15pub mod precision_recall;
16pub mod quartiles;
17pub mod roc;
18
19pub use calibration::{calibration_curve, CalibrationBin};
20pub use correlation::{correlation_matrix, pearson, rank, spearman, CorrelationMethod};
21pub use ecdf::{dkw_epsilon, ecdf, Ecdf as EcdfData};
22pub use gain::{gain_curve, GainPoint};
23pub use histogram::{histogram, BinRule, Histogram};
24pub use kde::{kde_curve, silverman_bandwidth, KdeCurve};
25pub use normal::norm_ppf;
26pub use precision_recall::{precision_recall_curve, PrCurve, PrPoint};
27pub use quartiles::{quartiles, Quartiles};
28pub use roc::{roc_curve, RocCurveData, RocPoint};
29
30use std::error::Error;
31use std::fmt;
32
33#[derive(Debug, Clone, PartialEq, Eq)]
36pub enum StatsError {
37 EmptyInput,
39 LengthMismatch {
41 scores: usize,
43 labels: usize,
45 },
46 NoPositiveLabels,
49 NoNegativeLabels,
52 InvalidBandwidth,
55 ZeroVariance,
59}
60
61impl fmt::Display for StatsError {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 match self {
64 StatsError::EmptyInput => write!(f, "input sample was empty"),
65 StatsError::LengthMismatch { scores, labels } => write!(
66 f,
67 "scores/labels length mismatch: {scores} scores vs {labels} labels"
68 ),
69 StatsError::NoPositiveLabels => {
70 write!(f, "no positive labels: TPR/recall is undefined")
71 }
72 StatsError::NoNegativeLabels => {
73 write!(f, "no negative labels: FPR is undefined")
74 }
75 StatsError::InvalidBandwidth => {
76 write!(f, "bandwidth must be a finite, strictly-positive number")
77 }
78 StatsError::ZeroVariance => {
79 write!(f, "input has zero variance (constant values)")
80 }
81 }
82 }
83}
84
85impl Error for StatsError {}
86
87pub(crate) fn sorted_finite(data: &[f64]) -> Vec<f64> {
92 let mut v: Vec<f64> = data.iter().copied().filter(|x| x.is_finite()).collect();
93 v.sort_by(|a, b| a.partial_cmp(b).expect("finite values are totally ordered"));
94 v
95}
96
97pub(crate) fn percentile_sorted(sorted: &[f64], p: f64) -> f64 {
103 debug_assert!(!sorted.is_empty());
104 let n = sorted.len();
105 if n == 1 {
106 return sorted[0];
107 }
108 let rank = p.clamp(0.0, 1.0) * (n as f64 - 1.0);
109 let lo = rank.floor() as usize;
110 let hi = rank.ceil() as usize;
111 if lo == hi {
112 sorted[lo]
113 } else {
114 let frac = rank - lo as f64;
115 sorted[lo] * (1.0 - frac) + sorted[hi] * frac
116 }
117}