gonum.org/v1/gonum@v0.14.0/stat/distuv/studentst.go (about)

     1  // Copyright ©2016 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  	"gonum.org/v1/gonum/mathext"
    13  )
    14  
    15  const logPi = 1.1447298858494001741 // http://oeis.org/A053510
    16  
    17  // StudentsT implements the three-parameter Student's T distribution, a distribution
    18  // over the real numbers.
    19  //
    20  // The Student's T distribution has density function
    21  //
    22  //	Γ((ν+1)/2) / (sqrt(νπ) Γ(ν/2) σ) (1 + 1/ν * ((x-μ)/σ)^2)^(-(ν+1)/2)
    23  //
    24  // The Student's T distribution approaches the normal distribution as ν → ∞.
    25  //
    26  // For more information, see https://en.wikipedia.org/wiki/Student%27s_t-distribution,
    27  // specifically https://en.wikipedia.org/wiki/Student%27s_t-distribution#Non-standardized_Student.27s_t-distribution .
    28  //
    29  // The standard Student's T distribution is with Mu = 0, and Sigma = 1.
    30  type StudentsT struct {
    31  	// Mu is the location parameter of the distribution, and the mean of the
    32  	// distribution
    33  	Mu float64
    34  
    35  	// Sigma is the scale parameter of the distribution. It is related to the
    36  	// standard deviation by std = Sigma * sqrt(Nu/(Nu-2))
    37  	Sigma float64
    38  
    39  	// Nu is the shape parameter of the distribution, representing the number of
    40  	// degrees of the distribution, and one less than the number of observations
    41  	// from a Normal distribution.
    42  	Nu float64
    43  
    44  	Src rand.Source
    45  }
    46  
    47  // CDF computes the value of the cumulative distribution function at x.
    48  func (s StudentsT) CDF(x float64) float64 {
    49  	// transform to standard normal
    50  	y := (x - s.Mu) / s.Sigma
    51  	if y == 0 {
    52  		return 0.5
    53  	}
    54  	// For t > 0
    55  	// F(y) = 1 - 0.5 * I_t(y)(nu/2, 1/2)
    56  	// t(y) = nu/(y^2 + nu)
    57  	// and 1 - F(y) for t < 0
    58  	t := s.Nu / (y*y + s.Nu)
    59  	if y > 0 {
    60  		return 1 - 0.5*mathext.RegIncBeta(0.5*s.Nu, 0.5, t)
    61  	}
    62  	return 0.5 * mathext.RegIncBeta(s.Nu/2, 0.5, t)
    63  }
    64  
    65  // LogProb computes the natural logarithm of the value of the probability
    66  // density function at x.
    67  func (s StudentsT) LogProb(x float64) float64 {
    68  	g1, _ := math.Lgamma((s.Nu + 1) / 2)
    69  	g2, _ := math.Lgamma(s.Nu / 2)
    70  	z := (x - s.Mu) / s.Sigma
    71  	return g1 - g2 - 0.5*math.Log(s.Nu) - 0.5*logPi - math.Log(s.Sigma) - ((s.Nu+1)/2)*math.Log(1+z*z/s.Nu)
    72  }
    73  
    74  // Mean returns the mean of the probability distribution.
    75  func (s StudentsT) Mean() float64 {
    76  	return s.Mu
    77  }
    78  
    79  // Mode returns the mode of the distribution.
    80  func (s StudentsT) Mode() float64 {
    81  	return s.Mu
    82  }
    83  
    84  // NumParameters returns the number of parameters in the distribution.
    85  func (StudentsT) NumParameters() int {
    86  	return 3
    87  }
    88  
    89  // Prob computes the value of the probability density function at x.
    90  func (s StudentsT) Prob(x float64) float64 {
    91  	return math.Exp(s.LogProb(x))
    92  }
    93  
    94  // Quantile returns the inverse of the cumulative distribution function.
    95  func (s StudentsT) Quantile(p float64) float64 {
    96  	if p < 0 || p > 1 {
    97  		panic(badPercentile)
    98  	}
    99  	// F(x) = 1 - 0.5 * I_t(x)(nu/2, 1/2)
   100  	// t(x) = nu/(t^2 + nu)
   101  	if p == 0.5 {
   102  		return s.Mu
   103  	}
   104  	var y float64
   105  	if p > 0.5 {
   106  		// Know t > 0
   107  		t := mathext.InvRegIncBeta(s.Nu/2, 0.5, 2*(1-p))
   108  		y = math.Sqrt(s.Nu * (1 - t) / t)
   109  	} else {
   110  		t := mathext.InvRegIncBeta(s.Nu/2, 0.5, 2*p)
   111  		y = -math.Sqrt(s.Nu * (1 - t) / t)
   112  	}
   113  	// Convert out of standard normal
   114  	return y*s.Sigma + s.Mu
   115  }
   116  
   117  // Rand returns a random sample drawn from the distribution.
   118  func (s StudentsT) Rand() float64 {
   119  	// http://www.math.uah.edu/stat/special/Student.html
   120  	n := Normal{0, 1, s.Src}.Rand()
   121  	c := Gamma{s.Nu / 2, 0.5, s.Src}.Rand()
   122  	z := n / math.Sqrt(c/s.Nu)
   123  	return z*s.Sigma + s.Mu
   124  }
   125  
   126  // StdDev returns the standard deviation of the probability distribution.
   127  //
   128  // The standard deviation is undefined for ν <= 1, and this returns math.NaN().
   129  func (s StudentsT) StdDev() float64 {
   130  	return math.Sqrt(s.Variance())
   131  }
   132  
   133  // Survival returns the survival function (complementary CDF) at x.
   134  func (s StudentsT) Survival(x float64) float64 {
   135  	// transform to standard normal
   136  	y := (x - s.Mu) / s.Sigma
   137  	if y == 0 {
   138  		return 0.5
   139  	}
   140  	// For t > 0
   141  	// F(y) = 1 - 0.5 * I_t(y)(nu/2, 1/2)
   142  	// t(y) = nu/(y^2 + nu)
   143  	// and 1 - F(y) for t < 0
   144  	t := s.Nu / (y*y + s.Nu)
   145  	if y > 0 {
   146  		return 0.5 * mathext.RegIncBeta(s.Nu/2, 0.5, t)
   147  	}
   148  	return 1 - 0.5*mathext.RegIncBeta(s.Nu/2, 0.5, t)
   149  }
   150  
   151  // Variance returns the variance of the probability distribution.
   152  //
   153  // The variance is undefined for ν <= 1, and this returns math.NaN().
   154  func (s StudentsT) Variance() float64 {
   155  	if s.Nu <= 1 {
   156  		return math.NaN()
   157  	}
   158  	if s.Nu <= 2 {
   159  		return math.Inf(1)
   160  	}
   161  	return s.Sigma * s.Sigma * s.Nu / (s.Nu - 2)
   162  }