rubystats 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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