use symplex::linprog::{q, qi};
use symplex::prelude::*;
use symplex::stats::aggregation::{
DawidSkeneOpts, LabelTable, dawid_skene, majority_votes, worker_accuracy,
};
use symplex::stats::agreement::{
IccForm, Level, RatingTable, cohen_kappa, fleiss_kappa_ratings, icc, kendall_w,
krippendorff_alpha, percent_agreement,
};
use symplex::stats::data::{self, Ddof, Q, QuantileMethod};
use symplex::stats::estimation::{IntervalMethod, proportion_interval};
use symplex::stats::hypothesis::{
Alternative, RankMethod, benjamini_hochberg, binomial_test, chi_square_independence, cohens_d,
counts, fisher_exact, mann_whitney_u, t_test_two_sample,
};
fn ratings() -> RatingTable {
RatingTable::from_i64_missing(&[
&[Some(0), Some(0), Some(0), Some(0), Some(1)],
&[Some(1), Some(1), Some(1), Some(2), Some(1)],
&[Some(2), Some(2), Some(2), Some(2), Some(2)],
&[Some(0), Some(1), Some(0), None, Some(0)],
&[Some(1), Some(1), Some(2), Some(1), Some(1)],
&[Some(2), Some(1), Some(2), Some(2), None],
&[Some(0), Some(0), Some(1), Some(0), Some(0)],
&[Some(1), Some(2), Some(1), Some(1), Some(1)],
])
.unwrap()
}
#[test]
fn agreement_walkthrough() {
let table = ratings();
let a: Vec<Q> = data::from_i64(&[0, 1, 2, 0, 1, 2, 0, 1]);
let b: Vec<Q> = data::from_i64(&[0, 1, 2, 1, 1, 1, 0, 2]);
assert_eq!(percent_agreement(&a, &b).unwrap(), q(5, 8));
let kappa = cohen_kappa(&a, &b).unwrap();
assert_eq!(kappa.observed, q(5, 8));
assert_eq!(kappa.expected, q(11, 32));
assert_eq!(kappa.kappa, q(3, 7));
let alpha = krippendorff_alpha(&table, Level::Nominal).unwrap();
assert_eq!(alpha, q(214, 473));
let alpha_ord = krippendorff_alpha(&table, Level::Ordinal).unwrap();
assert_eq!(alpha_ord, q(577, 836));
let complete = RatingTable::from_i64(&[
&[0, 0, 0, 0, 1],
&[1, 1, 1, 2, 1],
&[2, 2, 2, 2, 2],
&[1, 1, 2, 1, 1],
&[0, 0, 1, 0, 0],
&[1, 2, 1, 1, 1],
])
.unwrap();
let fleiss = fleiss_kappa_ratings(&complete).unwrap();
assert_eq!(fleiss, q(23, 48));
let icc21 = icc(&complete, IccForm::Icc2Single).unwrap();
assert!(icc21 > q(1, 2) && icc21 < qi(1), "{icc21}");
let w = kendall_w(&complete).unwrap();
assert!(w > q(1, 2) && w <= qi(1), "{w}");
println!(
"agreement: pct {} κ {} α_nom {} α_ord {} fleiss {} icc21 {} W {}",
percent_agreement(&a, &b).unwrap(),
kappa.kappa,
alpha,
alpha_ord,
fleiss,
icc21,
w
);
}
#[test]
fn aggregation_walkthrough() {
let labels = LabelTable::from_rows(
&[
&[Some(0), Some(0), Some(0), Some(0), Some(1)],
&[Some(1), Some(1), Some(1), Some(2), Some(1)],
&[Some(2), Some(2), Some(2), Some(2), Some(2)],
&[Some(0), Some(1), Some(0), None, Some(0)],
&[Some(1), Some(1), Some(2), Some(1), Some(1)],
&[Some(2), Some(1), Some(2), Some(2), None],
&[Some(0), Some(0), Some(1), Some(0), Some(0)],
&[Some(1), Some(2), Some(1), Some(1), Some(1)],
],
3,
)
.unwrap();
let votes = majority_votes(&labels);
let winners: Vec<Option<usize>> = votes.iter().map(|v| v.winner).collect();
assert_eq!(
winners,
[
Some(0),
Some(1),
Some(2),
Some(0),
Some(1),
Some(2),
Some(0),
Some(1)
]
);
let ds = dawid_skene(&labels, &DawidSkeneOpts::default()).unwrap();
assert!(ds.converged);
assert_eq!(
ds.labels(),
winners.iter().map(|w| w.unwrap()).collect::<Vec<_>>()
);
let gold: Vec<usize> = winners.iter().map(|w| w.unwrap()).collect();
let rater1: Vec<Option<usize>> = labels.rater(1).unwrap();
let acc = worker_accuracy(&rater1, &gold).unwrap();
assert_eq!(acc.correct, 5);
assert_eq!(acc.answered, 8);
assert_eq!(acc.accuracy, Some(q(5, 8)));
let ci = proportion_interval(5, 8, 0.95, IntervalMethod::ClopperPearson).unwrap();
assert!(
(ci.lower - 0.244_863_216_366_551_6).abs() < 1e-9
&& (ci.upper - 0.914_766_585_862_746_5).abs() < 1e-9,
"{ci}"
);
let wilson = proportion_interval(5, 8, 0.95, IntervalMethod::Wilson).unwrap();
assert!(wilson.lower < 0.625 && wilson.upper > 0.625);
}
#[test]
fn comparison_walkthrough() -> Result<(), SymplexError> {
let ctx = Context::new();
let fast = data::from_i64(&[12, 15, 11, 14, 13, 16, 10, 17]);
let slow = data::from_i64(&[18, 22, 19, 25, 20, 21, 23, 24]);
assert_eq!(data::mean(&fast)?, q(27, 2));
assert_eq!(data::variance(&fast, Ddof::Sample)?, qi(6));
assert_eq!(data::median(&slow)?, q(43, 2));
assert_eq!(
data::quantile(&slow, &q(3, 4), QuantileMethod::Inclusive)?,
q(93, 4)
);
let t = t_test_two_sample(&ctx, &fast, &slow, false, Alternative::TwoSided)?;
assert!((t.statistic_f64()? - -6.531_972_647_421_809).abs() < 1e-12);
assert!(
(t.p_value_f64()? - 1.329_873_727_130_148_8e-5).abs() < 1e-15,
"{}",
t.p_value
);
let d = cohens_d(&ctx, &fast, &slow, true)?;
assert!(d.eval_f64()? < -3.0, "{d}");
let u = mann_whitney_u(&ctx, &fast, &slow, Alternative::TwoSided, RankMethod::Exact)?;
assert_eq!(u.statistic, ctx.int(0));
assert_eq!(u.p_value, ctx.rational(1, 6435));
let approved = [[30usize, 10], [18, 22]];
let f = fisher_exact(&ctx, approved, Alternative::TwoSided)?;
assert!(
(f.p_value_f64()? - 0.011_506_212_016_560_47).abs() < 1e-12,
"{}",
f.p_value
);
let table = counts(&[&[30, 10], &[18, 22]]);
let chi = chi_square_independence(&ctx, &table, true)?;
assert_eq!(chi.df, 1);
assert!((chi.p_value.eval_f64()? - 0.012_059_616_177_490_23).abs() < 1e-12);
let bt = binomial_test(&ctx, 14, 20, &q(1, 3), Alternative::Greater)?;
assert_eq!(bt.p_value_exact(), Some(q(1_021_403, 1_162_261_467)));
let adj = benjamini_hochberg(&[0.001, 0.02, 0.03, 0.2, 0.8], 0.05)?;
assert_eq!(adj.reject, vec![true, true, true, false, false]);
Ok(())
}