Move add
and merge
to trait
This should make it possible to write more generic code.
This commit is contained in:
parent
d5b3fc80ab
commit
5d6d67bac9
@ -19,7 +19,7 @@
|
||||
//! ### Example
|
||||
//!
|
||||
//! ```
|
||||
//! use average::MeanWithError;
|
||||
//! use average::{MeanWithError, Estimate};
|
||||
//!
|
||||
//! let mut a: MeanWithError = (1..6).map(Into::into).collect();
|
||||
//! a.add(42.);
|
||||
|
@ -40,7 +40,7 @@ macro_rules! assert_almost_eq {
|
||||
/// # extern crate core;
|
||||
/// # #[macro_use] extern crate average;
|
||||
/// # fn main() {
|
||||
/// use average::{Min, Max};
|
||||
/// use average::{Min, Max, Estimate};
|
||||
///
|
||||
/// concatenate!(MinMax, [Min, min], [Max, max]);
|
||||
///
|
||||
@ -54,7 +54,7 @@ macro_rules! assert_almost_eq {
|
||||
/// The generated code looks roughly like this:
|
||||
///
|
||||
/// ```
|
||||
/// # use average::{Min, Max};
|
||||
/// # use average::{Min, Max, Estimate};
|
||||
/// #
|
||||
/// struct MinMax {
|
||||
/// min: Min,
|
||||
@ -88,7 +88,7 @@ macro_rules! assert_almost_eq {
|
||||
/// # extern crate core;
|
||||
/// # #[macro_use] extern crate average;
|
||||
/// # fn main() {
|
||||
/// use average::{Variance, Quantile};
|
||||
/// use average::{Variance, Quantile, Estimate};
|
||||
///
|
||||
/// concatenate!(Estimator,
|
||||
/// [Variance, variance, mean, sample_variance],
|
||||
|
125
src/minmax.rs
125
src/minmax.rs
@ -1,6 +1,7 @@
|
||||
use core;
|
||||
|
||||
use super::reduce::Reduce;
|
||||
use super::{Estimate, Merge};
|
||||
|
||||
/// Calculate the minimum of `a` and `b`.
|
||||
fn min(a: f64, b: f64) -> f64 {
|
||||
@ -43,38 +44,11 @@ impl Min {
|
||||
Min::from_value(::core::f64::INFINITY)
|
||||
}
|
||||
|
||||
/// Add an observation sampled from the population.
|
||||
#[inline]
|
||||
pub fn add(&mut self, x: f64) {
|
||||
self.r.add(x);
|
||||
}
|
||||
|
||||
/// Estimate the minium of the population.
|
||||
#[inline]
|
||||
pub fn min(&self) -> f64 {
|
||||
self.r.reduction()
|
||||
}
|
||||
|
||||
/// Merge another sample into this one.
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use average::Min;
|
||||
///
|
||||
/// let sequence: &[f64] = &[1., 2., 3., 4., 5., 6., 7., 8., 9.];
|
||||
/// let (left, right) = sequence.split_at(3);
|
||||
/// let min_total: Min = sequence.iter().map(|x| *x).collect();
|
||||
/// let mut min_left: Min = left.iter().map(|x| *x).collect();
|
||||
/// let min_right: Min = right.iter().map(|x| *x).collect();
|
||||
/// min_left.merge(&min_right);
|
||||
/// assert_eq!(min_total.min(), min_left.min());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn merge(&mut self, other: &Min) {
|
||||
self.r.merge(&other.r);
|
||||
}
|
||||
}
|
||||
|
||||
impl core::default::Default for Min {
|
||||
@ -85,6 +59,41 @@ impl core::default::Default for Min {
|
||||
|
||||
impl_from_iterator!(Min);
|
||||
|
||||
impl Estimate for Min {
|
||||
#[inline]
|
||||
fn add(&mut self, x: f64) {
|
||||
self.r.add(x);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn estimate(&self) -> f64 {
|
||||
self.min()
|
||||
}
|
||||
}
|
||||
|
||||
impl Merge for Min {
|
||||
/// Merge another sample into this one.
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use average::{Min, Merge};
|
||||
///
|
||||
/// let sequence: &[f64] = &[1., 2., 3., 4., 5., 6., 7., 8., 9.];
|
||||
/// let (left, right) = sequence.split_at(3);
|
||||
/// let min_total: Min = sequence.iter().map(|x| *x).collect();
|
||||
/// let mut min_left: Min = left.iter().map(|x| *x).collect();
|
||||
/// let min_right: Min = right.iter().map(|x| *x).collect();
|
||||
/// min_left.merge(&min_right);
|
||||
/// assert_eq!(min_total.min(), min_left.min());
|
||||
/// ```
|
||||
#[inline]
|
||||
fn merge(&mut self, other: &Min) {
|
||||
self.r.merge(&other.r);
|
||||
}
|
||||
}
|
||||
|
||||
/// Estimate the maximum of a sequence of numbers ("population").
|
||||
///
|
||||
///
|
||||
@ -116,38 +125,11 @@ impl Max {
|
||||
Max::from_value(::core::f64::NEG_INFINITY)
|
||||
}
|
||||
|
||||
/// Add an observation sampled from the population.
|
||||
#[inline]
|
||||
pub fn add(&mut self, x: f64) {
|
||||
self.r.add(x);
|
||||
}
|
||||
|
||||
/// Estimate the maxium of the population.
|
||||
#[inline]
|
||||
pub fn max(&self) -> f64 {
|
||||
self.r.reduction()
|
||||
}
|
||||
|
||||
/// Merge another sample into this one.
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use average::Max;
|
||||
///
|
||||
/// let sequence: &[f64] = &[1., 2., 3., 4., 5., 6., 7., 8., 9.];
|
||||
/// let (left, right) = sequence.split_at(3);
|
||||
/// let max_total: Max = sequence.iter().map(|x| *x).collect();
|
||||
/// let mut max_left: Max = left.iter().map(|x| *x).collect();
|
||||
/// let max_right: Max = right.iter().map(|x| *x).collect();
|
||||
/// max_left.merge(&max_right);
|
||||
/// assert_eq!(max_total.max(), max_left.max());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn merge(&mut self, other: &Max) {
|
||||
self.r.merge(&other.r);
|
||||
}
|
||||
}
|
||||
|
||||
impl core::default::Default for Max {
|
||||
@ -157,3 +139,38 @@ impl core::default::Default for Max {
|
||||
}
|
||||
|
||||
impl_from_iterator!(Max);
|
||||
|
||||
impl Estimate for Max {
|
||||
#[inline]
|
||||
fn add(&mut self, x: f64) {
|
||||
self.r.add(x);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn estimate(&self) -> f64 {
|
||||
self.max()
|
||||
}
|
||||
}
|
||||
|
||||
impl Merge for Max {
|
||||
/// Merge another sample into this one.
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use average::{Max, Merge};
|
||||
///
|
||||
/// let sequence: &[f64] = &[1., 2., 3., 4., 5., 6., 7., 8., 9.];
|
||||
/// let (left, right) = sequence.split_at(3);
|
||||
/// let max_total: Max = sequence.iter().map(|x| *x).collect();
|
||||
/// let mut max_left: Max = left.iter().map(|x| *x).collect();
|
||||
/// let max_right: Max = right.iter().map(|x| *x).collect();
|
||||
/// max_left.merge(&max_right);
|
||||
/// assert_eq!(max_total.max(), max_left.max());
|
||||
/// ```
|
||||
#[inline]
|
||||
fn merge(&mut self, other: &Max) {
|
||||
self.r.merge(&other.r);
|
||||
}
|
||||
}
|
||||
|
@ -20,15 +20,6 @@ impl Kurtosis {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an observation sampled from the population.
|
||||
#[inline]
|
||||
pub fn add(&mut self, x: f64) {
|
||||
let delta = x - self.mean();
|
||||
self.increment();
|
||||
let n = f64::approx_from(self.len()).unwrap();
|
||||
self.add_inner(delta, delta/n);
|
||||
}
|
||||
|
||||
/// Increment the sample size.
|
||||
///
|
||||
/// This does not update anything else.
|
||||
@ -114,9 +105,26 @@ impl Kurtosis {
|
||||
n * self.sum_4 / (self.avg.avg.sum_2 * self.avg.avg.sum_2) - 3.
|
||||
}
|
||||
|
||||
/// Merge another sample into this one.
|
||||
}
|
||||
|
||||
impl Estimate for Kurtosis {
|
||||
#[inline]
|
||||
pub fn merge(&mut self, other: &Kurtosis) {
|
||||
fn add(&mut self, x: f64) {
|
||||
let delta = x - self.mean();
|
||||
self.increment();
|
||||
let n = f64::approx_from(self.len()).unwrap();
|
||||
self.add_inner(delta, delta/n);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn estimate(&self) -> f64 {
|
||||
self.kurtosis()
|
||||
}
|
||||
}
|
||||
|
||||
impl Merge for Kurtosis {
|
||||
#[inline]
|
||||
fn merge(&mut self, other: &Kurtosis) {
|
||||
let len_self = f64::approx_from(self.len()).unwrap();
|
||||
let len_other = f64::approx_from(other.len()).unwrap();
|
||||
let len_total = len_self + len_other;
|
||||
|
@ -1,8 +1,3 @@
|
||||
use core;
|
||||
|
||||
use conv::ApproxFrom;
|
||||
|
||||
|
||||
/// Estimate the arithmetic mean of a sequence of numbers ("population").
|
||||
///
|
||||
///
|
||||
@ -29,15 +24,6 @@ impl Mean {
|
||||
Mean { avg: 0., n: 0 }
|
||||
}
|
||||
|
||||
/// Add an observation sampled from the population.
|
||||
#[inline]
|
||||
pub fn add(&mut self, sample: f64) {
|
||||
self.increment();
|
||||
let delta_n = (sample - self.avg)
|
||||
/ f64::approx_from(self.n).unwrap();
|
||||
self.add_inner(delta_n);
|
||||
}
|
||||
|
||||
/// Increment the sample size.
|
||||
///
|
||||
/// This does not update anything else.
|
||||
@ -80,13 +66,36 @@ impl Mean {
|
||||
self.n
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl core::default::Default for Mean {
|
||||
fn default() -> Mean {
|
||||
Mean::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Estimate for Mean {
|
||||
#[inline]
|
||||
fn add(&mut self, sample: f64) {
|
||||
self.increment();
|
||||
let delta_n = (sample - self.avg)
|
||||
/ f64::approx_from(self.n).unwrap();
|
||||
self.add_inner(delta_n);
|
||||
}
|
||||
|
||||
fn estimate(&self) -> f64 {
|
||||
self.mean()
|
||||
}
|
||||
}
|
||||
|
||||
impl Merge for Mean {
|
||||
/// Merge another sample into this one.
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use average::Mean;
|
||||
/// use average::{Mean, Merge};
|
||||
///
|
||||
/// let sequence: &[f64] = &[1., 2., 3., 4., 5., 6., 7., 8., 9.];
|
||||
/// let (left, right) = sequence.split_at(3);
|
||||
@ -97,7 +106,7 @@ impl Mean {
|
||||
/// assert_eq!(avg_total.mean(), avg_left.mean());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn merge(&mut self, other: &Mean) {
|
||||
fn merge(&mut self, other: &Mean) {
|
||||
// This algorithm was proposed by Chan et al. in 1979.
|
||||
//
|
||||
// See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance.
|
||||
@ -114,10 +123,4 @@ impl Mean {
|
||||
}
|
||||
}
|
||||
|
||||
impl core::default::Default for Mean {
|
||||
fn default() -> Mean {
|
||||
Mean::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_iterator!(Mean);
|
||||
|
@ -1,3 +1,9 @@
|
||||
use core;
|
||||
|
||||
use conv::ApproxFrom;
|
||||
|
||||
use super::{Estimate, Merge};
|
||||
|
||||
include!("mean.rs");
|
||||
include!("variance.rs");
|
||||
include!("skewness.rs");
|
||||
|
@ -20,15 +20,6 @@ impl Skewness {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an observation sampled from the population.
|
||||
#[inline]
|
||||
pub fn add(&mut self, x: f64) {
|
||||
let delta = x - self.mean();
|
||||
self.increment();
|
||||
let n = f64::approx_from(self.len()).unwrap();
|
||||
self.add_inner(delta, delta/n);
|
||||
}
|
||||
|
||||
/// Increment the sample size.
|
||||
///
|
||||
/// This does not update anything else.
|
||||
@ -107,10 +98,32 @@ impl Skewness {
|
||||
debug_assert_ne!(sum_2, 0.);
|
||||
n.sqrt() * self.sum_3 / (sum_2*sum_2*sum_2).sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
/// Merge another sample into this one.
|
||||
impl Default for Skewness {
|
||||
fn default() -> Skewness {
|
||||
Skewness::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Estimate for Skewness {
|
||||
#[inline]
|
||||
pub fn merge(&mut self, other: &Skewness) {
|
||||
fn add(&mut self, x: f64) {
|
||||
let delta = x - self.mean();
|
||||
self.increment();
|
||||
let n = f64::approx_from(self.len()).unwrap();
|
||||
self.add_inner(delta, delta/n);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn estimate(&self) -> f64 {
|
||||
self.skewness()
|
||||
}
|
||||
}
|
||||
|
||||
impl Merge for Skewness {
|
||||
#[inline]
|
||||
fn merge(&mut self, other: &Skewness) {
|
||||
let len_self = f64::approx_from(self.len()).unwrap();
|
||||
let len_other = f64::approx_from(other.len()).unwrap();
|
||||
let len_total = len_self + len_other;
|
||||
|
@ -27,15 +27,6 @@ impl Variance {
|
||||
Variance { avg: Mean::new(), sum_2: 0. }
|
||||
}
|
||||
|
||||
/// Add an observation sampled from the population.
|
||||
#[inline]
|
||||
pub fn add(&mut self, sample: f64) {
|
||||
self.increment();
|
||||
let delta_n = (sample - self.avg.mean())
|
||||
/ f64::approx_from(self.len()).unwrap();
|
||||
self.add_inner(delta_n);
|
||||
}
|
||||
|
||||
/// Increment the sample size.
|
||||
///
|
||||
/// This does not update anything else.
|
||||
@ -113,13 +104,37 @@ impl Variance {
|
||||
(self.sample_variance() / f64::approx_from(n).unwrap()).sqrt()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl core::default::Default for Variance {
|
||||
fn default() -> Variance {
|
||||
Variance::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Estimate for Variance {
|
||||
#[inline]
|
||||
fn add(&mut self, sample: f64) {
|
||||
self.increment();
|
||||
let delta_n = (sample - self.avg.mean())
|
||||
/ f64::approx_from(self.len()).unwrap();
|
||||
self.add_inner(delta_n);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn estimate(&self) -> f64 {
|
||||
self.population_variance()
|
||||
}
|
||||
}
|
||||
|
||||
impl Merge for Variance {
|
||||
/// Merge another sample into this one.
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use average::Variance;
|
||||
/// use average::{Variance, Merge};
|
||||
///
|
||||
/// let sequence: &[f64] = &[1., 2., 3., 4., 5., 6., 7., 8., 9.];
|
||||
/// let (left, right) = sequence.split_at(3);
|
||||
@ -131,7 +146,7 @@ impl Variance {
|
||||
/// assert_eq!(avg_total.sample_variance(), avg_left.sample_variance());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn merge(&mut self, other: &Variance) {
|
||||
fn merge(&mut self, other: &Variance) {
|
||||
// This algorithm was proposed by Chan et al. in 1979.
|
||||
//
|
||||
// See https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance.
|
||||
@ -144,10 +159,4 @@ impl Variance {
|
||||
}
|
||||
}
|
||||
|
||||
impl core::default::Default for Variance {
|
||||
fn default() -> Variance {
|
||||
Variance::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl_from_iterator!(Variance);
|
||||
|
119
src/quantile.rs
119
src/quantile.rs
@ -4,6 +4,8 @@ use core::cmp::min;
|
||||
use conv::{ApproxFrom, ConvAsUtil, ValueFrom};
|
||||
use quickersort::sort_floats;
|
||||
|
||||
use super::Estimate;
|
||||
|
||||
/// Estimate the p-quantile of a sequence of numbers ("population").
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Quantile {
|
||||
@ -38,62 +40,6 @@ impl Quantile {
|
||||
self.dm[2]
|
||||
}
|
||||
|
||||
/// Add an observation sampled from the population.
|
||||
#[inline]
|
||||
pub fn add(&mut self, x: f64) {
|
||||
// n[4] is the sample size.
|
||||
if self.n[4] < 5 {
|
||||
self.q[usize::value_from(self.n[4]).unwrap()] = x; // n[4] < 5
|
||||
self.n[4] += 1;
|
||||
if self.n[4] == 5 {
|
||||
sort_floats(&mut self.q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Find cell k.
|
||||
let mut k: usize;
|
||||
if x < self.q[0] {
|
||||
self.q[0] = x;
|
||||
k = 0;
|
||||
} else {
|
||||
k = 4;
|
||||
for i in 1..5 {
|
||||
if x < self.q[i] {
|
||||
k = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if self.q[4] < x {
|
||||
self.q[4] = x;
|
||||
}
|
||||
};
|
||||
|
||||
// Increment all positions greater than k.
|
||||
for i in k..5 {
|
||||
self.n[i] += 1;
|
||||
}
|
||||
for i in 0..5 {
|
||||
self.m[i] += self.dm[i];
|
||||
}
|
||||
|
||||
// Adjust height of markers.
|
||||
for i in 1..4 {
|
||||
let d: f64 = self.m[i] - f64::approx_from(self.n[i]).unwrap();
|
||||
if d >= 1. && self.n[i + 1] - self.n[i] > 1 ||
|
||||
d <= -1. && self.n[i - 1] - self.n[i] < -1 {
|
||||
let d = d.signum();
|
||||
let q_new = self.parabolic(i, d);
|
||||
if self.q[i - 1] < q_new && q_new < self.q[i + 1] {
|
||||
self.q[i] = q_new;
|
||||
} else {
|
||||
self.q[i] = self.linear(i, d);
|
||||
}
|
||||
self.n[i] += d.approx().unwrap(); // d == +-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parabolic prediction for marker height.
|
||||
#[inline]
|
||||
fn parabolic(&self, i: usize, d: f64) -> f64 {
|
||||
@ -171,6 +117,67 @@ impl core::default::Default for Quantile {
|
||||
}
|
||||
}
|
||||
|
||||
impl Estimate for Quantile {
|
||||
#[inline]
|
||||
fn add(&mut self, x: f64) {
|
||||
// n[4] is the sample size.
|
||||
if self.n[4] < 5 {
|
||||
self.q[usize::value_from(self.n[4]).unwrap()] = x; // n[4] < 5
|
||||
self.n[4] += 1;
|
||||
if self.n[4] == 5 {
|
||||
sort_floats(&mut self.q);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Find cell k.
|
||||
let mut k: usize;
|
||||
if x < self.q[0] {
|
||||
self.q[0] = x;
|
||||
k = 0;
|
||||
} else {
|
||||
k = 4;
|
||||
for i in 1..5 {
|
||||
if x < self.q[i] {
|
||||
k = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if self.q[4] < x {
|
||||
self.q[4] = x;
|
||||
}
|
||||
};
|
||||
|
||||
// Increment all positions greater than k.
|
||||
for i in k..5 {
|
||||
self.n[i] += 1;
|
||||
}
|
||||
for i in 0..5 {
|
||||
self.m[i] += self.dm[i];
|
||||
}
|
||||
|
||||
// Adjust height of markers.
|
||||
for i in 1..4 {
|
||||
let d: f64 = self.m[i] - f64::approx_from(self.n[i]).unwrap();
|
||||
if d >= 1. && self.n[i + 1] - self.n[i] > 1 ||
|
||||
d <= -1. && self.n[i - 1] - self.n[i] < -1 {
|
||||
let d = d.signum();
|
||||
let q_new = self.parabolic(i, d);
|
||||
if self.q[i - 1] < q_new && q_new < self.q[i + 1] {
|
||||
self.q[i] = q_new;
|
||||
} else {
|
||||
self.q[i] = self.linear(i, d);
|
||||
}
|
||||
self.n[i] += d.approx().unwrap(); // d == +-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn estimate(&self) -> f64 {
|
||||
self.quantile()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reference() {
|
||||
let observations = [
|
||||
|
@ -1,3 +1,5 @@
|
||||
use super::{Estimate, Merge};
|
||||
|
||||
/// Estimate the reduction of a sequence of numbers ("population").
|
||||
///
|
||||
/// The reduction is a given function `Fn(f64, f64) -> f64`.
|
||||
@ -10,30 +12,41 @@ pub struct Reduce<F> {
|
||||
reduce: F,
|
||||
}
|
||||
|
||||
impl<F> Reduce<F>
|
||||
where F: Fn(f64, f64) -> f64
|
||||
{
|
||||
impl<F> Reduce<F> {
|
||||
/// Create a new reduction estimator given an initial value and a reduction.
|
||||
#[inline]
|
||||
pub fn from_value_and_fn(x: f64, f: F) -> Reduce<F> {
|
||||
Reduce { x: x, reduce: f }
|
||||
}
|
||||
|
||||
/// Add an element sampled from the population.
|
||||
#[inline]
|
||||
pub fn add(&mut self, x: f64) {
|
||||
self.x = (self.reduce)(self.x, x);
|
||||
}
|
||||
|
||||
/// Estimate the reduction of the population.
|
||||
#[inline]
|
||||
pub fn reduction(&self) -> f64 {
|
||||
self.x
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<F> Estimate for Reduce<F>
|
||||
where F: Fn(f64, f64) -> f64,
|
||||
{
|
||||
#[inline]
|
||||
fn add(&mut self, x: f64) {
|
||||
self.x = (self.reduce)(self.x, x);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn estimate(&self) -> f64 {
|
||||
self.reduction()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Merge for Reduce<F>
|
||||
where F: Fn(f64, f64) -> f64,
|
||||
{
|
||||
/// Merge another sample into this one.
|
||||
#[inline]
|
||||
pub fn merge(&mut self, other: &Reduce<F>) {
|
||||
fn merge(&mut self, other: &Reduce<F>) {
|
||||
self.add(other.x);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use core;
|
||||
|
||||
use super::MeanWithError;
|
||||
use super::{MeanWithError, Estimate, Merge};
|
||||
|
||||
|
||||
/// Estimate the weighted and unweighted arithmetic mean of a sequence of
|
||||
@ -32,7 +32,7 @@ impl WeightedMean {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a weighted observation sampled from the population.
|
||||
/// Add an observation sampled from the population.
|
||||
#[inline]
|
||||
pub fn add(&mut self, sample: f64, weight: f64) {
|
||||
// The algorithm for the unweighted mean was suggested by Welford in 1962.
|
||||
@ -70,33 +70,6 @@ impl WeightedMean {
|
||||
pub fn mean(&self) -> f64 {
|
||||
self.weighted_avg
|
||||
}
|
||||
|
||||
/// Merge another sample into this one.
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use average::WeightedMean;
|
||||
///
|
||||
/// 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.9)];
|
||||
/// let (left, right) = weighted_sequence.split_at(3);
|
||||
/// let avg_total: WeightedMean = weighted_sequence.iter().map(|&x| x).collect();
|
||||
/// let mut avg_left: WeightedMean = left.iter().map(|&x| x).collect();
|
||||
/// let avg_right: WeightedMean = right.iter().map(|&x| x).collect();
|
||||
/// avg_left.merge(&avg_right);
|
||||
/// assert!((avg_total.mean() - avg_left.mean()).abs() < 1e-15);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn merge(&mut self, other: &WeightedMean) {
|
||||
let total_weight_sum = self.weight_sum + other.weight_sum;
|
||||
self.weighted_avg = (self.weight_sum * self.weighted_avg
|
||||
+ other.weight_sum * other.weighted_avg)
|
||||
/ total_weight_sum;
|
||||
self.weight_sum = total_weight_sum;
|
||||
}
|
||||
}
|
||||
|
||||
impl core::default::Default for WeightedMean {
|
||||
@ -117,6 +90,35 @@ impl core::iter::FromIterator<(f64, f64)> for WeightedMean {
|
||||
}
|
||||
}
|
||||
|
||||
impl Merge for WeightedMean {
|
||||
/// Merge another sample into this one.
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use average::{WeightedMean, Merge};
|
||||
///
|
||||
/// 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.9)];
|
||||
/// let (left, right) = weighted_sequence.split_at(3);
|
||||
/// let avg_total: WeightedMean = weighted_sequence.iter().map(|&x| x).collect();
|
||||
/// let mut avg_left: WeightedMean = left.iter().map(|&x| x).collect();
|
||||
/// let avg_right: WeightedMean = right.iter().map(|&x| x).collect();
|
||||
/// avg_left.merge(&avg_right);
|
||||
/// assert!((avg_total.mean() - avg_left.mean()).abs() < 1e-15);
|
||||
/// ```
|
||||
#[inline]
|
||||
fn merge(&mut self, other: &WeightedMean) {
|
||||
let total_weight_sum = self.weight_sum + other.weight_sum;
|
||||
self.weighted_avg = (self.weight_sum * self.weighted_avg
|
||||
+ other.weight_sum * other.weighted_avg)
|
||||
/ total_weight_sum;
|
||||
self.weight_sum = total_weight_sum;
|
||||
}
|
||||
}
|
||||
|
||||
/// Estimate the weighted and unweighted arithmetic mean and the unweighted
|
||||
/// variance of a sequence of numbers ("population").
|
||||
///
|
||||
@ -153,7 +155,7 @@ impl WeightedMeanWithError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a weighted observation sampled from the population.
|
||||
/// Add an observation sampled from the population.
|
||||
#[inline]
|
||||
pub fn add(&mut self, sample: f64, weight: f64) {
|
||||
// The algorithm for the unweighted mean was suggested by Welford in 1962.
|
||||
@ -225,6 +227,7 @@ impl WeightedMeanWithError {
|
||||
/// Calculate the *unweighted* population variance of the sample.
|
||||
///
|
||||
/// This is a biased estimator of the variance of the population.
|
||||
#[inline]
|
||||
pub fn population_variance(&self) -> f64 {
|
||||
self.unweighted_avg.population_variance()
|
||||
}
|
||||
@ -232,6 +235,7 @@ impl WeightedMeanWithError {
|
||||
/// Calculate the *unweighted* sample variance.
|
||||
///
|
||||
/// This is an unbiased estimator of the variance of the population.
|
||||
#[inline]
|
||||
pub fn sample_variance(&self) -> f64 {
|
||||
self.unweighted_avg.sample_variance()
|
||||
}
|
||||
@ -242,6 +246,7 @@ impl WeightedMeanWithError {
|
||||
///
|
||||
/// This unbiased estimator assumes that the samples were independently
|
||||
/// drawn from the same population with constant variance.
|
||||
#[inline]
|
||||
pub fn error(&self) -> f64 {
|
||||
// This uses the same estimate as WinCross, which should provide better
|
||||
// results than the ones used by SPSS or Mentor.
|
||||
@ -254,14 +259,16 @@ impl WeightedMeanWithError {
|
||||
let inv_effective_len = self.weight_sum_sq / (weight_sum * weight_sum);
|
||||
(self.sample_variance() * inv_effective_len).sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
impl Merge for WeightedMeanWithError {
|
||||
/// Merge another sample into this one.
|
||||
///
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use average::WeightedMeanWithError;
|
||||
/// use average::{WeightedMeanWithError, Merge};
|
||||
///
|
||||
/// let weighted_sequence: &[(f64, f64)] = &[
|
||||
/// (1., 0.1), (2., 0.2), (3., 0.3), (4., 0.4), (5., 0.5),
|
||||
@ -274,7 +281,8 @@ impl WeightedMeanWithError {
|
||||
/// assert!((avg_total.weighted_mean() - avg_left.weighted_mean()).abs() < 1e-15);
|
||||
/// assert!((avg_total.error() - avg_left.error()).abs() < 1e-15);
|
||||
/// ```
|
||||
pub fn merge(&mut self, other: &WeightedMeanWithError) {
|
||||
#[inline]
|
||||
fn merge(&mut self, other: &WeightedMeanWithError) {
|
||||
self.weight_sum_sq += other.weight_sum_sq;
|
||||
self.weighted_avg.merge(&other.weighted_avg);
|
||||
self.unweighted_avg.merge(&other.unweighted_avg);
|
||||
|
@ -6,7 +6,7 @@ extern crate core;
|
||||
|
||||
use core::iter::Iterator;
|
||||
|
||||
use average::Kurtosis;
|
||||
use average::{Kurtosis, Estimate, Merge};
|
||||
|
||||
#[test]
|
||||
fn trivial() {
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
extern crate core;
|
||||
|
||||
use average::Estimate;
|
||||
|
||||
#[test]
|
||||
fn concatenate_simple() {
|
||||
use average::{Min, Max};
|
||||
|
@ -6,7 +6,7 @@ extern crate core;
|
||||
|
||||
use core::iter::Iterator;
|
||||
|
||||
use average::Max;
|
||||
use average::{Max, Estimate, Merge};
|
||||
|
||||
#[test]
|
||||
fn trivial() {
|
||||
|
@ -6,7 +6,7 @@ extern crate core;
|
||||
|
||||
use core::iter::Iterator;
|
||||
|
||||
use average::MeanWithError;
|
||||
use average::{MeanWithError, Estimate, Merge};
|
||||
|
||||
#[test]
|
||||
fn trivial() {
|
||||
|
@ -6,7 +6,7 @@ extern crate core;
|
||||
|
||||
use core::iter::Iterator;
|
||||
|
||||
use average::Min;
|
||||
use average::{Min, Estimate, Merge};
|
||||
|
||||
#[test]
|
||||
fn trivial() {
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
extern crate rand;
|
||||
|
||||
use average::Kurtosis;
|
||||
use average::{Kurtosis, Estimate};
|
||||
|
||||
#[test]
|
||||
fn normal_distribution() {
|
||||
|
@ -6,7 +6,7 @@ extern crate core;
|
||||
|
||||
use core::iter::Iterator;
|
||||
|
||||
use average::Skewness;
|
||||
use average::{Skewness, Estimate, Merge};
|
||||
|
||||
#[test]
|
||||
fn trivial() {
|
||||
|
@ -6,7 +6,7 @@ extern crate core;
|
||||
|
||||
use core::iter::Iterator;
|
||||
|
||||
use average::WeightedMeanWithError;
|
||||
use average::{WeightedMeanWithError, Merge};
|
||||
|
||||
#[test]
|
||||
fn trivial() {
|
||||
|
Loading…
Reference in New Issue
Block a user