rubystats 0.2.6 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,59 @@
1
+ require 'rubystats/probability_distribution'
2
+ require 'rubystats/normal_distribution'
3
+ # This class provides an object for encapsulating lognormal distributions
4
+ module Rubystats
5
+ class LognormalDistribution < Rubystats::ProbabilityDistribution
6
+ include Rubystats::SpecialMath
7
+
8
+ # Constructs a lognormal distribution.
9
+ def initialize(meanlog=0.0, sdlog=1.0)
10
+ raise "Argument Error: standard deviation for log-normal distribution must be positive." if sdlog < 0.0
11
+ @meanlog = meanlog.to_f
12
+ @sdlog = sdlog.to_f
13
+ @norm = Rubystats::NormalDistribution.new(@meanlog, @sdlog)
14
+ end
15
+
16
+ # Returns the mean of the distribution
17
+ def get_mean
18
+ return Math.exp(@meanlog + @sdlog**2 / 2.0)
19
+ end
20
+
21
+ # Returns the standard deviation of the distribution
22
+ def get_standard_deviation
23
+ return Math.sqrt(get_variance)
24
+ end
25
+
26
+ # Returns the variance of the distribution
27
+ def get_variance
28
+ return (Math.exp(@sdlog**2) - 1) * Math.exp(2.0 * @meanlog + @sdlog**2)
29
+ end
30
+
31
+ private
32
+
33
+ # Obtain single PDF value
34
+ # Returns the probability that a stochastic variable x has the value X,
35
+ # i.e. P(x=X)
36
+ def get_pdf(x)
37
+ raise "Argument Error: x must be greater than zero" if x <= 0.0
38
+ return 1.0/x.to_f * @norm.pdf(Math.log(x.to_f))
39
+ end
40
+
41
+ # Obtain single CDF value
42
+ # Returns the probability that a stochastic variable x is less than X,
43
+ # i.e. P(x<X)
44
+ def get_cdf(x)
45
+ return 0.5 + 0.5 * Math.erf((Math.log(x.to_f) - @meanlog) / (NumericalConstants::SQRT2 * @sdlog))
46
+ end
47
+
48
+ # Obtain single inverse CDF value.
49
+ # returns the value X for which P(x&lt;X).
50
+ def get_icdf(p)
51
+ raise "method 'get_icdf' not implemented for log-normal"
52
+ end
53
+
54
+ # returns single random number from log normal
55
+ def get_rng
56
+ return Math.exp(@norm.rng)
57
+ end
58
+ end
59
+ end
@@ -6,6 +6,12 @@ module Rubystats
6
6
  end
7
7
  end
8
8
 
9
+ module MakeDiscrete
10
+ def pmf(x)
11
+ pdf(x)
12
+ end
13
+ end
14
+
9
15
  module NumericalConstants
10
16
  MAX_FLOAT = 3.40282346638528860e292
11
17
  EPS = 2.22e-16
@@ -0,0 +1,73 @@
1
+ require 'rubystats/probability_distribution'
2
+ require 'rubystats/normal_distribution'
3
+ require 'matrix'
4
+
5
+ module Rubystats
6
+ module MultivariateDistribution
7
+ #override probability_distribution pdf function to work with multivariate input variables
8
+ def pdf(x)
9
+ get_pdf(x)
10
+ end
11
+ end
12
+ class MultivariateNormalDistribution < Rubystats::ProbabilityDistribution
13
+ include Rubystats::NumericalConstants
14
+ include Rubystats::MultivariateDistribution
15
+
16
+ def initialize(mu=[0.0,0.0],sigma=[[1.0,0.0],[0.0,1.0]])
17
+ raise "dimensions of mu vector and sigma matrix doesn't match" if mu.size != sigma.size
18
+ sigma.each{|row| raise "row dim of sigma does not match mu vector" if row.size != mu.size }
19
+
20
+ mu_f = mu.collect{|x| x.to_f }
21
+ sigma_f = sigma.collect{|row| row.collect{|x| x.to_f}}
22
+
23
+ @mu = Vector.elements(mu_f)
24
+ @sigma = Matrix.rows(sigma_f)
25
+ u, d, u_inv = @sigma.eigensystem
26
+ @sigma_inv = u * (1/d) * u_inv
27
+ @a = u * (d)**(0.5)
28
+
29
+ @pdf_factor = 1.0 / Math.sqrt((TWO_PI * @sigma).determinant.to_f)
30
+ @stdnorm = Rubystats::NormalDistribution.new(0.0,1.0)
31
+ end
32
+
33
+ private
34
+
35
+ def get_mean
36
+ @mu.to_a
37
+ end
38
+
39
+ def get_variance
40
+ raise "variance for multivariate normal distribution not implemented"
41
+ end
42
+
43
+ # Private method to obtain single PDF value.
44
+ # x should be greater than 0
45
+ # returns the probability that a stochastic variable x has the value X, i.e. P(x=X).
46
+ def get_pdf(x)
47
+ d = Vector.elements(x) - @mu
48
+ @pdf_factor * Math.exp(-0.5 * d.inner_product(@sigma_inv*d).to_f)
49
+ end
50
+
51
+ # Private method to obtain single CDF value.
52
+ # param x should be greater than 0
53
+ # return the probability that a stochastic variable x is less then X, i.e. P(x<X).
54
+ def get_cdf(x)
55
+ raise "cdf for multivariate normal distribution not implemented"
56
+ end
57
+
58
+ # Private method to obtain single inverse CDF value.
59
+ # return the value X for which P(x<X).
60
+ def get_icdf(p)
61
+ check_range(p)
62
+ raise "inverse cdf for multivariate normal distribution not implemented"
63
+ end
64
+
65
+ # Private method to obtain single RNG value.
66
+ def get_rng
67
+ z = Vector.elements(@mu.collect{ @stdnorm.rng })
68
+ (@mu + @a * z).to_a
69
+ end
70
+
71
+ end
72
+ end
73
+
@@ -11,11 +11,11 @@ module Rubystats
11
11
  # Constructs a normal distribution (defaults to zero mean and
12
12
  # unity variance).
13
13
  def initialize(mu=0.0, sigma=1.0)
14
- @mean = mu
14
+ @mean = mu.to_f
15
15
  if sigma <= 0.0
16
16
  raise "error, invalid sigma #{sigma}, should be > 0"
17
17
  end
18
- @stdev = sigma
18
+ @stdev = sigma.to_f
19
19
  @variance = sigma**2
20
20
  @pdf_denominator = SQRT2PI * Math.sqrt(@variance)
21
21
  @cdf_denominator = SQRT2 * Math.sqrt(@variance)
@@ -0,0 +1,78 @@
1
+ require 'rubystats/probability_distribution'
2
+
3
+ module Rubystats
4
+ class PoissonDistribution < Rubystats::ProbabilityDistribution
5
+ include Rubystats::MakeDiscrete
6
+
7
+ # Constructs a Poisson distribution
8
+ def initialize (rate)
9
+ if rate <= 0.0
10
+ raise ArgumentError.new("The rate for the Poisson distribution should be greater than zero.")
11
+ end
12
+ @rate = rate.to_f
13
+ end
14
+
15
+ #returns the mean
16
+ def get_mean
17
+ @rate
18
+ end
19
+
20
+ #returns the variance
21
+ def get_variance
22
+ @rate
23
+ end
24
+
25
+ # Private methods below
26
+
27
+ private
28
+
29
+ # Probability mass function of a Poisson distribution .
30
+ # k should be an integer
31
+ # returns the probability that a stochastic variable x has the value k,
32
+ # i.e. P(x = k)
33
+ def get_pdf(k)
34
+ raise ArgumentError.new("Poisson pdf: k needs to be >= 0") if k < 0
35
+ (@rate**k) * Math.exp(-@rate) / get_factorial(k).to_f
36
+ end
37
+
38
+ # Private shared function for getting cumulant for particular x
39
+ # param k should be integer-valued
40
+ # returns the probability that a stochastic variable x is less than _x
41
+ # i.e P(x < k)
42
+ def get_cdf(k)
43
+ raise ArgumentError.new("Poisson pdf: k needs to be >= 0") if k < 0
44
+ sum = 0.0
45
+ for i in (0 .. k)
46
+ sum = sum + get_pdf(i)
47
+ end
48
+ return sum
49
+ end
50
+
51
+ # Inverse of the cumulative Poisson distribution function
52
+ def get_icdf(prob)
53
+ check_range(prob)
54
+ sum = 0.0
55
+ k = 0
56
+ until prob <= sum
57
+ sum += get_pdf(k)
58
+ k += 1
59
+ end
60
+ return k - 1
61
+ end
62
+
63
+ # Private Poisson RNG function
64
+ # Poisson generator based upon the inversion by sequential search
65
+ def get_rng
66
+ x = 0
67
+ p = Math.exp(-@rate)
68
+ s = p
69
+ u = Kernel.rand
70
+ while u > s
71
+ x += 1
72
+ p *= @rate / x.to_f
73
+ s += p
74
+ end
75
+ x
76
+ end
77
+ end
78
+ end
@@ -122,15 +122,15 @@ module Rubystats
122
122
  def check_range(x, lo=0.0, hi=1.0)
123
123
  raise ArgumentError.new("x cannot be nil") if x.nil?
124
124
  if x < lo or x > hi
125
- raise ArgumentError.new("x must be less than lo (#{lo}) and greater than hi (#{hi})")
125
+ raise ArgumentError.new("x must be greater than lo (#{lo}) and less than hi (#{hi})")
126
126
  end
127
127
  end
128
128
 
129
129
  def get_factorial(n)
130
130
  if n <= 1
131
131
  return 1
132
- else
133
- return n * get_factorial(n-1)
132
+ else
133
+ return n.downto(1).reduce(:*)
134
134
  end
135
135
  end
136
136
 
@@ -0,0 +1,62 @@
1
+ require 'rubystats/probability_distribution'
2
+ require 'rubystats/normal_distribution'
3
+ # This class provides an object for encapsulating student t distributions
4
+ module Rubystats
5
+ class StudentTDistribution < Rubystats::ProbabilityDistribution
6
+ include Rubystats::SpecialMath
7
+
8
+ # Constructs a student t distribution.
9
+ def initialize(degree_of_freedom=1.0)
10
+ raise "Argument Error: degrees of freedom for student t distribution must be greater than zero." if degree_of_freedom <= 0.0
11
+ @dof = degree_of_freedom.to_f
12
+ @pdf_factor = Math.gamma((@dof + 1.0) / 2.0) / ( Math.sqrt(@dof * Math::PI) * Math.gamma(@dof / 2.0))
13
+ @stdnorm = Rubystats::NormalDistribution.new(0.0,1.0)
14
+ end
15
+
16
+ # Returns the mean of the distribution
17
+ def get_mean
18
+ (@dof > 1) ? 0.0 : Float::NAN
19
+ end
20
+
21
+ # Returns the standard deviation of the distribution
22
+ def get_standard_deviation
23
+ return Math.sqrt(get_variance)
24
+ end
25
+
26
+ # Returns the variance of the distribution
27
+ def get_variance
28
+ (@dof > 2.0) ? (@dof / (@dof - 2)) : Float::NAN
29
+ end
30
+
31
+ private
32
+
33
+ # Obtain single PDF value
34
+ # Returns the probability that a stochastic variable x has the value X,
35
+ # i.e. P(x=X)
36
+ def get_pdf(x)
37
+ return @pdf_factor * (1.0 + (x**2.0) / @dof)**(-(@dof+1.0)/2.0)
38
+ end
39
+
40
+ # Obtain single CDF value
41
+ # Returns the probability that a stochastic variable x is less than X,
42
+ # i.e. P(x<X)
43
+ def get_cdf(x)
44
+ raise "method 'cdf' not implemented for student t"
45
+ end
46
+
47
+ # Obtain single inverse CDF value.
48
+ # returns the value X for which P(x&lt;X).
49
+ def get_icdf(p)
50
+ raise "method 'icdf' not implemented for student t"
51
+ end
52
+
53
+ # returns single random number from the student t distribution
54
+ def get_rng
55
+ k = @dof.to_i
56
+ samples = []
57
+ k.times {|i| samples << @stdnorm.rng }
58
+ factor = 1.0 / Math.sqrt(samples.inject(0.0) {|sum,x| sum + x**2} / k)
59
+ return (factor * @stdnorm.rng)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,70 @@
1
+ require 'rubystats/probability_distribution'
2
+ # This class provides an object for encapsulating uniform distributions
3
+ module Rubystats
4
+ class UniformDistribution < Rubystats::ProbabilityDistribution
5
+ include Rubystats::SpecialMath
6
+
7
+ # Constructs a uniform distribution (defaults to zero lower and
8
+ # unity upper bound).
9
+ def initialize(lower=0.0, upper=1.0)
10
+ lower,upper = upper,lower if lower > upper
11
+ @lower = lower.to_f
12
+ @upper = upper.to_f
13
+ @pdf_denominator = 1.0 / (@upper - @lower)
14
+ @use_last = nil
15
+ end
16
+
17
+ # Returns the mean of the distribution
18
+ def get_mean
19
+ return 0.5*(@lower + @upper)
20
+ end
21
+
22
+ # Returns the standard deviation of the distribution
23
+ def get_standard_deviation
24
+ return Math.sqrt(get_variance)
25
+ end
26
+
27
+ # Returns the variance of the distribution
28
+ def get_variance
29
+ return 1.0/12.0 * (@upper-@lower)**2
30
+ end
31
+
32
+ private
33
+
34
+ # Obtain single PDF value
35
+ # Returns the probability that a stochastic variable x has the value X,
36
+ # i.e. P(x=X)
37
+ def get_pdf(x)
38
+ if x >= @lower && x <= @upper
39
+ @pdf_denominator
40
+ else
41
+ 0.0
42
+ end
43
+ end
44
+
45
+ # Obtain single CDF value
46
+ # Returns the probability that a stochastic variable x is less than X,
47
+ # i.e. P(x<X)
48
+ def get_cdf(x)
49
+ if x >= @lower && x < @upper
50
+ (x - @lower).fdiv(@upper - @lower)
51
+ elsif x >= @upper
52
+ 1.0
53
+ else
54
+ 0.0
55
+ end
56
+ end
57
+
58
+ # Obtain single inverse CDF value.
59
+ # returns the value X for which P(x&lt;X).
60
+ def get_icdf(p)
61
+ check_range(p)
62
+ return @lower + p.to_f * (@upper - @lower)
63
+ end
64
+
65
+ # returns single random number
66
+ def get_rng
67
+ return @lower + (@upper - @lower) * Kernel.rand
68
+ end
69
+ end
70
+ end
@@ -1,3 +1,3 @@
1
1
  module Rubystats
2
- VERSION = '0.2.6'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -0,0 +1,56 @@
1
+ require 'rubystats/probability_distribution'
2
+ module Rubystats
3
+ class WeibullDistribution < Rubystats::ProbabilityDistribution
4
+ include Rubystats::NumericalConstants
5
+
6
+ def initialize(scale=1.0, shape=1.0)
7
+ if scale <= 0.0
8
+ raise ArgumentError.new("Scale parameter should be greater than zero.")
9
+ end
10
+ if shape <= 0.0
11
+ raise ArgumentError.new("Shape parameter should be greater than zero.")
12
+ end
13
+ @scale = scale.to_f
14
+ @shape = shape.to_f
15
+ end
16
+
17
+ private
18
+
19
+ def get_mean
20
+ @scale * Math.gamma(1.0 + 1.0 / @shape)
21
+ end
22
+
23
+ def get_variance
24
+ @scale**2 * (Math.gamma(1.0 + 2.0 / @shape) - (Math.gamma(1.0 + 1.0 / @shape))**2)
25
+ end
26
+
27
+ # Private method to obtain single PDF value.
28
+ # x should be greater than or equal to 0.0
29
+ # returns the probability that a stochastic variable x has the value X, i.e. P(x=X).
30
+ def get_pdf(x)
31
+ check_range(x, 0.0, MAX_VALUE)
32
+ (@shape / @scale) * (x / @scale)**(@shape-1.0) * Math.exp(-1.0 * ((x/@scale)**@shape))
33
+ end
34
+
35
+ # Private method to obtain single CDF value.
36
+ # param x should be greater than 0
37
+ # return the probability that a stochastic variable x is less then X, i.e. P(x<X).
38
+ def get_cdf(x)
39
+ check_range(x,0.0,MAX_VALUE)
40
+ 1.0 - Math.exp(-1.0 * ((x.to_f/@scale)**@shape))
41
+ end
42
+
43
+ # Private method to obtain single inverse CDF value.
44
+ # return the value X for which P(x<X).
45
+ def get_icdf(p)
46
+ check_range(p)
47
+ @scale * (-1.0 * Math.log(1.0 - p.to_f))**(1.0 / @shape)
48
+ end
49
+
50
+ # Private method to obtain single RNG value.
51
+ def get_rng
52
+ self.icdf(Kernel.rand)
53
+ end
54
+
55
+ end
56
+ end