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  }