commit 7823ebe6228564c1b3718001aeeee0f1580c60fd Author: Vinzent Steinberg Date: Wed Apr 26 20:24:20 2017 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4308d82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target/ +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..71e0119 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +authors = ["Vinzent Steinberg "] +name = "average" +version = "0.1.0" + +[dependencies] +conv = "0.3" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6e6f5cc --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,83 @@ +extern crate conv; + +use conv::ApproxFrom; + +/// Represent and average value of a sequence of numbers. +/// +/// The average is calculated iteratively, so the sequence of numbers can be an +/// iterator. +#[derive(Debug, Clone)] +pub struct Average { + avg: f64, + n: u64, + v: f64, +} + +impl Average { + /// Create a new average. + pub fn new() -> Average { + Average { avg: 0., n: 0, v: 0. } + } + + /// Add a number to the sequence of which the average is calculated. + pub fn add(&mut self, x: f64) { + self.n += 1; + let prev_avg = self.avg; + self.avg += (x - prev_avg) / f64::approx_from(self.n).unwrap(); + self.v += (x - prev_avg) * (x - self.avg); + } + + /// Return the average of the sequence. + pub fn avg(&self) -> f64 { + self.avg + } + + /// Return the number of elements in the sequence. + pub fn len(&self) -> u64 { + self.n + } + + /// Calculate the unbiased sample variance of the sequence. + pub fn var(&self) -> f64 { + if self.n < 2 { + return 0.; + } + self.v / f64::approx_from(self.n - 1).unwrap() + } + + /// Calculate the standard error of the average of the sequence. + pub fn err(&self) -> f64 { + if self.n == 0 { + return 0.; + } + (self.var() / f64::approx_from(self.n).unwrap()).sqrt() + } +} + +impl std::iter::FromIterator for Average { + fn from_iter(iter: T) -> Average + where T: IntoIterator + { + let mut a = Average::new(); + for i in iter { + a.add(i); + } + a + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use ::conv::ConvAsUtil; + + #[test] + fn average() { + let a: Average = (1..6).map(|x| x.approx().unwrap()).collect(); + assert_eq!(a.avg(), 3.0); + assert_eq!(a.len(), 5); + assert_eq!(a.var(), 2.5); + assert!((a.err() - f64::sqrt(0.5)).abs() < 1e-16); + } +}