rubystats 0.2.6 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/README.rdoc +17 -6
- data/examples/uniform.rb +14 -0
- data/lib/rubystats.rb +29 -0
- data/lib/rubystats/beta_distribution.rb +4 -0
- data/lib/rubystats/binomial_distribution.rb +42 -131
- data/lib/rubystats/cauchy_distribution.rb +50 -0
- data/lib/rubystats/exponential_distribution.rb +2 -2
- data/lib/rubystats/gamma_distribution.rb +70 -0
- data/lib/rubystats/lognormal_distribution.rb +59 -0
- data/lib/rubystats/modules.rb +6 -0
- data/lib/rubystats/multivariate_normal_distribution.rb +73 -0
- data/lib/rubystats/normal_distribution.rb +2 -2
- data/lib/rubystats/poisson_distribution.rb +78 -0
- data/lib/rubystats/probability_distribution.rb +3 -3
- data/lib/rubystats/student_t_distribution.rb +62 -0
- data/lib/rubystats/uniform_distribution.rb +70 -0
- data/lib/rubystats/version.rb +1 -1
- data/lib/rubystats/weibull_distribution.rb +56 -0
- data/test/tc_beta.rb +21 -0
- data/test/tc_binomial.rb +14 -0
- data/test/tc_cauchy.rb +39 -0
- data/test/tc_exponential.rb +10 -0
- data/test/tc_gamma.rb +39 -0
- data/test/tc_lnorm.rb +45 -0
- data/test/tc_multivariate_normal.rb +53 -0
- data/test/tc_norm.rb +11 -1
- data/test/tc_poisson.rb +35 -0
- data/test/tc_studentt.rb +43 -0
- data/test/tc_unif.rb +48 -0
- data/test/tc_weibull.rb +51 -0
- metadata +27 -2
@@ -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<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
|
data/lib/rubystats/modules.rb
CHANGED
@@ -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
|
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
|
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<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<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
|
data/lib/rubystats/version.rb
CHANGED
@@ -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
|