github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/stat/distuv/weibull.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 13 // Weibull distribution. Valid range for x is [0,+∞). 14 type Weibull struct { 15 // Shape parameter of the distribution. A value of 1 represents 16 // the exponential distribution. A value of 2 represents the 17 // Rayleigh distribution. Valid range is (0,+∞). 18 K float64 19 // Scale parameter of the distribution. Valid range is (0,+∞). 20 Lambda float64 21 // Source of random numbers 22 Src rand.Source 23 } 24 25 // CDF computes the value of the cumulative density function at x. 26 func (w Weibull) CDF(x float64) float64 { 27 if x < 0 { 28 return 0 29 } 30 return -math.Expm1(-math.Pow(x/w.Lambda, w.K)) 31 } 32 33 // Entropy returns the entropy of the distribution. 34 func (w Weibull) Entropy() float64 { 35 return eulerGamma*(1-1/w.K) + math.Log(w.Lambda/w.K) + 1 36 } 37 38 // ExKurtosis returns the excess kurtosis of the distribution. 39 func (w Weibull) ExKurtosis() float64 { 40 return (-6*w.gammaIPow(1, 4) + 12*w.gammaIPow(1, 2)*math.Gamma(1+2/w.K) - 3*w.gammaIPow(2, 2) - 4*math.Gamma(1+1/w.K)*math.Gamma(1+3/w.K) + math.Gamma(1+4/w.K)) / math.Pow(math.Gamma(1+2/w.K)-w.gammaIPow(1, 2), 2) 41 } 42 43 // gammIPow is a shortcut for computing the gamma function to a power. 44 func (w Weibull) gammaIPow(i, pow float64) float64 { 45 return math.Pow(math.Gamma(1+i/w.K), pow) 46 } 47 48 // LogProb computes the natural logarithm of the value of the probability 49 // density function at x. -Inf is returned if x is less than zero. 50 // 51 // Special cases occur when x == 0, and the result depends on the shape 52 // parameter as follows: 53 // If 0 < K < 1, LogProb returns +Inf. 54 // If K == 1, LogProb returns 0. 55 // If K > 1, LogProb returns -Inf. 56 func (w Weibull) LogProb(x float64) float64 { 57 if x < 0 { 58 return math.Inf(-1) 59 } 60 if x == 0 && w.K == 1 { 61 return 0 62 } 63 return math.Log(w.K) - math.Log(w.Lambda) + (w.K-1)*(math.Log(x)-math.Log(w.Lambda)) - math.Pow(x/w.Lambda, w.K) 64 } 65 66 // LogSurvival returns the log of the survival function (complementary CDF) at x. 67 func (w Weibull) LogSurvival(x float64) float64 { 68 if x < 0 { 69 return 0 70 } 71 return -math.Pow(x/w.Lambda, w.K) 72 } 73 74 // Mean returns the mean of the probability distribution. 75 func (w Weibull) Mean() float64 { 76 return w.Lambda * math.Gamma(1+1/w.K) 77 } 78 79 // Median returns the median of the normal distribution. 80 func (w Weibull) Median() float64 { 81 return w.Lambda * math.Pow(ln2, 1/w.K) 82 } 83 84 // Mode returns the mode of the normal distribution. 85 // 86 // The mode is NaN in the special case where the K (shape) parameter 87 // is less than 1. 88 func (w Weibull) Mode() float64 { 89 if w.K > 1 { 90 return w.Lambda * math.Pow((w.K-1)/w.K, 1/w.K) 91 } 92 return 0 93 } 94 95 // NumParameters returns the number of parameters in the distribution. 96 func (Weibull) NumParameters() int { 97 return 2 98 } 99 100 // Prob computes the value of the probability density function at x. 101 func (w Weibull) Prob(x float64) float64 { 102 if x < 0 { 103 return 0 104 } 105 return math.Exp(w.LogProb(x)) 106 } 107 108 // Quantile returns the inverse of the cumulative probability distribution. 109 func (w Weibull) Quantile(p float64) float64 { 110 if p < 0 || p > 1 { 111 panic(badPercentile) 112 } 113 return w.Lambda * math.Pow(-math.Log(1-p), 1/w.K) 114 } 115 116 // Rand returns a random sample drawn from the distribution. 117 func (w Weibull) Rand() float64 { 118 var rnd float64 119 if w.Src == nil { 120 rnd = rand.Float64() 121 } else { 122 rnd = rand.New(w.Src).Float64() 123 } 124 return w.Quantile(rnd) 125 } 126 127 // Score returns the score function with respect to the parameters of the 128 // distribution at the input location x. The score function is the derivative 129 // of the log-likelihood at x with respect to the parameters 130 // (∂/∂θ) log(p(x;θ)) 131 // If deriv is non-nil, len(deriv) must equal the number of parameters otherwise 132 // Score will panic, and the derivative is stored in-place into deriv. If deriv 133 // is nil a new slice will be allocated and returned. 134 // 135 // The order is [∂LogProb / ∂K, ∂LogProb / ∂λ]. 136 // 137 // For more information, see https://en.wikipedia.org/wiki/Score_%28statistics%29. 138 // 139 // Special cases: 140 // Score(x) = [NaN, NaN] for x <= 0 141 func (w Weibull) Score(deriv []float64, x float64) []float64 { 142 if deriv == nil { 143 deriv = make([]float64, w.NumParameters()) 144 } 145 if len(deriv) != w.NumParameters() { 146 panic(badLength) 147 } 148 if x > 0 { 149 deriv[0] = 1/w.K + math.Log(x) - math.Log(w.Lambda) - (math.Log(x)-math.Log(w.Lambda))*math.Pow(x/w.Lambda, w.K) 150 deriv[1] = (w.K * (math.Pow(x/w.Lambda, w.K) - 1)) / w.Lambda 151 return deriv 152 } 153 deriv[0] = math.NaN() 154 deriv[1] = math.NaN() 155 return deriv 156 } 157 158 // ScoreInput returns the score function with respect to the input of the 159 // distribution at the input location specified by x. The score function is the 160 // derivative of the log-likelihood 161 // (d/dx) log(p(x)) . 162 // 163 // Special cases: 164 // ScoreInput(x) = NaN for x <= 0 165 func (w Weibull) ScoreInput(x float64) float64 { 166 if x > 0 { 167 return (-w.K*math.Pow(x/w.Lambda, w.K) + w.K - 1) / x 168 } 169 return math.NaN() 170 } 171 172 // Skewness returns the skewness of the distribution. 173 func (w Weibull) Skewness() float64 { 174 stdDev := w.StdDev() 175 firstGamma, firstGammaSign := math.Lgamma(1 + 3/w.K) 176 logFirst := firstGamma + 3*(math.Log(w.Lambda)-math.Log(stdDev)) 177 logSecond := math.Log(3) + math.Log(w.Mean()) + 2*math.Log(stdDev) - 3*math.Log(stdDev) 178 logThird := 3 * (math.Log(w.Mean()) - math.Log(stdDev)) 179 return float64(firstGammaSign)*math.Exp(logFirst) - math.Exp(logSecond) - math.Exp(logThird) 180 } 181 182 // StdDev returns the standard deviation of the probability distribution. 183 func (w Weibull) StdDev() float64 { 184 return math.Sqrt(w.Variance()) 185 } 186 187 // Survival returns the survival function (complementary CDF) at x. 188 func (w Weibull) Survival(x float64) float64 { 189 return math.Exp(w.LogSurvival(x)) 190 } 191 192 // setParameters modifies the parameters of the distribution. 193 func (w *Weibull) setParameters(p []Parameter) { 194 if len(p) != w.NumParameters() { 195 panic("weibull: incorrect number of parameters to set") 196 } 197 if p[0].Name != "K" { 198 panic("weibull: " + panicNameMismatch) 199 } 200 if p[1].Name != "λ" { 201 panic("weibull: " + panicNameMismatch) 202 } 203 w.K = p[0].Value 204 w.Lambda = p[1].Value 205 } 206 207 // Variance returns the variance of the probability distribution. 208 func (w Weibull) Variance() float64 { 209 return math.Pow(w.Lambda, 2) * (math.Gamma(1+2/w.K) - w.gammaIPow(1, 2)) 210 } 211 212 // parameters returns the parameters of the distribution. 213 func (w Weibull) parameters(p []Parameter) []Parameter { 214 nParam := w.NumParameters() 215 if p == nil { 216 p = make([]Parameter, nParam) 217 } else if len(p) != nParam { 218 panic("weibull: improper parameter length") 219 } 220 p[0].Name = "K" 221 p[0].Value = w.K 222 p[1].Name = "λ" 223 p[1].Value = w.Lambda 224 return p 225 226 }