r2rs_nmath/distribution/lnorm/mod.rs
1mod d;
2mod p;
3mod q;
4mod r;
5
6use strafe_type::{LogProbability64, Positive64, Probability64, Real64};
7
8pub(crate) use self::{d::*, p::*, q::*, r::*};
9use crate::traits::{Distribution, RNG};
10
11/// # The Log Normal Distribution
12///
13/// ## Description
14/// Density, distribution function, quantile function and random generation for the log normal
15/// distribution whose logarithm has mean equal to meanlog and standard deviation equal to sdlog.
16///
17/// ## Arguments
18///
19/// * meanlog, sdlog: mean and standard deviation of the distribution on the log scale with default
20/// values of 0 and 1 respectively.
21///
22/// ## Details
23///
24/// The log normal distribution has density
25///
26/// $ f(x) = 1/(\sqrt(2 \pi) \sigma x) e^-((log x - \mu)^2 / (2 \mu^2)) $
27///
28/// where $ \mu $ and $ \sigma $ are the mean and standard deviation of the logarithm. The mean is
29/// $ E(X) = exp(\mu + 1/2 \sigma^2) $, the median is $ med(X) = exp(\mu) $, and the variance
30/// $ Var(X) = exp(2*\mu + \sigma^2)*(exp(\sigma^2) - 1) $ and hence the coefficient of variation
31/// is $ \sqrt(exp(\sigma^2) - 1) $ which is approximately $ \sigma $ when that is small (e.g.,
32/// $ \sigma < 1/2 $).
33///
34/// ## Density Plot
35///
36/// ```rust
37/// # use r2rs_base::traits::StatisticalSlice;
38/// # use r2rs_nmath::{distribution::LogNormalBuilder, traits::Distribution};
39/// # use strafe_plot::prelude::{IntoDrawingArea, Line, Plot, PlotOptions, SVGBackend, BLACK};
40/// # use strafe_type::FloatConstraint;
41/// let lnorm = LogNormalBuilder::new().build();
42/// let x = <[f64]>::sequence(-1.0, 8.0, 1000);
43/// let y = x
44/// .iter()
45/// .map(|x| lnorm.density(x).unwrap())
46/// .collect::<Vec<_>>();
47///
48/// let root = SVGBackend::new("density.svg", (1024, 768)).into_drawing_area();
49/// Plot::new()
50/// .with_options(PlotOptions {
51/// x_axis_label: "x".to_string(),
52/// y_axis_label: "density".to_string(),
53/// ..Default::default()
54/// })
55/// .with_plottable(Line {
56/// x,
57/// y,
58/// color: BLACK,
59/// ..Default::default()
60/// })
61/// .plot(&root)
62/// .unwrap();
63/// # use std::fs::rename;
64/// # drop(root);
65/// # rename(
66/// # format!("density.svg"),
67/// # format!("src/distribution/lnorm/doctest_out/density.svg"),
68/// # )
69/// # .unwrap();
70/// ```
71#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = embed_doc_image::embed_image!("density", "src/distribution/lnorm/doctest_out/density.svg")))]
72#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = "![Density][density]"))]
73///
74/// ## Note
75///
76/// The cumulative hazard $ H(t) = - \log(1 - F(t)) $ is
77/// $ -plnorm(t, r, lower = FALSE, log = TRUE) $.
78///
79/// ## Source
80///
81/// dlnorm is calculated from the definition (in ‘Details’).
82/// \[pqr\]lnorm are based on the relationship to the normal.
83///
84/// Consequently, they model a single point mass at exp(meanlog) for the boundary case sdlog = 0.
85///
86/// ## References
87///
88/// Becker, R. A., Chambers, J. M. and Wilks, A. R. (1988) The New S Language. Wadsworth &
89/// Brooks/Cole.
90///
91/// Johnson, N. L., Kotz, S. and Balakrishnan, N. (1995) Continuous Univariate Distributions,
92/// volume 1, chapter 14. Wiley, New York.
93///
94/// ## See Also
95///
96/// Distributions for other standard distributions, including dnorm for the normal distribution.
97///
98/// ## Examples
99///
100/// ```rust
101/// # use r2rs_nmath::{
102/// # distribution::{LogNormalBuilder, NormalBuilder},
103/// # traits::Distribution,
104/// # };
105/// let norm = NormalBuilder::new().build();
106/// println!("{}", norm.density(0));
107/// let lnorm = LogNormalBuilder::new().build();
108/// println!("{}", lnorm.density(1));
109/// # use std::{fs::File, io::Write};
110/// # let mut f = File::create("src/distribution/lnorm/doctest_out/dens.md").unwrap();
111/// # writeln!(f, "```output").unwrap();
112/// # writeln!(f, "{}", norm.density(0)).unwrap();
113/// # writeln!(f, "{}", lnorm.density(1)).unwrap();
114/// # writeln!(f, "```").unwrap();
115/// ```
116#[cfg_attr(feature = "doc_outputs", cfg_attr(all(), doc = include_str!("doctest_out/dens.md")))]
117pub struct LogNormal {
118 mean_log: Real64,
119 standard_deviation_log: Positive64,
120}
121
122impl Distribution for LogNormal {
123 fn density<R: Into<Real64>>(&self, x: R) -> Real64 {
124 dlnorm(x, self.mean_log, self.standard_deviation_log, false)
125 }
126
127 fn log_density<R: Into<Real64>>(&self, x: R) -> Real64 {
128 dlnorm(x, self.mean_log, self.standard_deviation_log, true)
129 }
130
131 fn probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> Probability64 {
132 plnorm(q, self.mean_log, self.standard_deviation_log, lower_tail)
133 }
134
135 fn log_probability<R: Into<Real64>>(&self, q: R, lower_tail: bool) -> LogProbability64 {
136 log_plnorm(q, self.mean_log, self.standard_deviation_log, lower_tail)
137 }
138
139 fn quantile<P: Into<Probability64>>(&self, p: P, lower_tail: bool) -> Real64 {
140 qlnorm(p, self.mean_log, self.standard_deviation_log, lower_tail)
141 }
142
143 fn log_quantile<LP: Into<LogProbability64>>(&self, p: LP, lower_tail: bool) -> Real64 {
144 log_qlnorm(p, self.mean_log, self.standard_deviation_log, lower_tail)
145 }
146
147 fn random_sample<R: RNG>(&self, rng: &mut R) -> Real64 {
148 rlnorm(self.mean_log, self.standard_deviation_log, rng)
149 }
150}
151
152pub struct LogNormalBuilder {
153 mean_log: Option<Real64>,
154 standard_deviation_log: Option<Positive64>,
155}
156
157impl LogNormalBuilder {
158 pub fn new() -> Self {
159 Self {
160 mean_log: None,
161 standard_deviation_log: None,
162 }
163 }
164
165 pub fn with_mean_log<R: Into<Real64>>(&mut self, mean_log: R) -> &mut Self {
166 self.mean_log = Some(mean_log.into());
167 self
168 }
169
170 pub fn with_standard_deviation_log<P: Into<Positive64>>(
171 &mut self,
172 standard_deviation_log: P,
173 ) -> &mut Self {
174 self.standard_deviation_log = Some(standard_deviation_log.into());
175 self
176 }
177
178 pub fn build(&self) -> LogNormal {
179 let mean_log = self.mean_log.unwrap_or(0.0.into());
180 let standard_deviation_log = self.standard_deviation_log.unwrap_or(1.0.into());
181
182 LogNormal {
183 mean_log,
184 standard_deviation_log,
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests;
191
192#[cfg(all(test, feature = "enable_proptest"))]
193mod proptests;
194
195#[cfg(all(test, feature = "enable_covtest"))]
196mod covtests;