use approx::assert_abs_diff_eq;
use plotters_statistical::stats::{
calibration_curve, correlation_matrix, dkw_epsilon, ecdf, gain_curve, histogram, norm_ppf,
pearson, rank, spearman, BinRule, CorrelationMethod, StatsError,
};
#[test]
fn pearson_perfect_and_anti() {
assert_abs_diff_eq!(pearson(&[1.0, 2.0, 3.0], &[2.0, 4.0, 6.0]).unwrap(), 1.0);
assert_abs_diff_eq!(pearson(&[1.0, 2.0, 3.0], &[3.0, 2.0, 1.0]).unwrap(), -1.0);
}
#[test]
fn pearson_zero_variance_errors() {
assert_eq!(
pearson(&[1.0, 1.0, 1.0], &[1.0, 2.0, 3.0]),
Err(StatsError::ZeroVariance)
);
}
#[test]
fn rank_handles_ties() {
assert_eq!(rank(&[10.0, 20.0, 20.0, 40.0]), vec![1.0, 2.5, 2.5, 4.0]);
}
#[test]
fn spearman_monotonic_nonlinear() {
let s = spearman(&[1.0, 2.0, 3.0, 4.0], &[1.0, 4.0, 9.0, 16.0]).unwrap();
assert_abs_diff_eq!(s, 1.0);
}
#[test]
fn correlation_matrix_is_symmetric_unit_diagonal() {
let cols = vec![
vec![1.0, 2.0, 3.0, 4.0],
vec![2.0, 4.0, 6.0, 8.0],
vec![4.0, 3.0, 2.0, 1.0],
];
let m = correlation_matrix(&cols, CorrelationMethod::Pearson).unwrap();
for (i, row) in m.iter().enumerate() {
assert_abs_diff_eq!(row[i], 1.0);
for (j, &v) in row.iter().enumerate() {
assert_abs_diff_eq!(v, m[j][i]); }
}
assert_abs_diff_eq!(m[0][1], 1.0); assert_abs_diff_eq!(m[0][2], -1.0); }
#[test]
fn ecdf_basic() {
let e = ecdf(&[3.0, 1.0, 2.0, 2.0]).unwrap();
assert_eq!(e.x, vec![1.0, 2.0, 3.0]);
assert_eq!(e.p, vec![0.25, 0.75, 1.0]);
assert_eq!(e.n, 4);
}
#[test]
fn dkw_epsilon_reference() {
let expected = ((2.0f64 / 0.05).ln() / 200.0).sqrt();
assert_abs_diff_eq!(dkw_epsilon(100, 0.05), expected, epsilon = 1e-12);
}
#[test]
fn norm_ppf_reference_points() {
assert_abs_diff_eq!(norm_ppf(0.5), 0.0, epsilon = 1e-9);
assert_abs_diff_eq!(norm_ppf(0.975), 1.959963985, epsilon = 1e-6);
assert_abs_diff_eq!(norm_ppf(0.025), -1.959963985, epsilon = 1e-6);
}
#[test]
fn histogram_counts_sum_to_n() {
let data: Vec<f64> = (0..=10).map(|i| i as f64).collect();
let h = histogram(&data, BinRule::Count(5)).unwrap();
assert_eq!(h.edges.len(), 6);
assert_eq!(h.counts.iter().sum::<usize>(), data.len());
assert_abs_diff_eq!(h.bin_width(), 2.0);
}
#[test]
fn histogram_constant_errors() {
assert_eq!(
histogram(&[5.0, 5.0, 5.0], BinRule::Sturges),
Err(StatsError::ZeroVariance)
);
}
#[test]
fn calibration_bins_partition() {
let scores = [0.05, 0.15, 0.85, 0.95];
let labels = [false, false, true, true];
let bins = calibration_curve(&scores, &labels, 2).unwrap();
assert_eq!(bins.len(), 2);
assert_abs_diff_eq!(bins[0].observed_freq, 0.0);
assert_abs_diff_eq!(bins[1].observed_freq, 1.0);
}
#[test]
fn gain_perfect_ranking() {
let scores = [0.9, 0.8, 0.2, 0.1];
let labels = [true, true, false, false];
let g = gain_curve(&scores, &labels).unwrap();
assert!(g[0].fraction == 0.0 && g[0].lift.is_nan());
let at_half = g.iter().find(|p| (p.fraction - 0.5).abs() < 1e-9).unwrap();
assert_abs_diff_eq!(at_half.gain, 1.0);
assert_abs_diff_eq!(g.last().unwrap().gain, 1.0);
}
#[test]
fn gain_no_positives_errors() {
assert_eq!(
gain_curve(&[0.1, 0.2], &[false, false]),
Err(StatsError::NoPositiveLabels)
);
}