Improve concatenate to support several statistics from one estimator

This commit is contained in:
Vinzent Steinberg 2017-06-21 19:29:25 +02:00
parent 2b960310ce
commit f698e4dc50

View File

@ -17,17 +17,20 @@ macro_rules! assert_almost_eq {
/// Concatenate several iterative estimators into one. /// Concatenate several iterative estimators into one.
/// ///
/// `$name` is the name of the new type. `$statistic` is the name of a statistic /// `$name` is the name of the new struct. `$statistic` is the name of a
/// and must exist as a method of the corresponding type `$estimator`. /// statistic and must exist as a method of the corresponding type `$estimator`.
/// `$estimator` must have an `add` method for adding new observations to the /// `$estimator` must have an `add` method for adding new observations to the
/// sample (taking an `f64` as an argument). It must also implement `Default`. /// sample (taking an `f64` as an argument). It must also implement `Default`.
/// ///
/// If the short syntax is used, the fields will be named `$statistic`. Use the
/// long syntax and `$field` to give them explicit names. The long syntax also
/// supports calculating several statistics from one estimator.
///
/// For moments, only an estimator for the highest moment should be used and /// 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 /// reused for the lower moments (see the example below).
/// and has to be done by hand.
/// ///
/// ///
/// # Example /// # Examples
/// ///
/// ``` /// ```
/// # extern crate core; /// # extern crate core;
@ -35,7 +38,7 @@ macro_rules! assert_almost_eq {
/// # fn main() { /// # fn main() {
/// use average::{Min, Max}; /// use average::{Min, Max};
/// ///
/// concatenate!(MinMax, min, Min, max, Max); /// concatenate!(MinMax, [Min, min], [Max, max]);
/// ///
/// let s: MinMax = (1..6).map(Into::into).collect(); /// let s: MinMax = (1..6).map(Into::into).collect();
/// ///
@ -73,12 +76,30 @@ macro_rules! assert_almost_eq {
/// } /// }
/// } /// }
/// ``` /// ```
///
/// If you want to calculate the mean, variance and the median in one pass, you
/// can do the following:
///
/// ```
/// # extern crate core;
/// # #[macro_use] extern crate average;
/// # fn main() {
/// use average::{Variance, Quantile};
///
/// concatenate!(Estimator,
/// [Variance, variance, mean, sample_variance],
/// [Quantile, quantile, quantile]);
/// # }
/// ```
#[macro_export] #[macro_export]
macro_rules! concatenate { macro_rules! concatenate {
( $name:ident, $($statistic:ident, $estimator:ident),* ) => { ( $name:ident, $([$estimator:ident, $statistic:ident]),+ ) => {
concatenate!( $name, $([$estimator, $statistic, $statistic]),* )
};
( $name:ident, $( [$estimator:ident, $field:ident, $($statistic:ident),+] ),+ ) => {
struct $name { struct $name {
$( $(
$statistic: $estimator, $field: $estimator,
)* )*
} }
@ -86,22 +107,22 @@ macro_rules! concatenate {
pub fn new() -> $name { pub fn new() -> $name {
$name { $name {
$( $(
$statistic: ::core::default::Default::default(), $field: ::core::default::Default::default(),
)* )*
} }
} }
pub fn add(&mut self, x: f64) { pub fn add(&mut self, x: f64) {
$( $(
self.$statistic.add(x); self.$field.add(x);
)* )*
} }
$( $( $(
pub fn $statistic(&self) -> f64 { pub fn $statistic(&self) -> f64 {
self.$statistic.$statistic() self.$field.$statistic()
} }
)* )* )*
} }
impl Default for $name { impl Default for $name {