gonum.org/v1/gonum@v0.14.0/stat/distuv/exponential.go (about) 1 // Copyright ©2014 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package distuv 6 7 import ( 8 "math" 9 10 "golang.org/x/exp/rand" 11 12 "gonum.org/v1/gonum/floats" 13 "gonum.org/v1/gonum/stat" 14 ) 15 16 // Exponential represents the exponential distribution (https://en.wikipedia.org/wiki/Exponential_distribution). 17 type Exponential struct { 18 Rate float64 19 Src rand.Source 20 } 21 22 // CDF computes the value of the cumulative density function at x. 23 func (e Exponential) CDF(x float64) float64 { 24 if x < 0 { 25 return 0 26 } 27 return -math.Expm1(-e.Rate * x) 28 } 29 30 // ConjugateUpdate updates the parameters of the distribution from the sufficient 31 // statistics of a set of samples. The sufficient statistics, suffStat, have been 32 // observed with nSamples observations. The prior values of the distribution are those 33 // currently in the distribution, and have been observed with priorStrength samples. 34 // 35 // For the exponential distribution, the sufficient statistic is the inverse of 36 // the mean of the samples. 37 // The prior is having seen priorStrength[0] samples with inverse mean Exponential.Rate 38 // As a result of this function, Exponential.Rate is updated based on the weighted 39 // samples, and priorStrength is modified to include the new number of samples observed. 40 // 41 // This function panics if len(suffStat) != e.NumSuffStat() or 42 // len(priorStrength) != e.NumSuffStat(). 43 func (e *Exponential) ConjugateUpdate(suffStat []float64, nSamples float64, priorStrength []float64) { 44 if len(suffStat) != e.NumSuffStat() { 45 panic("exponential: incorrect suffStat length") 46 } 47 if len(priorStrength) != e.NumSuffStat() { 48 panic("exponential: incorrect priorStrength length") 49 } 50 51 totalSamples := nSamples + priorStrength[0] 52 53 totalSum := nSamples / suffStat[0] 54 if !(priorStrength[0] == 0) { 55 totalSum += priorStrength[0] / e.Rate 56 } 57 e.Rate = totalSamples / totalSum 58 priorStrength[0] = totalSamples 59 } 60 61 // Entropy returns the entropy of the distribution. 62 func (e Exponential) Entropy() float64 { 63 return 1 - math.Log(e.Rate) 64 } 65 66 // ExKurtosis returns the excess kurtosis of the distribution. 67 func (Exponential) ExKurtosis() float64 { 68 return 6 69 } 70 71 // Fit sets the parameters of the probability distribution from the 72 // data samples x with relative weights w. 73 // If weights is nil, then all the weights are 1. 74 // If weights is not nil, then the len(weights) must equal len(samples). 75 func (e *Exponential) Fit(samples, weights []float64) { 76 suffStat := make([]float64, e.NumSuffStat()) 77 nSamples := e.SuffStat(suffStat, samples, weights) 78 e.ConjugateUpdate(suffStat, nSamples, make([]float64, e.NumSuffStat())) 79 } 80 81 // LogProb computes the natural logarithm of the value of the probability density function at x. 82 func (e Exponential) LogProb(x float64) float64 { 83 if x < 0 { 84 return math.Inf(-1) 85 } 86 return math.Log(e.Rate) - e.Rate*x 87 } 88 89 // Mean returns the mean of the probability distribution. 90 func (e Exponential) Mean() float64 { 91 return 1 / e.Rate 92 } 93 94 // Median returns the median of the probability distribution. 95 func (e Exponential) Median() float64 { 96 return math.Ln2 / e.Rate 97 } 98 99 // Mode returns the mode of the probability distribution. 100 func (Exponential) Mode() float64 { 101 return 0 102 } 103 104 // NumParameters returns the number of parameters in the distribution. 105 func (Exponential) NumParameters() int { 106 return 1 107 } 108 109 // NumSuffStat returns the number of sufficient statistics for the distribution. 110 func (Exponential) NumSuffStat() int { 111 return 1 112 } 113 114 // Prob computes the value of the probability density function at x. 115 func (e Exponential) Prob(x float64) float64 { 116 return math.Exp(e.LogProb(x)) 117 } 118 119 // Quantile returns the inverse of the cumulative probability distribution. 120 func (e Exponential) Quantile(p float64) float64 { 121 if p < 0 || p > 1 { 122 panic(badPercentile) 123 } 124 return -math.Log(1-p) / e.Rate 125 } 126 127 // Rand returns a random sample drawn from the distribution. 128 func (e Exponential) Rand() float64 { 129 var rnd float64 130 if e.Src == nil { 131 rnd = rand.ExpFloat64() 132 } else { 133 rnd = rand.New(e.Src).ExpFloat64() 134 } 135 return rnd / e.Rate 136 } 137 138 // Score returns the score function with respect to the parameters of the 139 // distribution at the input location x. The score function is the derivative 140 // of the log-likelihood at x with respect to the parameters 141 // 142 // (∂/∂θ) log(p(x;θ)) 143 // 144 // If deriv is non-nil, len(deriv) must equal the number of parameters otherwise 145 // Score will panic, and the derivative is stored in-place into deriv. If deriv 146 // is nil a new slice will be allocated and returned. 147 // 148 // The order is [∂LogProb / ∂Rate]. 149 // 150 // For more information, see https://en.wikipedia.org/wiki/Score_%28statistics%29. 151 // 152 // Special cases: 153 // 154 // Score(0) = [NaN] 155 func (e Exponential) Score(deriv []float64, x float64) []float64 { 156 if deriv == nil { 157 deriv = make([]float64, e.NumParameters()) 158 } 159 if len(deriv) != e.NumParameters() { 160 panic(badLength) 161 } 162 if x > 0 { 163 deriv[0] = 1/e.Rate - x 164 return deriv 165 } 166 if x < 0 { 167 deriv[0] = 0 168 return deriv 169 } 170 deriv[0] = math.NaN() 171 return deriv 172 } 173 174 // ScoreInput returns the score function with respect to the input of the 175 // distribution at the input location specified by x. The score function is the 176 // derivative of the log-likelihood 177 // 178 // (d/dx) log(p(x)) . 179 // 180 // Special cases: 181 // 182 // ScoreInput(0) = NaN 183 func (e Exponential) ScoreInput(x float64) float64 { 184 if x > 0 { 185 return -e.Rate 186 } 187 if x < 0 { 188 return 0 189 } 190 return math.NaN() 191 } 192 193 // Skewness returns the skewness of the distribution. 194 func (Exponential) Skewness() float64 { 195 return 2 196 } 197 198 // StdDev returns the standard deviation of the probability distribution. 199 func (e Exponential) StdDev() float64 { 200 return 1 / e.Rate 201 } 202 203 // SuffStat computes the sufficient statistics of set of samples to update 204 // the distribution. The sufficient statistics are stored in place, and the 205 // effective number of samples are returned. 206 // 207 // The exponential distribution has one sufficient statistic, the average rate 208 // of the samples. 209 // 210 // If weights is nil, the weights are assumed to be 1, otherwise panics if 211 // len(samples) != len(weights). Panics if len(suffStat) != NumSuffStat(). 212 func (Exponential) SuffStat(suffStat, samples, weights []float64) (nSamples float64) { 213 if len(weights) != 0 && len(samples) != len(weights) { 214 panic(badLength) 215 } 216 217 if len(suffStat) != (Exponential{}).NumSuffStat() { 218 panic(badSuffStat) 219 } 220 221 if len(weights) == 0 { 222 nSamples = float64(len(samples)) 223 } else { 224 nSamples = floats.Sum(weights) 225 } 226 227 mean := stat.Mean(samples, weights) 228 suffStat[0] = 1 / mean 229 return nSamples 230 } 231 232 // Survival returns the survival function (complementary CDF) at x. 233 func (e Exponential) Survival(x float64) float64 { 234 if x < 0 { 235 return 1 236 } 237 return math.Exp(-e.Rate * x) 238 } 239 240 // setParameters modifies the parameters of the distribution. 241 func (e *Exponential) setParameters(p []Parameter) { 242 if len(p) != e.NumParameters() { 243 panic("exponential: incorrect number of parameters to set") 244 } 245 if p[0].Name != "Rate" { 246 panic("exponential: " + panicNameMismatch) 247 } 248 e.Rate = p[0].Value 249 } 250 251 // Variance returns the variance of the probability distribution. 252 func (e Exponential) Variance() float64 { 253 return 1 / (e.Rate * e.Rate) 254 } 255 256 // parameters returns the parameters of the distribution. 257 func (e Exponential) parameters(p []Parameter) []Parameter { 258 nParam := e.NumParameters() 259 if p == nil { 260 p = make([]Parameter, nParam) 261 } else if len(p) != nParam { 262 panic("exponential: improper parameter length") 263 } 264 p[0].Name = "Rate" 265 p[0].Value = e.Rate 266 return p 267 }