gonum.org/v1/gonum@v0.14.0/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 // 54 // If 0 < K < 1, LogProb returns +Inf. 55 // If K == 1, LogProb returns 0. 56 // If K > 1, LogProb returns -Inf. 57 func (w Weibull) LogProb(x float64) float64 { 58 if x < 0 { 59 return math.Inf(-1) 60 } 61 if x == 0 && w.K == 1 { 62 return 0 63 } 64 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) 65 } 66 67 // LogSurvival returns the log of the survival function (complementary CDF) at x. 68 func (w Weibull) LogSurvival(x float64) float64 { 69 if x < 0 { 70 return 0 71 } 72 return -math.Pow(x/w.Lambda, w.K) 73 } 74 75 // Mean returns the mean of the probability distribution. 76 func (w Weibull) Mean() float64 { 77 return w.Lambda * math.Gamma(1+1/w.K) 78 } 79 80 // Median returns the median of the normal distribution. 81 func (w Weibull) Median() float64 { 82 return w.Lambda * math.Pow(ln2, 1/w.K) 83 } 84 85 // Mode returns the mode of the normal distribution. 86 // 87 // The mode is NaN in the special case where the K (shape) parameter 88 // is less than 1. 89 func (w Weibull) Mode() float64 { 90 if w.K > 1 { 91 return w.Lambda * math.Pow((w.K-1)/w.K, 1/w.K) 92 } 93 return 0 94 } 95 96 // NumParameters returns the number of parameters in the distribution. 97 func (Weibull) NumParameters() int { 98 return 2 99 } 100 101 // Prob computes the value of the probability density function at x. 102 func (w Weibull) Prob(x float64) float64 { 103 if x < 0 { 104 return 0 105 } 106 return math.Exp(w.LogProb(x)) 107 } 108 109 // Quantile returns the inverse of the cumulative probability distribution. 110 func (w Weibull) Quantile(p float64) float64 { 111 if p < 0 || p > 1 { 112 panic(badPercentile) 113 } 114 return w.Lambda * math.Pow(-math.Log(1-p), 1/w.K) 115 } 116 117 // Rand returns a random sample drawn from the distribution. 118 func (w Weibull) Rand() float64 { 119 var rnd float64 120 if w.Src == nil { 121 rnd = rand.Float64() 122 } else { 123 rnd = rand.New(w.Src).Float64() 124 } 125 return w.Quantile(rnd) 126 } 127 128 // Score returns the score function with respect to the parameters of the 129 // distribution at the input location x. The score function is the derivative 130 // of the log-likelihood at x with respect to the parameters 131 // 132 // (∂/∂θ) log(p(x;θ)) 133 // 134 // If deriv is non-nil, len(deriv) must equal the number of parameters otherwise 135 // Score will panic, and the derivative is stored in-place into deriv. If deriv 136 // is nil a new slice will be allocated and returned. 137 // 138 // The order is [∂LogProb / ∂K, ∂LogProb / ∂λ]. 139 // 140 // For more information, see https://en.wikipedia.org/wiki/Score_%28statistics%29. 141 // 142 // Special cases: 143 // 144 // Score(x) = [NaN, NaN] for x <= 0 145 func (w Weibull) Score(deriv []float64, x float64) []float64 { 146 if deriv == nil { 147 deriv = make([]float64, w.NumParameters()) 148 } 149 if len(deriv) != w.NumParameters() { 150 panic(badLength) 151 } 152 if x > 0 { 153 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) 154 deriv[1] = (w.K * (math.Pow(x/w.Lambda, w.K) - 1)) / w.Lambda 155 return deriv 156 } 157 deriv[0] = math.NaN() 158 deriv[1] = math.NaN() 159 return deriv 160 } 161 162 // ScoreInput returns the score function with respect to the input of the 163 // distribution at the input location specified by x. The score function is the 164 // derivative of the log-likelihood 165 // 166 // (d/dx) log(p(x)) . 167 // 168 // Special cases: 169 // 170 // ScoreInput(x) = NaN for x <= 0 171 func (w Weibull) ScoreInput(x float64) float64 { 172 if x > 0 { 173 return (-w.K*math.Pow(x/w.Lambda, w.K) + w.K - 1) / x 174 } 175 return math.NaN() 176 } 177 178 // Skewness returns the skewness of the distribution. 179 func (w Weibull) Skewness() float64 { 180 stdDev := w.StdDev() 181 firstGamma, firstGammaSign := math.Lgamma(1 + 3/w.K) 182 logFirst := firstGamma + 3*(math.Log(w.Lambda)-math.Log(stdDev)) 183 logSecond := math.Log(3) + math.Log(w.Mean()) + 2*math.Log(stdDev) - 3*math.Log(stdDev) 184 logThird := 3 * (math.Log(w.Mean()) - math.Log(stdDev)) 185 return float64(firstGammaSign)*math.Exp(logFirst) - math.Exp(logSecond) - math.Exp(logThird) 186 } 187 188 // StdDev returns the standard deviation of the probability distribution. 189 func (w Weibull) StdDev() float64 { 190 return math.Sqrt(w.Variance()) 191 } 192 193 // Survival returns the survival function (complementary CDF) at x. 194 func (w Weibull) Survival(x float64) float64 { 195 return math.Exp(w.LogSurvival(x)) 196 } 197 198 // setParameters modifies the parameters of the distribution. 199 func (w *Weibull) setParameters(p []Parameter) { 200 if len(p) != w.NumParameters() { 201 panic("weibull: incorrect number of parameters to set") 202 } 203 if p[0].Name != "K" { 204 panic("weibull: " + panicNameMismatch) 205 } 206 if p[1].Name != "λ" { 207 panic("weibull: " + panicNameMismatch) 208 } 209 w.K = p[0].Value 210 w.Lambda = p[1].Value 211 } 212 213 // Variance returns the variance of the probability distribution. 214 func (w Weibull) Variance() float64 { 215 return math.Pow(w.Lambda, 2) * (math.Gamma(1+2/w.K) - w.gammaIPow(1, 2)) 216 } 217 218 // parameters returns the parameters of the distribution. 219 func (w Weibull) parameters(p []Parameter) []Parameter { 220 nParam := w.NumParameters() 221 if p == nil { 222 p = make([]Parameter, nParam) 223 } else if len(p) != nParam { 224 panic("weibull: improper parameter length") 225 } 226 p[0].Name = "K" 227 p[0].Value = w.K 228 p[1].Name = "λ" 229 p[1].Value = w.Lambda 230 return p 231 232 }