1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DeriveDataTypeable, DeriveGeneric #-}
-- |
-- Module : Statistics.Distribution.Lognormal
-- Copyright : (c) 2020 Ximin Luo
-- License : BSD3
--
-- Maintainer : infinity0@pwned.gg
-- Stability : experimental
-- Portability : portable
--
-- The log normal distribution. This is a continuous probability
-- distribution that describes data whose log is clustered around a
-- mean. For example, the multiplicative product of many independent
-- positive random variables.
module Statistics.Distribution.Lognormal
(
LognormalDistribution
-- * Constructors
, lognormalDistr
, lognormalDistrErr
, lognormalDistrMeanStddevErr
, lognormalStandard
) where
import Data.Aeson (FromJSON, ToJSON)
import Data.Binary (Binary (..))
import Data.Data (Data, Typeable)
import GHC.Generics (Generic)
import Numeric.MathFunctions.Constants (m_huge, m_sqrt_2_pi)
import Numeric.SpecFunctions (expm1, log1p)
import qualified Data.Vector.Generic as G
import qualified Statistics.Distribution as D
import qualified Statistics.Distribution.Normal as N
import Statistics.Internal
-- | The lognormal distribution.
newtype LognormalDistribution = LND N.NormalDistribution
deriving (Eq, Typeable, Data, Generic)
instance Show LognormalDistribution where
showsPrec i (LND d) = defaultShow2 "lognormalDistr" m s i
where
m = D.mean d
s = D.stdDev d
instance Read LognormalDistribution where
readPrec = defaultReadPrecM2 "lognormalDistr" $
(either (const Nothing) Just .) . lognormalDistrErr
instance ToJSON LognormalDistribution
instance FromJSON LognormalDistribution
instance Binary LognormalDistribution where
put (LND d) = put m >> put s
where
m = D.mean d
s = D.stdDev d
get = do
m <- get
sd <- get
either fail return $ lognormalDistrErr m sd
instance D.Distribution LognormalDistribution where
cumulative = cumulative
complCumulative = complCumulative
instance D.ContDistr LognormalDistribution where
logDensity = logDensity
quantile = quantile
complQuantile = complQuantile
instance D.MaybeMean LognormalDistribution where
maybeMean = Just . D.mean
instance D.Mean LognormalDistribution where
mean (LND d) = exp (m + v / 2)
where
m = D.mean d
v = D.variance d
instance D.MaybeVariance LognormalDistribution where
maybeStdDev = Just . D.stdDev
maybeVariance = Just . D.variance
instance D.Variance LognormalDistribution where
variance (LND d) = expm1 v * exp (2 * m + v)
where
m = D.mean d
v = D.variance d
instance D.Entropy LognormalDistribution where
entropy (LND d) = logBase 2 (s * exp (m + 0.5) * m_sqrt_2_pi)
where
m = D.mean d
s = D.stdDev d
instance D.MaybeEntropy LognormalDistribution where
maybeEntropy = Just . D.entropy
instance D.ContGen LognormalDistribution where
genContVar d = D.genContinuous d
-- | Standard log normal distribution with mu 0 and sigma 1.
--
-- Mean is @sqrt e@ and variance is @(e - 1) * e@.
lognormalStandard :: LognormalDistribution
lognormalStandard = LND N.standard
-- | Create log normal distribution from parameters.
lognormalDistr
:: Double -- ^ Mu
-> Double -- ^ Sigma
-> LognormalDistribution
lognormalDistr mu sig = either error id $ lognormalDistrErr mu sig
-- | Create log normal distribution from parameters.
lognormalDistrErr
:: Double -- ^ Mu
-> Double -- ^ Sigma
-> Either String LognormalDistribution
lognormalDistrErr mu sig
| sig >= sqrt (log m_huge - 2 * mu) = Left $ errMsg mu sig
| otherwise = LND <$> N.normalDistrErr mu sig
errMsg :: Double -> Double -> String
errMsg mu sig =
"Statistics.Distribution.Lognormal.lognormalDistr: sigma must be > 0 && < "
++ show lim ++ ". Got " ++ show sig
where lim = sqrt (log m_huge - 2 * mu)
-- | Create log normal distribution from mean and standard deviation.
lognormalDistrMeanStddevErr
:: Double -- ^ Mu
-> Double -- ^ Sigma
-> Either String LognormalDistribution
lognormalDistrMeanStddevErr m sd = LND <$> N.normalDistrErr mu sig
where r = sd / m
sig2 = log1p (r * r)
sig = sqrt sig2
mu = log m - sig2 / 2
-- | Variance is estimated using maximum likelihood method
-- (biased estimation) over the log of the data.
--
-- Returns @Nothing@ if sample contains less than one element or
-- variance is zero (all elements are equal)
instance D.FromSample LognormalDistribution Double where
fromSample = fmap LND . D.fromSample . G.map log
logDensity :: LognormalDistribution -> Double -> Double
logDensity (LND d) x
| x > 0 = let lx = log x in D.logDensity d lx - lx
| otherwise = 0
cumulative :: LognormalDistribution -> Double -> Double
cumulative (LND d) x
| x > 0 = D.cumulative d $ log x
| otherwise = 0
complCumulative :: LognormalDistribution -> Double -> Double
complCumulative (LND d) x
| x > 0 = D.complCumulative d $ log x
| otherwise = 1
quantile :: LognormalDistribution -> Double -> Double
quantile (LND d) = exp . D.quantile d
complQuantile :: LognormalDistribution -> Double -> Double
complQuantile (LND d) = exp . D.complQuantile d
|