Macro for concatenating iterative estimators
This commit is contained in:
parent
0eed9d06db
commit
01157da831
65
src/lib.rs
65
src/lib.rs
@ -36,6 +36,20 @@
|
||||
//! * Quantiles ([`Quantile`]).
|
||||
//! * Minimum ([`Min`]) and maximum ([`Max`]).
|
||||
//!
|
||||
//! ## Estimating several statistics at once
|
||||
//!
|
||||
//! The estimators are designed to have minimal state. The recommended way to
|
||||
//! calculate several of them at once is to create a struct with all the
|
||||
//! estimators you need. You can then implement `add` for your struct by
|
||||
//! forwarding to the underlying estimators. Everything is inlined, so there
|
||||
//! should be no overhead.
|
||||
//!
|
||||
//! You can avoid the boilerplate code by using the [`concatenate`] macro.
|
||||
//!
|
||||
//! Note that calculating moments requires calculating the lower moments, so you
|
||||
//! only need to include the highest moment in your struct.
|
||||
//!
|
||||
//!
|
||||
//! [`Mean`]: ./struct.Mean.html
|
||||
//! [`MeanWithError`]: ./type.MeanWithError.html
|
||||
//! [`WeightedMean`]: ./struct.WeightedMean.html
|
||||
@ -46,56 +60,7 @@
|
||||
//! [`Quantile`]: ./struct.Quantile.html
|
||||
//! [`Min`]: ./struct.Min.html
|
||||
//! [`Max`]: ./struct.Max.html
|
||||
//!
|
||||
//!
|
||||
//! ## Estimating several statistics at once
|
||||
//!
|
||||
//! The estimators are designed to have minimal state. The recommended way to
|
||||
//! calculate several of them at once is to create a struct with all the
|
||||
//! estimators you need. You can then implement `add` for your struct by
|
||||
//! forwarding to the underlying estimators.
|
||||
//!
|
||||
//! Note that calculating moments requires calculating the lower moments, so you
|
||||
//! only need to include the highest moment in your struct.
|
||||
//!
|
||||
//!
|
||||
//! ### Example
|
||||
//!
|
||||
//! ```
|
||||
//! use average::{Min, Max};
|
||||
//!
|
||||
//! struct MinMax {
|
||||
//! min: Min,
|
||||
//! max: Max,
|
||||
//! }
|
||||
//!
|
||||
//! impl MinMax {
|
||||
//! pub fn new() -> MinMax {
|
||||
//! MinMax { min: Min::new(), max: Max::new() }
|
||||
//! }
|
||||
//!
|
||||
//! pub fn add(&mut self, x: f64) {
|
||||
//! self.min.add(x);
|
||||
//! self.max.add(x);
|
||||
//! }
|
||||
//!
|
||||
//! pub fn min(&self) -> f64 {
|
||||
//! self.min.min()
|
||||
//! }
|
||||
//!
|
||||
//! pub fn max(&self) -> f64 {
|
||||
//! self.max.max()
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! let mut s = MinMax::new();
|
||||
//! for i in 1..6 {
|
||||
//! s.add(i as f64);
|
||||
//! }
|
||||
//!
|
||||
//! assert_eq!(s.min(), 1.0);
|
||||
//! assert_eq!(s.max(), 5.0);
|
||||
//! ```
|
||||
//! [`concatenate`]: ./macro.concatenate.html
|
||||
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp, map_clone))]
|
||||
|
||||
|
109
src/macros.rs
109
src/macros.rs
@ -14,3 +14,112 @@ macro_rules! assert_almost_eq {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// Concatenate several iterative estimators into one.
|
||||
///
|
||||
/// `$name` is the name of the new type. `$statistic` is the name of a statistic
|
||||
/// and must exist as a method of the corresponding type `$estimator`.
|
||||
/// `$estimator` must have an `add` method for adding new observations to the
|
||||
/// sample (taking an `f64` as an argument). It must also implement `Default`.
|
||||
///
|
||||
/// For moments, only an estimator for the highest moment should be used and
|
||||
/// reused for the lower moments. This is currently not supported by this macro
|
||||
/// and has to be done by hand.
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate core;
|
||||
/// # #[macro_use] extern crate average;
|
||||
/// # fn main() {
|
||||
/// use average::{Min, Max};
|
||||
///
|
||||
/// concatenate!(MinMax, min, Min, max, Max);
|
||||
///
|
||||
/// let s: MinMax = (1..6).map(Into::into).collect();
|
||||
///
|
||||
/// assert_eq!(s.min(), 1.0);
|
||||
/// assert_eq!(s.max(), 5.0);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// The generated code looks roughly like this:
|
||||
///
|
||||
/// ```
|
||||
/// # use average::{Min, Max};
|
||||
/// #
|
||||
/// struct MinMax {
|
||||
/// min: Min,
|
||||
/// max: Max,
|
||||
/// }
|
||||
///
|
||||
/// impl MinMax {
|
||||
/// pub fn new() -> MinMax {
|
||||
/// MinMax { min: Min::default(), max: Max::default() }
|
||||
/// }
|
||||
///
|
||||
/// pub fn add(&mut self, x: f64) {
|
||||
/// self.min.add(x);
|
||||
/// self.max.add(x);
|
||||
/// }
|
||||
///
|
||||
/// pub fn min(&self) -> f64 {
|
||||
/// self.min.min()
|
||||
/// }
|
||||
///
|
||||
/// pub fn max(&self) -> f64 {
|
||||
/// self.max.max()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! concatenate {
|
||||
( $name:ident, $($statistic:ident, $estimator:ident),* ) => {
|
||||
struct $name {
|
||||
$(
|
||||
$statistic: $estimator,
|
||||
)*
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn new() -> $name {
|
||||
$name {
|
||||
$(
|
||||
$statistic: ::core::default::Default::default(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, x: f64) {
|
||||
$(
|
||||
self.$statistic.add(x);
|
||||
)*
|
||||
}
|
||||
|
||||
$(
|
||||
pub fn $statistic(&self) -> f64 {
|
||||
self.$statistic.$statistic()
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
impl Default for $name {
|
||||
fn default() -> $name {
|
||||
$name::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::iter::FromIterator<f64> for $name {
|
||||
fn from_iter<T>(iter: T) -> $name
|
||||
where T: IntoIterator<Item=f64>
|
||||
{
|
||||
let mut e = $name::new();
|
||||
for i in iter {
|
||||
e.add(i);
|
||||
}
|
||||
e
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user