pub mod calibration;
pub mod correlation;
pub mod ecdf;
pub mod gain;
pub mod histogram;
pub mod kde;
pub mod normal;
pub mod precision_recall;
pub mod quartiles;
pub mod roc;
pub use calibration::{calibration_curve, CalibrationBin};
pub use correlation::{correlation_matrix, pearson, rank, spearman, CorrelationMethod};
pub use ecdf::{dkw_epsilon, ecdf, Ecdf as EcdfData};
pub use gain::{gain_curve, GainPoint};
pub use histogram::{histogram, BinRule, Histogram};
pub use kde::{kde_curve, silverman_bandwidth, KdeCurve};
pub use normal::norm_ppf;
pub use precision_recall::{precision_recall_curve, PrCurve, PrPoint};
pub use quartiles::{quartiles, Quartiles};
pub use roc::{roc_curve, RocCurveData, RocPoint};
use std::error::Error;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StatsError {
EmptyInput,
LengthMismatch {
scores: usize,
labels: usize,
},
NoPositiveLabels,
NoNegativeLabels,
InvalidBandwidth,
ZeroVariance,
}
impl fmt::Display for StatsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StatsError::EmptyInput => write!(f, "input sample was empty"),
StatsError::LengthMismatch { scores, labels } => write!(
f,
"scores/labels length mismatch: {scores} scores vs {labels} labels"
),
StatsError::NoPositiveLabels => {
write!(f, "no positive labels: TPR/recall is undefined")
}
StatsError::NoNegativeLabels => {
write!(f, "no negative labels: FPR is undefined")
}
StatsError::InvalidBandwidth => {
write!(f, "bandwidth must be a finite, strictly-positive number")
}
StatsError::ZeroVariance => {
write!(f, "input has zero variance (constant values)")
}
}
}
}
impl Error for StatsError {}
pub(crate) fn sorted_finite(data: &[f64]) -> Vec<f64> {
let mut v: Vec<f64> = data.iter().copied().filter(|x| x.is_finite()).collect();
v.sort_by(|a, b| a.partial_cmp(b).expect("finite values are totally ordered"));
v
}
pub(crate) fn percentile_sorted(sorted: &[f64], p: f64) -> f64 {
debug_assert!(!sorted.is_empty());
let n = sorted.len();
if n == 1 {
return sorted[0];
}
let rank = p.clamp(0.0, 1.0) * (n as f64 - 1.0);
let lo = rank.floor() as usize;
let hi = rank.ceil() as usize;
if lo == hi {
sorted[lo]
} else {
let frac = rank - lo as f64;
sorted[lo] * (1.0 - frac) + sorted[hi] * frac
}
}