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.
- 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
|