r2rs-nmath 0.1.1

Statistics programming for Rust based on R's nmath package
Documentation
mod d;
mod p;
mod q;
mod r;

use strafe_type::{LogProbability64, Positive64, Probability64, Real64};

pub(crate) use self::{d::*, p::*, q::*, r::*};
use crate::traits::{Distribution, RNG};

/// # The Log Normal Distribution
///
/// ## Description
/// Density, distribution function, quantile function and random generation for the log normal
/// distribution whose logarithm has mean equal to meanlog and standard deviation equal to sdlog.
///
/// ## Arguments
///
/// * meanlog, sdlog: mean and standard deviation of the distribution on the log scale with default
/// values of 0 and 1 respectively.
///
/// ## Details
///
/// The log normal distribution has density
///
/// $ f(x) = 1/(\sqrt(2 \pi) \sigma x) e^-((log x - \mu)^2 / (2 \mu^2)) $
///
/// where $ \mu $ and $ \sigma $ are the mean and standard deviation of the logarithm. The mean is
/// $ E(X) = exp(\mu + 1/2 \sigma^2) $, the median is $ med(X) = exp(\mu) $, and the variance
/// $ Var(X) = exp(2*\mu + \sigma^2)*(exp(\sigma^2) - 1) $ and hence the coefficient of variation
/// is $ \sqrt(exp(\sigma^2) - 1) $ which is approximately $ \sigma $ when that is small (e.g.,
/// $ \sigma < 1/2 $).
///
/// ## Density Plot
///
/// ```rust
/// # use r2rs_base::traits::StatisticalSlice;
/// # use r2rs_nmath::{distribution::LogNormalBuilder, traits::Distribution};
/// # use strafe_plot::prelude::{IntoDrawingArea, Line, Plot, PlotOptions, SVGBackend, BLACK};
/// # use strafe_type::FloatConstraint;
/// let lnorm = LogNormalBuilder::new().build();
/// let x = <[f64]>::sequence(-1.0, 8.0, 1000);
/// let y = x
///     .iter()
///     .map(|x| lnorm.density(x).unwrap())
///     .collect::<Vec<_>>();
///
/// let root = SVGBackend::new("density.svg", (1024, 768)).into_drawing_area();
/// Plot::new()
///     .with_options(PlotOptions {
///         x_axis_label: "x".to_string(),
///         y_axis_label: "density".to_string(),
///         ..Default::default()
///     })
///     .with_plottable(Line {
///         x,
///         y,
///         color: BLACK,
///         ..Default::default()
///     })
///     .plot(&root)
///     .unwrap();
/// # use std::fs::rename;
/// #     drop(root);
/// #     rename(
/// #             format!("density.svg"),
/// #             format!("src/distribution/lnorm/doctest_out/density.svg"),
/// #     )
/// #     .unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = embed_doc_image::embed_image!("density", "src/distribution/lnorm/doctest_out/density.svg")))]
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = "![Density][density]"))]
///
/// ## Note
///
/// The cumulative hazard $ H(t) = - \log(1 - F(t)) $ is
/// $ -plnorm(t, r, lower = FALSE, log = TRUE) $.
///
/// ## Source
///
/// dlnorm is calculated from the definition (in ‘Details’).
/// \[pqr\]lnorm are based on the relationship to the normal.
///
/// Consequently, they model a single point mass at exp(meanlog) for the boundary case sdlog = 0.
///
/// ## References
///
/// Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988) The New S Language. Wadsworth &
/// Brooks/Cole.
///
/// Johnson, N. L., Kotz, S. and Balakrishnan, N. (1995) Continuous Univariate Distributions,
/// volume 1, chapter 14. Wiley, New York.
///
/// ## See Also
///
/// Distributions for other standard distributions, including dnorm for the normal distribution.
///
/// ## Examples
///
/// ```rust
/// # use r2rs_nmath::{
/// #     distribution::{LogNormalBuilder, NormalBuilder},
/// #     traits::Distribution,
/// # };
/// let norm = NormalBuilder::new().build();
/// println!("{}", norm.density(0));
/// let lnorm = LogNormalBuilder::new().build();
/// println!("{}", lnorm.density(1));
/// # use std::{fs::File, io::Write};
/// # let mut f = File::create("src/distribution/lnorm/doctest_out/dens.md").unwrap();
/// # writeln!(f, "```output").unwrap();
/// # writeln!(f, "{}", norm.density(0)).unwrap();
/// # writeln!(f, "{}", lnorm.density(1)).unwrap();
/// # writeln!(f, "```").unwrap();
/// ```
#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/dens.md")))]
pub struct LogNormal {
    mean_log: Real64,
    standard_deviation_log: Positive64,
}

impl Distribution for LogNormal {
    fn density<R: Into<Real64>>(&self, x: R) -> Real64 {
        dlnorm(x, self.mean_log, self.standard_deviation_log, false)
    }

    fn log_density<R: Into<Real64>>(&self, x: R) -> Real64 {
        dlnorm(x, self.mean_log, self.standard_deviation_log, true)
    }

    fn probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> Probability64 {
        plnorm(q, self.mean_log, self.standard_deviation_log, lower_tail)
    }

    fn log_probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> LogProbability64 {
        log_plnorm(q, self.mean_log, self.standard_deviation_log, lower_tail)
    }

    fn quantile<P: Into<Probability64>>(&self, p: P, lower_tail: bool) -> Real64 {
        qlnorm(p, self.mean_log, self.standard_deviation_log, lower_tail)
    }

    fn log_quantile<LP: Into<LogProbability64>>(&self, p: LP, lower_tail: bool) -> Real64 {
        log_qlnorm(p, self.mean_log, self.standard_deviation_log, lower_tail)
    }

    fn random_sample<R: RNG>(&self, rng: &mut R) -> Real64 {
        rlnorm(self.mean_log, self.standard_deviation_log, rng)
    }
}

pub struct LogNormalBuilder {
    mean_log: Option<Real64>,
    standard_deviation_log: Option<Positive64>,
}

impl LogNormalBuilder {
    pub fn new() -> Self {
        Self {
            mean_log: None,
            standard_deviation_log: None,
        }
    }

    pub fn with_mean_log<R: Into<Real64>>(&mut self, mean_log: R) -> &mut Self {
        self.mean_log = Some(mean_log.into());
        self
    }

    pub fn with_standard_deviation_log<P: Into<Positive64>>(
        &mut self,
        standard_deviation_log: P,
    ) -> &mut Self {
        self.standard_deviation_log = Some(standard_deviation_log.into());
        self
    }

    pub fn build(&self) -> LogNormal {
        let mean_log = self.mean_log.unwrap_or(0.0.into());
        let standard_deviation_log = self.standard_deviation_log.unwrap_or(1.0.into());

        LogNormal {
            mean_log,
            standard_deviation_log,
        }
    }
}

#[cfg(test)]
mod tests;

#[cfg(all(test, feature = "enable_proptest"))]
mod proptests;

#[cfg(all(test, feature = "enable_covtest"))]
mod covtests;