github.com/gopherd/gonum@v0.0.4/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  	"math/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  }