From 334d8ae9cddfd11879c13b5338ad74c786deb50f Mon Sep 17 00:00:00 2001 From: Vinzent Steinberg Date: Fri, 19 May 2017 17:53:54 +0200 Subject: [PATCH] Improve documentation --- src/average.rs | 36 +++++++++++++++++++------------- src/lib.rs | 25 +++++++++++++++++++++++ src/weighted_average.rs | 44 ++++++++++++++++++++++++++-------------- src/weighted_average2.rs | 44 +++++++++++++++++++++++++++------------- 4 files changed, 106 insertions(+), 43 deletions(-) diff --git a/src/average.rs b/src/average.rs index 3cadd5e..74e3b69 100644 --- a/src/average.rs +++ b/src/average.rs @@ -2,18 +2,23 @@ use core; use conv::ApproxFrom; -/// Represent the arithmetic mean and the variance of a sequence of numbers. +/// Estimate the arithmetic mean and the variance of a sequence of numbers +/// ("population"). +/// +/// This can be used to estimate the standard error of the mean. /// /// Everything is calculated iteratively using constant memory, so the sequence /// of numbers can be an iterator. The used algorithms try to avoid numerical /// instabilities. /// +/// +/// ## Example +/// /// ``` /// use average::Average; /// /// let a: Average = (1..6).map(Into::into).collect(); -/// assert_eq!(a.mean(), 3.0); -/// assert_eq!(a.sample_variance(), 2.5); +/// println!("The average is {} ± {}.", a.mean(), a.error()); /// ``` #[derive(Debug, Clone)] pub struct Average { @@ -26,12 +31,12 @@ pub struct Average { } impl Average { - /// Create a new average. + /// Create a new average estimator. pub fn new() -> Average { Average { avg: 0., n: 0, v: 0. } } - /// Add a sample to the sequence of which the average is calculated. + /// Add an element sampled from the population. pub fn add(&mut self, sample: f64) { // This algorithm introduced by Welford in 1962 trades numerical // stability for a division inside the loop. @@ -43,24 +48,24 @@ impl Average { self.v += delta * (sample - self.avg); } - /// Determine whether the sequence is empty. + /// Determine whether the samples are empty. pub fn is_empty(&self) -> bool { self.n == 0 } - /// Estimate the mean of the sequence. + /// Estimate the mean of the population. pub fn mean(&self) -> f64 { self.avg } - /// Return the number of elements in the sequence. + /// Return the number of samples. pub fn len(&self) -> u64 { self.n } - /// Calculate the unbiased sample variance of the sequence. + /// Calculate the sample variance. /// - /// This assumes that the sequence consists of samples of a larger population. + /// This is an unbiased estimator of the variance of the population. pub fn sample_variance(&self) -> f64 { if self.n < 2 { return 0.; @@ -68,9 +73,9 @@ impl Average { self.v / f64::approx_from(self.n - 1).unwrap() } - /// Calculate the population variance of the sequence. + /// Calculate the population variance of the sample. /// - /// This assumes that the sequence consists of the entire population. + /// This is a biased estimator of the variance of the population. pub fn population_variance(&self) -> f64 { if self.n < 2 { return 0.; @@ -78,7 +83,7 @@ impl Average { self.v / f64::approx_from(self.n).unwrap() } - /// Estimate the standard error of the mean of the sequence. + /// Estimate the standard error of the mean of the population. pub fn error(&self) -> f64 { if self.n == 0 { return 0.; @@ -86,7 +91,10 @@ impl Average { (self.sample_variance() / f64::approx_from(self.n).unwrap()).sqrt() } - /// Merge the average of another sequence into this one. + /// Merge another sample into this one. + /// + /// + /// ## Example /// /// ``` /// use average::Average; diff --git a/src/lib.rs b/src/lib.rs index f0185b7..b870eca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,28 @@ +//! This crate provides estimators for the weighted and unweighted average of a +//! sequence of numbers, and for their standard errors. The typical workflow +//! looks like this: +//! +//! 1. Initialize your estimator of choice (`Average`, `WeightedAverage` or +//! `WeightedAverage2`) with `new()`. +//! 2. Add some subset (called "samples") of the sequence of numbers (called +//! "population") for which you want to estimate the average, using `add()` +//! or `collect()`. +//! 3. Calculate the arithmetic mean with `mean()` and its standard error with +//! `error(). +//! +//! You can run several estimators in parallel and merge them into one with +//! `merge()`. +//! +//! ## Example +//! +//! ``` +//! use average::Average; +//! +//! let mut a: Average = (1..6).map(Into::into).collect(); +//! a.add(42.); +//! println!("The average is {} ± {}.", a.mean(), a.error()); +//! ``` + #![no_std] extern crate conv; diff --git a/src/weighted_average.rs b/src/weighted_average.rs index b5fcd60..fb196c9 100644 --- a/src/weighted_average.rs +++ b/src/weighted_average.rs @@ -1,7 +1,20 @@ use core; -/// Represent the weighted arithmetic mean and the weighted variance of a -/// sequence of numbers. +/// Estimate the weighted arithmetic mean and the weighted variance of a +/// sequence of numbers ("population"). +/// +/// This can be used to estimate the standard error of the weighted mean. +/// +/// +/// ## Example +/// +/// ``` +/// use average::WeightedAverage; +/// +/// let a: WeightedAverage = (1..6).zip(1..6) +/// .map(|(x, w)| (f64::from(x), f64::from(w))).collect(); +/// println!("The weighted average is {} ± {}.", a.mean(), a.error()); +/// ``` #[derive(Debug, Clone)] pub struct WeightedAverage { /// Sum of the weights. @@ -13,12 +26,12 @@ pub struct WeightedAverage { } impl WeightedAverage { - /// Create a new weighted average. + /// Create a new weighted average estimator. pub fn new() -> WeightedAverage { WeightedAverage { weight_sum: 0., avg: 0., v: 0. } } - /// Add a sample to the weighted sequence of which the average is calculated. + /// Add a weighted element sampled from the population. pub fn add(&mut self, sample: f64, weight: f64) { // This algorithm was suggested by West in 1979. // @@ -32,7 +45,7 @@ impl WeightedAverage { self.v += weight * (sample - prev_avg) * (sample - self.avg); } - /// Determine whether the sequence is empty. + /// Determine whether the samples are empty. pub fn is_empty(&self) -> bool { self.weight_sum == 0. && self.v == 0. && self.avg == 0. } @@ -42,15 +55,14 @@ impl WeightedAverage { self.weight_sum } - /// Estimate the weighted mean of the sequence. + /// Estimate the weighted mean of the population. pub fn mean(&self) -> f64 { self.avg } - /// Calculate the population variance of the weighted sequence. + /// Calculate the weighted population variance of the sample. /// - /// This assumes that the sequence consists of the entire population and the - /// weights represent *frequency*. + /// This is a biased estimator of the weighted variance of the population. pub fn population_variance(&self) -> f64 { if self.is_empty() { 0. @@ -59,10 +71,9 @@ impl WeightedAverage { } } - /// Calculate the unbiased sample variance of the weighted sequence. + /// Calculate the weighted sample variance. /// - /// This assumes that the sequence consists of samples of a larger - /// population and the weights represent *frequency*. + /// This is an unbiased estimator of the weighted variance of the population. /// /// Note that this will return 0 if the sum of the weights is <= 1. pub fn sample_variance(&self) -> f64 { @@ -73,10 +84,10 @@ impl WeightedAverage { } } - /// Estimate the standard error of the weighted mean of the sequence. + /// Estimate the standard error of the weighted mean of the population. /// /// Note that this will return 0 if the sum of the weights is 0. - /// For this estimator the sum of weights should be larger than 1. + /// For this estimator, the sum of weights should be larger than 1. /// /// This biased estimator uses the weighted variance and the sum of weights. /// It considers the weights as (noninteger) counts of how often the sample @@ -97,7 +108,10 @@ impl WeightedAverage { (variance / self.weight_sum).sqrt() } - /// Merge the weighted average of another sequence into this one. + /// Merge another sample into this one. + /// + /// + /// ## Example /// /// ``` /// use average::WeightedAverage; diff --git a/src/weighted_average2.rs b/src/weighted_average2.rs index d11f7ba..5ea71e2 100644 --- a/src/weighted_average2.rs +++ b/src/weighted_average2.rs @@ -2,27 +2,40 @@ use core; use conv::ApproxFrom; -/// Represent the weighted and unweighted arithmetic mean and the unweighted -/// variance of a sequence of numbers. +/// Estimate the weighted and unweighted arithmetic mean and the unweighted +/// variance of a sequence of numbers ("population"). +/// +/// This can be used to estimate the standard error of the weighted mean. +/// +/// +/// ## Example +/// +/// ``` +/// use average::WeightedAverage2 as WeightedAverage; +/// +/// let a: WeightedAverage = (1..6).zip(1..6) +/// .map(|(x, w)| (f64::from(x), f64::from(w))).collect(); +/// println!("The weighted average is {} ± {}.", a.weighted_mean(), a.error()); +/// ``` #[derive(Debug, Clone)] pub struct WeightedAverage { /// Sum of the weights. weight_sum: f64, /// Sum of the squares of the weights. weight_sum_sq: f64, - /// Weighted verage value. + /// Weighted average value. weighted_avg: f64, /// Number of samples. n: u64, - /// Unweighted verage value. + /// Unweighted average value. unweighted_avg: f64, /// Intermediate sum of squares for calculating the *unweighted* variance. v: f64, } impl WeightedAverage { - /// Create a new weighted average. + /// Create a new weighted and unweighted average estimator. pub fn new() -> WeightedAverage { WeightedAverage { weight_sum: 0., weight_sum_sq: 0., weighted_avg: 0., @@ -30,7 +43,7 @@ impl WeightedAverage { } } - /// Add a sample to the weighted sequence of which the average is calculated. + /// Add a weighted element sampled from the population. pub fn add(&mut self, sample: f64, weight: f64) { // The algorithm for the unweighted average was suggested by Welford in 1962. // The algorithm for the weighted average was suggested by West in 1979. @@ -51,7 +64,7 @@ impl WeightedAverage { self.v += delta * (sample - self.unweighted_avg); } - /// Determine whether the sequence is empty. + /// Determine whether the sample is empty. pub fn is_empty(&self) -> bool { self.n == 0 } @@ -89,9 +102,9 @@ impl WeightedAverage { self.weight_sum * self.weight_sum / self.weight_sum_sq } - /// Calculate the *unweighted* population variance of the sequence. + /// Calculate the *unweighted* population variance of the sample. /// - /// This assumes that the sequence consists of the entire population. + /// This is a biased estimator of the variance of the population. pub fn population_variance(&self) -> f64 { if self.n < 2 { return 0.; @@ -99,9 +112,9 @@ impl WeightedAverage { self.v / f64::approx_from(self.n).unwrap() } - /// Calculate the *unweighted*, unbiased sample variance of the sequence. + /// Calculate the *unweighted* sample variance. /// - /// This assumes that the sequence consists of samples of a larger population. + /// This is an unbiased estimator of the variance of the population. pub fn sample_variance(&self) -> f64 { if self.n < 2 { return 0.; @@ -109,7 +122,7 @@ impl WeightedAverage { self.v / f64::approx_from(self.n - 1).unwrap() } - /// Estimate the standard error of the weighted mean of the sequence. + /// Estimate the standard error of the *weighted* mean of the sequence. /// /// Returns 0 if the sum of weights is 0. /// @@ -126,14 +139,17 @@ impl WeightedAverage { (self.sample_variance() / effective_base).sqrt() } - /// Merge the weighted average of another sequence into this one. + /// Merge another sample into this one. + /// + /// + /// ## Example /// /// ``` /// use average::WeightedAverage2 as WeightedAverage; /// /// let weighted_sequence: &[(f64, f64)] = &[ /// (1., 0.1), (2., 0.2), (3., 0.3), (4., 0.4), (5., 0.5), - /// (6., 0.6), (7., 0.7), (8., 0.8), (9., 0.)]; + /// (6., 0.6), (7., 0.7), (8., 0.8), (9., 0.9)]; /// let (left, right) = weighted_sequence.split_at(3); /// let avg_total: WeightedAverage = weighted_sequence.iter().map(|&x| x).collect(); /// let mut avg_left: WeightedAverage = left.iter().map(|&x| x).collect();