use crate::*;
use error::*;
use mean::StatisticsOps;
use num_traits::Float;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Paired<T: Float> {
stats: mean::Arithmetic<T>,
}
impl<T: Float> Paired<T> {
pub fn append_pair(&mut self, data_a: T, data_b: T) -> CIResult<()> {
self.stats.append(data_a - data_b)
}
pub fn extend_tuple<I>(&mut self, iter: &I) -> CIResult<()>
where
for<'a> &'a I: IntoIterator<Item = &'a (T, T)>,
{
for &(x, y) in iter.into_iter() {
self.stats.append(x - y)?;
}
Ok(())
}
pub fn extend<I1, I2>(&mut self, data_a: &I1, data_b: &I2) -> CIResult<()>
where
for<'a> &'a I1: IntoIterator<Item = &'a T>,
for<'b> &'b I2: IntoIterator<Item = &'b T>,
{
let mut data_a = data_a.into_iter();
let mut data_b = data_b.into_iter();
let mut count = 0;
loop {
match (data_a.next(), data_b.next()) {
(Some(x), Some(y)) => {
count += 1;
self.stats.append(*x - *y)?
}
(None, None) => return Ok(()),
(None, _) => {
return Err(CIError::DifferentSampleSizes(
count,
count + 1 + data_b.count(),
))
}
(_, None) => {
return Err(CIError::DifferentSampleSizes(
count + 1 + data_a.count(),
count,
))
}
}
}
}
pub fn sample_mean(&self) -> T {
self.stats.sample_mean()
}
pub fn sample_sem(&self) -> T {
self.stats.sample_sem()
}
pub fn sample_count(&self) -> usize {
self.stats.sample_count()
}
pub fn ci_mean(&self, confidence: Confidence) -> CIResult<Interval<T>> {
self.stats.ci_mean(confidence)
}
pub fn ci<Ia, Ib>(confidence: Confidence, data_a: &Ia, data_b: &Ib) -> CIResult<Interval<T>>
where
for<'a> &'a Ia: IntoIterator<Item = &'a T>,
for<'a> &'a Ib: IntoIterator<Item = &'a T>,
{
let mut stats = Paired::default();
stats.extend(data_a, data_b)?;
stats.ci_mean(confidence)
}
}
impl<T: Float> Default for Paired<T> {
fn default() -> Self {
Self {
stats: mean::Arithmetic::default(),
}
}
}
impl<F: Float> std::ops::Add for Paired<F> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
stats: self.stats + rhs.stats,
}
}
}
impl<F: Float> std::ops::AddAssign for Paired<F> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.stats += rhs.stats;
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Unpaired<T: Float> {
stats_a: mean::Arithmetic<T>,
stats_b: mean::Arithmetic<T>,
}
impl<T: Float> Default for Unpaired<T> {
fn default() -> Self {
Self {
stats_a: mean::Arithmetic::default(),
stats_b: mean::Arithmetic::default(),
}
}
}
impl<T: Float> Unpaired<T> {
pub fn new(stats_a: mean::Arithmetic<T>, stats_b: mean::Arithmetic<T>) -> Self {
Self { stats_a, stats_b }
}
pub fn from_iter<Ia, Ib>(data_a: &Ia, data_b: &Ib) -> CIResult<Self>
where
for<'a> &'a Ia: IntoIterator<Item = &'a T>,
for<'b> &'b Ib: IntoIterator<Item = &'b T>,
{
let mut stats = Self::default();
stats.extend_a(data_a)?;
stats.extend_b(data_b)?;
Ok(stats)
}
pub fn stats_a(&self) -> &mean::Arithmetic<T> {
&self.stats_a
}
pub fn stats_a_mut(&mut self) -> &mut mean::Arithmetic<T> {
&mut self.stats_a
}
pub fn stats_b(&self) -> &mean::Arithmetic<T> {
&self.stats_b
}
pub fn stats_b_mut(&mut self) -> &mut mean::Arithmetic<T> {
&mut self.stats_b
}
pub fn append_pair(&mut self, data_a: T, data_b: T) -> CIResult<()> {
self.append_a(data_a)?;
self.append_b(data_b)?;
Ok(())
}
pub fn append_a(&mut self, data_a: T) -> CIResult<()> {
self.stats_a.append(data_a)
}
pub fn append_b(&mut self, data_b: T) -> CIResult<()> {
self.stats_b.append(data_b)
}
pub fn extend_a<I>(&mut self, data_a: &I) -> CIResult<()>
where
for<'a> &'a I: IntoIterator<Item = &'a T>,
{
self.stats_a.extend(data_a)
}
pub fn extend_b<I>(&mut self, data_b: &I) -> CIResult<()>
where
for<'a> &'a I: IntoIterator<Item = &'a T>,
{
self.stats_b.extend(data_b)
}
pub fn extend<Ia, Ib>(&mut self, data_a: &Ia, data_b: &Ib) -> CIResult<()>
where
for<'a> &'a Ia: IntoIterator<Item = &'a T>,
for<'b> &'b Ib: IntoIterator<Item = &'b T>,
{
self.stats_a.extend(data_a)?;
self.stats_b.extend(data_b)?;
Ok(())
}
pub fn ci_mean(&self, confidence: Confidence) -> CIResult<Interval<T>> {
let stats_a = self.stats_a;
let stats_b = self.stats_b;
let n_a = T::from(stats_a.sample_count()).convert("stats_a.sample_count")?;
let n_b = T::from(stats_b.sample_count()).convert("stats_b.sample_count")?;
let mean_a = stats_a.sample_mean();
let mean_b = stats_b.sample_mean();
let std_dev_a = stats_a.sample_std_dev();
let std_dev_b = stats_b.sample_std_dev();
let mean_difference = mean_a - mean_b;
let sa2_na = std_dev_a * std_dev_a / n_a;
let sb2_nb = std_dev_b * std_dev_b / n_b;
let sum_s2_n = sa2_na + sb2_nb;
let std_err_mean = sum_s2_n.sqrt();
let effective_dof = sum_s2_n * sum_s2_n
/ (sa2_na * sa2_na / (n_a + T::one())
+ sb2_nb * sb2_nb / (n_b + T::one())) - T::one() - T::one();
let (lo, hi) = stats::interval_bounds(
confidence,
mean_difference.try_f64("mean_difference")?,
std_err_mean.try_f64("std_err_mean")?,
effective_dof.try_f64("effective_dof")?,
);
let lo = T::from(lo).convert("lo")?;
let hi = T::from(hi).convert("hi")?;
match confidence {
Confidence::TwoSided(_) => Interval::new(lo, hi).map_err(|e| e.into()),
Confidence::UpperOneSided(_) => Ok(Interval::new_upper(lo)),
Confidence::LowerOneSided(_) => Ok(Interval::new_lower(hi)),
}
}
pub fn ci<Ia, Ib>(confidence: Confidence, data_a: &Ia, data_b: &Ib) -> CIResult<Interval<T>>
where
for<'a> &'a Ia: IntoIterator<Item = &'a T>,
for<'a> &'a Ib: IntoIterator<Item = &'a T>,
{
let mut stats = Self::default();
stats.extend(data_a, data_b)?;
stats.ci_mean(confidence)
}
}
impl<F: Float> std::ops::Add for Unpaired<F> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self::Output {
Self {
stats_a: self.stats_a + rhs.stats_a,
stats_b: self.stats_b + rhs.stats_b,
}
}
}
impl<F: Float> std::ops::AddAssign for Unpaired<F> {
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.stats_a += rhs.stats_a;
self.stats_b += rhs.stats_b;
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::*;
#[test]
fn test_paired() {
{
let data_bottom_water = [
0.430, 0.266, 0.567, 0.531, 0.707, 0.716, 0.651, 0.589, 0.469, 0.723,
];
let data_surface_water = [
0.415, 0.238, 0.390, 0.410, 0.605, 0.609, 0.632, 0.523, 0.411, 0.612,
];
let ci = Paired::ci(
Confidence::new_two_sided(0.95),
&data_bottom_water,
&data_surface_water,
)
.unwrap();
println!("ci = {} (ref: )", ci);
println!("reference: (0.04299, 0.11781)");
assert_abs_diff_eq!(ci, Interval::new(0.04299, 0.11781).unwrap(), epsilon = 1e-4);
}
{
let data_watch_a = [9.8, 9.8, 10.1, 10.1, 10.2];
let data_watch_b = [10.1, 10., 10.2, 9.9, 10.1];
let ci = Paired::ci(
Confidence::new_two_sided(0.95),
&data_watch_b,
&data_watch_a,
)
.unwrap();
println!("ci = {}", ci);
println!("reference: (-0.20, 0.32)");
assert_abs_diff_eq!(ci, Interval::new(-0.20, 0.32).unwrap(), epsilon = 1e-2);
let data_pre = [140., 152., 153., 159., 150., 146.];
let data_post = [150., 159., 170., 164., 148., 166.];
let ci = Paired::ci(Confidence::new_two_sided(0.95), &data_post, &data_pre).unwrap();
println!("ci = {}", ci);
println!("reference: (1.03,17.97)");
assert_abs_diff_eq!(ci, Interval::new(1.03, 17.97).unwrap(), epsilon = 1e-2);
}
}
#[test]
fn test_unpaired() {
let data_high_protein = [
134., 146., 104., 119., 124., 161., 107., 83., 113., 129., 97., 123.,
];
let data_low_protein = [70., 118., 101., 85., 107., 132., 94.];
let ci = Unpaired::ci(
Confidence::new_two_sided(0.95),
&data_high_protein,
&data_low_protein,
)
.unwrap();
println!("ci = {}", ci);
println!("reference: (-2.193679, 40.193679)");
assert_abs_diff_eq!(
ci,
Interval::new(-2.193679, 40.193679).unwrap(),
epsilon = 1e-2
);
}
#[test]
fn test_paired_diff_length() {
let sample_size = 10;
let data1 = (0..sample_size)
.map(|_| rand::random::<f64>())
.collect::<Vec<_>>();
let data2 = (0..sample_size + 1)
.map(|_| rand::random::<f64>())
.collect::<Vec<_>>();
let mut stats = comparison::Paired::default();
let res = stats.extend(&data1, &data2);
assert!(res.is_err());
match res.unwrap_err() {
CIError::DifferentSampleSizes(a, b) => {
println!("DifferentSampleSizes({a},{b})");
assert_eq!(a, sample_size);
assert_eq!(b, sample_size + 1);
}
e => panic!("unexpected error: {}", e),
}
}
}