github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/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 "github.com/jingcheng-WU/gonum/floats" 13 "github.com/jingcheng-WU/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 // (∂/∂θ) log(p(x;θ)) 142 // If deriv is non-nil, len(deriv) must equal the number of parameters otherwise 143 // Score will panic, and the derivative is stored in-place into deriv. If deriv 144 // is nil a new slice will be allocated and returned. 145 // 146 // The order is [∂LogProb / ∂Rate]. 147 // 148 // For more information, see https://en.wikipedia.org/wiki/Score_%28statistics%29. 149 // 150 // Special cases: 151 // Score(0) = [NaN] 152 func (e Exponential) Score(deriv []float64, x float64) []float64 { 153 if deriv == nil { 154 deriv = make([]float64, e.NumParameters()) 155 } 156 if len(deriv) != e.NumParameters() { 157 panic(badLength) 158 } 159 if x > 0 { 160 deriv[0] = 1/e.Rate - x 161 return deriv 162 } 163 if x < 0 { 164 deriv[0] = 0 165 return deriv 166 } 167 deriv[0] = math.NaN() 168 return deriv 169 } 170 171 // ScoreInput returns the score function with respect to the input of the 172 // distribution at the input location specified by x. The score function is the 173 // derivative of the log-likelihood 174 // (d/dx) log(p(x)) . 175 // Special cases: 176 // ScoreInput(0) = NaN 177 func (e Exponential) ScoreInput(x float64) float64 { 178 if x > 0 { 179 return -e.Rate 180 } 181 if x < 0 { 182 return 0 183 } 184 return math.NaN() 185 } 186 187 // Skewness returns the skewness of the distribution. 188 func (Exponential) Skewness() float64 { 189 return 2 190 } 191 192 // StdDev returns the standard deviation of the probability distribution. 193 func (e Exponential) StdDev() float64 { 194 return 1 / e.Rate 195 } 196 197 // SuffStat computes the sufficient statistics of set of samples to update 198 // the distribution. The sufficient statistics are stored in place, and the 199 // effective number of samples are returned. 200 // 201 // The exponential distribution has one sufficient statistic, the average rate 202 // of the samples. 203 // 204 // If weights is nil, the weights are assumed to be 1, otherwise panics if 205 // len(samples) != len(weights). Panics if len(suffStat) != NumSuffStat(). 206 func (Exponential) SuffStat(suffStat, samples, weights []float64) (nSamples float64) { 207 if len(weights) != 0 && len(samples) != len(weights) { 208 panic(badLength) 209 } 210 211 if len(suffStat) != (Exponential{}).NumSuffStat() { 212 panic(badSuffStat) 213 } 214 215 if len(weights) == 0 { 216 nSamples = float64(len(samples)) 217 } else { 218 nSamples = floats.Sum(weights) 219 } 220 221 mean := stat.Mean(samples, weights) 222 suffStat[0] = 1 / mean 223 return nSamples 224 } 225 226 // Survival returns the survival function (complementary CDF) at x. 227 func (e Exponential) Survival(x float64) float64 { 228 if x < 0 { 229 return 1 230 } 231 return math.Exp(-e.Rate * x) 232 } 233 234 // setParameters modifies the parameters of the distribution. 235 func (e *Exponential) setParameters(p []Parameter) { 236 if len(p) != e.NumParameters() { 237 panic("exponential: incorrect number of parameters to set") 238 } 239 if p[0].Name != "Rate" { 240 panic("exponential: " + panicNameMismatch) 241 } 242 e.Rate = p[0].Value 243 } 244 245 // Variance returns the variance of the probability distribution. 246 func (e Exponential) Variance() float64 { 247 return 1 / (e.Rate * e.Rate) 248 } 249 250 // parameters returns the parameters of the distribution. 251 func (e Exponential) parameters(p []Parameter) []Parameter { 252 nParam := e.NumParameters() 253 if p == nil { 254 p = make([]Parameter, nParam) 255 } else if len(p) != nParam { 256 panic("exponential: improper parameter length") 257 } 258 p[0].Name = "Rate" 259 p[0].Value = e.Rate 260 return p 261 }