Skip to content

Commit

Permalink
Add log-normal distribution
Browse files Browse the repository at this point in the history
Ref: #73
  • Loading branch information
mitchelloharawild committed Nov 12, 2021
1 parent d60c0ef commit 71a2b9b
Show file tree
Hide file tree
Showing 9 changed files with 334 additions and 13 deletions.
15 changes: 15 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ S3method("dimnames<-",distribution)
S3method("names<-",hilo)
S3method(.DollarNames,hilo)
S3method(Math,dist_default)
S3method(Math,dist_lognormal)
S3method(Math,dist_na)
S3method(Math,dist_normal)
S3method(Math,dist_transformed)
S3method(Ops,dist_default)
S3method(Ops,dist_na)
Expand All @@ -33,6 +35,7 @@ S3method(cdf,dist_inverse_gamma)
S3method(cdf,dist_inverse_gaussian)
S3method(cdf,dist_logarithmic)
S3method(cdf,dist_logistic)
S3method(cdf,dist_lognormal)
S3method(cdf,dist_mixture)
S3method(cdf,dist_mvnorm)
S3method(cdf,dist_na)
Expand Down Expand Up @@ -73,6 +76,7 @@ S3method(covariance,dist_inverse_gamma)
S3method(covariance,dist_inverse_gaussian)
S3method(covariance,dist_logarithmic)
S3method(covariance,dist_logistic)
S3method(covariance,dist_lognormal)
S3method(covariance,dist_mixture)
S3method(covariance,dist_multinomial)
S3method(covariance,dist_mvnorm)
Expand Down Expand Up @@ -110,6 +114,7 @@ S3method(density,dist_inverse_gamma)
S3method(density,dist_inverse_gaussian)
S3method(density,dist_logarithmic)
S3method(density,dist_logistic)
S3method(density,dist_lognormal)
S3method(density,dist_mixture)
S3method(density,dist_multinomial)
S3method(density,dist_mvnorm)
Expand Down Expand Up @@ -154,6 +159,7 @@ S3method(format,dist_inverse_gamma)
S3method(format,dist_inverse_gaussian)
S3method(format,dist_logarithmic)
S3method(format,dist_logistic)
S3method(format,dist_lognormal)
S3method(format,dist_mixture)
S3method(format,dist_multinomial)
S3method(format,dist_mvnorm)
Expand Down Expand Up @@ -197,6 +203,7 @@ S3method(generate,dist_inverse_gamma)
S3method(generate,dist_inverse_gaussian)
S3method(generate,dist_logarithmic)
S3method(generate,dist_logistic)
S3method(generate,dist_lognormal)
S3method(generate,dist_mixture)
S3method(generate,dist_multinomial)
S3method(generate,dist_mvnorm)
Expand Down Expand Up @@ -237,6 +244,7 @@ S3method(kurtosis,dist_geometric)
S3method(kurtosis,dist_gumbel)
S3method(kurtosis,dist_hypergeometric)
S3method(kurtosis,dist_logistic)
S3method(kurtosis,dist_lognormal)
S3method(kurtosis,dist_na)
S3method(kurtosis,dist_negbin)
S3method(kurtosis,dist_normal)
Expand All @@ -247,6 +255,7 @@ S3method(kurtosis,distribution)
S3method(likelihood,dist_default)
S3method(likelihood,distribution)
S3method(log_cdf,dist_default)
S3method(log_cdf,dist_lognormal)
S3method(log_cdf,dist_na)
S3method(log_cdf,dist_normal)
S3method(log_cdf,distribution)
Expand All @@ -268,6 +277,7 @@ S3method(log_density,dist_inverse_gamma)
S3method(log_density,dist_inverse_gaussian)
S3method(log_density,dist_logarithmic)
S3method(log_density,dist_logistic)
S3method(log_density,dist_lognormal)
S3method(log_density,dist_multinomial)
S3method(log_density,dist_mvnorm)
S3method(log_density,dist_na)
Expand All @@ -284,6 +294,7 @@ S3method(log_density,distribution)
S3method(log_likelihood,dist_default)
S3method(log_likelihood,distribution)
S3method(log_quantile,dist_default)
S3method(log_quantile,dist_lognormal)
S3method(log_quantile,dist_na)
S3method(log_quantile,dist_normal)
S3method(log_quantile,distribution)
Expand All @@ -308,6 +319,7 @@ S3method(mean,dist_inverse_gamma)
S3method(mean,dist_inverse_gaussian)
S3method(mean,dist_logarithmic)
S3method(mean,dist_logistic)
S3method(mean,dist_lognormal)
S3method(mean,dist_mixture)
S3method(mean,dist_multinomial)
S3method(mean,dist_mvnorm)
Expand Down Expand Up @@ -352,6 +364,7 @@ S3method(quantile,dist_inverse_gamma)
S3method(quantile,dist_inverse_gaussian)
S3method(quantile,dist_logarithmic)
S3method(quantile,dist_logistic)
S3method(quantile,dist_lognormal)
S3method(quantile,dist_mixture)
S3method(quantile,dist_mvnorm)
S3method(quantile,dist_na)
Expand Down Expand Up @@ -385,6 +398,7 @@ S3method(skewness,dist_geometric)
S3method(skewness,dist_gumbel)
S3method(skewness,dist_hypergeometric)
S3method(skewness,dist_logistic)
S3method(skewness,dist_lognormal)
S3method(skewness,dist_na)
S3method(skewness,dist_negbin)
S3method(skewness,dist_normal)
Expand Down Expand Up @@ -442,6 +456,7 @@ export(dist_inverse_gamma)
export(dist_inverse_gaussian)
export(dist_logarithmic)
export(dist_logistic)
export(dist_lognormal)
export(dist_missing)
export(dist_mixture)
export(dist_multinomial)
Expand Down
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
### Probability distributions

* Added `dist_categorical()` for the Categorical distribution.
* Added `dist_lognormal()` for the log-normal distribution. Mathematical
conversion shortcuts have also been added, so `exp(dist_normal())` produces
`dist_lognormal()`.

### Generics

Expand Down
154 changes: 154 additions & 0 deletions R/dist_lognormal.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#' The log-normal distribution
#'
#' \lifecycle{stable}
#'
#' The log-normal distribution is a commonly used transformation of the Normal
#' distribution. If \eqn{X} follows a log-normal distribution, then \eqn{\ln{X}}
#' would be characteristed by a Normal distribution.
#'
#' @param mu The mean (location parameter) of the distribution, which is the
#' mean of the associated Normal distribution. Can be any real number.
#' @param sigma The standard deviation (scale parameter) of the distribution.
#' Can be any positive number.
#'
#' @details
#'
#' We recommend reading this documentation on
#' <https://pkg.mitchelloharawild.com/distributional/>, where the math
#' will render nicely.
#'
#' In the following, let \eqn{Y} be a Normal random variable with mean
#' `mu` = \eqn{\mu} and standard deviation `sigma` = \eqn{\sigma}. The
#' log-normal distribution \eqn{X = exp(Y)} is characterised by:
#'
#' **Support**: \eqn{R+}, the set of all real numbers greater than or equal to 0.
#'
#' **Mean**: \eqn{e^(\mu + \sigma^2/2}
#'
#' **Variance**: \eqn{(e^(\sigma^2)-1) e^(2\mu + \sigma^2}
#'
#' **Probability density function (p.d.f)**:
#'
#' \deqn{
#' f(x) = \frac{1}{x\sqrt{2 \pi \sigma^2}} e^{-(\ln{x} - \mu)^2 / 2 \sigma^2}
#' }{
#' f(x) = 1 / (x * sqrt(2 \pi \sigma^2)) exp(-(log(x) - \mu)^2 / (2 \sigma^2))
#' }
#'
#' **Cumulative distribution function (c.d.f)**:
#'
#' The cumulative distribution function has the form
#'
#' \deqn{
#' F(x) = \Phi((\ln{x} - \mu)/\sigma)
#' }{
#' F(x) = Phi((log(x) - \mu)/\sigma)
#' }
#'
#' Where \eqn{Phi}{Phi} is the CDF of a standard Normal distribution, N(0,1).
#'
#' @seealso [stats::Lognormal]
#'
#' @examples
#' dist <- dist_lognormal(mu = 1:5, sigma = 0.1)
#'
#' dist
#' mean(dist)
#' variance(dist)
#' skewness(dist)
#' kurtosis(dist)
#'
#' generate(dist, 10)
#'
#' density(dist, 2)
#' density(dist, 2, log = TRUE)
#'
#' cdf(dist, 4)
#'
#' quantile(dist, 0.7)
#'
#' # A log-normal distribution X is exp(Y), where Y is a Normal distribution of
#' # the same parameters. So log(X) will produce the Normal distribution Y.
#' log(dist)
#' @export
dist_lognormal <- function(mu = 0, sigma = 1){
mu <- vec_cast(mu, double())
sigma <- vec_cast(sigma, double())
if(any(sigma[!is.na(sigma)] < 0)){
abort("Standard deviation of a log-normal distribution must be non-negative")
}
new_dist(mu = mu, sigma = sigma, class = "dist_lognormal")
}

#' @export
format.dist_lognormal <- function(x, digits = 2, ...){
sprintf(
"lN(%s, %s)",
format(x[["mu"]], digits = digits, ...),
format(x[["sigma"]]^2, digits = digits, ...)
)
}

#' @export
density.dist_lognormal <- function(x, at, ...){
stats::dlnorm(at, x[["mu"]], x[["sigma"]])
}

#' @export
log_density.dist_lognormal <- function(x, at, ...){
stats::dlnorm(at, x[["mu"]], x[["sigma"]], log = TRUE)
}

#' @export
quantile.dist_lognormal <- function(x, p, ...){
stats::qlnorm(p, x[["mu"]], x[["sigma"]], ...)
}
#' @export
log_quantile.dist_lognormal <- function(x, p, ...){
stats::qlnorm(p, x[["mu"]], x[["sigma"]], ..., log.p = TRUE)
}

#' @export
cdf.dist_lognormal <- function(x, q, ...){
stats::plnorm(q, x[["mu"]], x[["sigma"]], ...)
}
#' @export
log_cdf.dist_lognormal <- function(x, q, ...){
stats::plnorm(q, x[["mu"]], x[["sigma"]], ..., log.p = TRUE)
}

#' @export
generate.dist_lognormal <- function(x, times, ...){
stats::rlnorm(times, x[["mu"]], x[["sigma"]])
}

#' @export
mean.dist_lognormal <- function(x, ...){
exp(x[["mu"]] + x[["sigma"]]^2/2)
}

#' @export
covariance.dist_lognormal <- function(x, ...){
s2 <- x[["sigma"]]^2
(exp(s2)-1)*exp(2*x[["mu"]] + s2)
}

#' @export
skewness.dist_lognormal <- function(x, ...) {
es2 <- exp(x[["sigma"]]^2)
(es2+2)*sqrt(es2-1)
}

#' @export
kurtosis.dist_lognormal <- function(x, ...) {
s2 <- x[["sigma"]]^2
exp(4*s2) + 2*exp(3*s2) + 3*exp(2*s2) - 6
}

#' @method Math dist_lognormal
#' @export
Math.dist_lognormal <- function(x, ...) {
# Shortcut to get Normal distribution from log-normal.
if(.Generic == "log") return(vec_data(dist_normal(x[["mu"]], x[["sigma"]]))[[1]])
NextMethod()
}
12 changes: 10 additions & 2 deletions R/dist_normal.R
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#' \deqn{
#' f(x) = \frac{1}{\sqrt{2 \pi \sigma^2}} e^{-(x - \mu)^2 / 2 \sigma^2}
#' }{
#' f(x) = 1 / (2 \pi \sigma^2) exp(-(x - \mu)^2 / (2 \sigma^2))
#' f(x) = 1 / sqrt(2 \pi \sigma^2) exp(-(x - \mu)^2 / (2 \sigma^2))
#' }
#'
#' **Cumulative distribution function (c.d.f)**:
Expand All @@ -49,7 +49,7 @@
#' \deqn{
#' F(t) = \int_{-\infty}^t \frac{1}{\sqrt{2 \pi \sigma^2}} e^{-(x - \mu)^2 / 2 \sigma^2} dx
#' }{
#' F(t) = integral_{-\infty}^t 1 / (2 \pi \sigma^2) exp(-(x - \mu)^2 / (2 \sigma^2)) dx
#' F(t) = integral_{-\infty}^t 1 / sqrt(2 \pi \sigma^2) exp(-(x - \mu)^2 / (2 \sigma^2)) dx
#' }
#'
#' but this integral does not have a closed form solution and must be
Expand Down Expand Up @@ -211,3 +211,11 @@ Ops.dist_normal <- function(e1, e2){
}
dist
}

#' @method Math dist_normal
#' @export
Math.dist_normal <- function(x, ...) {
# Shortcut to get log-normal distribution from Normal.
if(.Generic == "exp") return(vec_data(dist_lognormal(x[["mu"]], x[["sigma"]]))[[1]])
NextMethod()
}
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ reference:
- dist_inverse_gamma
- dist_inverse_gaussian
- dist_logistic
- dist_lognormal
- dist_multivariate_normal
- dist_normal
- dist_pareto
Expand Down
82 changes: 82 additions & 0 deletions man/dist_lognormal.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 71a2b9b

Please sign in to comment.