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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 594c482d4bcbf6954516475851e5d8361eb171e6
|
4
|
+
data.tar.gz: 89bb13f0241a4a42439099c37d4f2500f64b8519
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34a76aa78895d6fc605660d72b8e7602c1dd85150132b1bef2969c438cac22c1c91425cf1c3599b3202a43bc5a644444c31e30d9d181c8894cdfa2a70755da89
|
7
|
+
data.tar.gz: 12836502065954197f8c097060794f968d5845e9e9460c6ce175b91df04a569050f26a69855bee7803136661774c0aa39921aff2fe57f89b95de53ef4089782e
|
data/.travis.yml
CHANGED
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= Rubystats
|
2
2
|
|
3
|
-
*
|
3
|
+
* https://github.com/phillbaker/rubystats
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
@@ -37,19 +37,30 @@
|
|
37
37
|
This is beta-quality software. It works well according to my tests, but the API may change and other features may be added.
|
38
38
|
|
39
39
|
== FEATURES:
|
40
|
-
Classes for distributions:
|
40
|
+
Classes for continuous distributions:
|
41
41
|
|
42
|
-
* Normal
|
43
|
-
* Binomial
|
44
42
|
* Beta
|
43
|
+
* Cauchy
|
45
44
|
* Exponential
|
45
|
+
* Gamma
|
46
|
+
* Lognormal
|
47
|
+
* Multivariate Normal
|
48
|
+
* Normal
|
49
|
+
* Student t
|
50
|
+
* Uniform
|
51
|
+
* Weibull
|
52
|
+
|
53
|
+
Classes for discrete distributions:
|
54
|
+
|
55
|
+
* Binomial
|
56
|
+
* Poisson
|
46
57
|
|
47
58
|
Also includes Fisher's Exact Test
|
48
59
|
|
49
60
|
== SYNOPSIS:
|
50
61
|
=== Example: normal distribution with mean of 10 and standard deviation of 2
|
51
62
|
|
52
|
-
norm = Rubystats::NormalDistribution.new(10, 2)
|
63
|
+
norm = Rubystats::NormalDistribution.new(10.0, 2.0)
|
53
64
|
cdf = norm.cdf(11)
|
54
65
|
pdf = norm.pdf(11)
|
55
66
|
puts "CDF(11): #{cdf}"
|
@@ -57,7 +68,7 @@ Also includes Fisher's Exact Test
|
|
57
68
|
|
58
69
|
Output:
|
59
70
|
CDF(11): 0.691462461274013
|
60
|
-
PDF(11): 0.
|
71
|
+
PDF(11): 0.17603266338214973
|
61
72
|
|
62
73
|
=== Example: get some random numbers from a normal distribution
|
63
74
|
|
data/examples/uniform.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
require 'rubystats/uniform_distribution'
|
3
|
+
|
4
|
+
#uniform distribution with lower and upper bound of 0.0 and 1.0
|
5
|
+
unif = Rubystats::UniformDistribution.new(1.0, 6.0)
|
6
|
+
cdf = unif.cdf(2.5)
|
7
|
+
pdf = unif.pdf(2.5)
|
8
|
+
puts "CDF(2.5): #{cdf}"
|
9
|
+
puts "PDF(2.5): #{pdf}"
|
10
|
+
|
11
|
+
puts "Random numbers from the uniform distribution:"
|
12
|
+
10.times do
|
13
|
+
puts unif.rng
|
14
|
+
end
|
data/lib/rubystats.rb
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
require 'rubystats/normal_distribution'
|
2
2
|
require 'rubystats/binomial_distribution'
|
3
|
+
require 'rubystats/poisson_distribution'
|
3
4
|
require 'rubystats/beta_distribution'
|
4
5
|
require 'rubystats/fishers_exact_test'
|
5
6
|
require 'rubystats/exponential_distribution'
|
7
|
+
require 'rubystats/uniform_distribution'
|
8
|
+
require 'rubystats/lognormal_distribution'
|
9
|
+
require 'rubystats/student_t_distribution'
|
10
|
+
require 'rubystats/weibull_distribution'
|
11
|
+
require 'rubystats/cauchy_distribution'
|
12
|
+
require 'rubystats/gamma_distribution'
|
13
|
+
require 'rubystats/multivariate_normal_distribution'
|
6
14
|
require 'rubystats/version'
|
7
15
|
|
8
16
|
module Rubystats
|
@@ -10,6 +18,27 @@ end
|
|
10
18
|
|
11
19
|
NormalDistribution = Rubystats::NormalDistribution
|
12
20
|
BinomialDistribution = Rubystats::BinomialDistribution
|
21
|
+
PoissonDistribution = Rubystats::PoissonDistribution
|
13
22
|
BetaDistribution = Rubystats::BetaDistribution
|
14
23
|
FishersExactTest = Rubystats::FishersExactTest
|
15
24
|
ExponentialDistribution = Rubystats::ExponentialDistribution
|
25
|
+
UniformDistribution = Rubystats::UniformDistribution
|
26
|
+
LognormalDistribution = Rubystats::LognormalDistribution
|
27
|
+
StudentTDistribution = Rubystats::StudentTDistribution
|
28
|
+
WeibullDistribution = Rubystats::WeibullDistribution
|
29
|
+
CauchyDistribution = Rubystats::CauchyDistribution
|
30
|
+
GammaDistribution = Rubystats::GammaDistribution
|
31
|
+
MultivariateNormalDistribution = Rubystats::MultivariateNormalDistribution
|
32
|
+
|
33
|
+
#short-hand notation
|
34
|
+
Normal = Rubystats::NormalDistribution
|
35
|
+
Binomial = Rubystats::BinomialDistribution
|
36
|
+
Poisson = Rubystats::PoissonDistribution
|
37
|
+
Beta = Rubystats::BetaDistribution
|
38
|
+
Exponential = Rubystats::ExponentialDistribution
|
39
|
+
Uniform = Rubystats::UniformDistribution
|
40
|
+
Lognormal = Rubystats::LognormalDistribution
|
41
|
+
Weibull = Rubystats::WeibullDistribution
|
42
|
+
Cauchy = Rubystats::CauchyDistribution
|
43
|
+
Gamma = Rubystats::GammaDistribution
|
44
|
+
MultivariateNormal = Rubystats::MultivariateNormalDistribution
|
@@ -9,6 +9,7 @@ module Rubystats
|
|
9
9
|
include Rubystats::NumericalConstants
|
10
10
|
include Rubystats::SpecialMath
|
11
11
|
include Rubystats::ExtraMath
|
12
|
+
include Rubystats::MakeDiscrete
|
12
13
|
|
13
14
|
attr_reader :p, :n
|
14
15
|
attr_writer :p, :n
|
@@ -18,11 +19,11 @@ module Rubystats
|
|
18
19
|
if trials <= 0
|
19
20
|
raise ArgumentError.new("Error: trials must be greater than 0")
|
20
21
|
end
|
21
|
-
@n = trials
|
22
|
+
@n = trials.to_i
|
22
23
|
if prob < 0.0 || prob > 1.0
|
23
24
|
raise ArgumentError.new("prob must be between 0 and 1")
|
24
25
|
end
|
25
|
-
@p = prob
|
26
|
+
@p = prob.to_f
|
26
27
|
end
|
27
28
|
|
28
29
|
#returns the number of trials
|
@@ -45,79 +46,20 @@ module Rubystats
|
|
45
46
|
@n * @p * (1.0 - @p)
|
46
47
|
end
|
47
48
|
|
49
|
+
# Private methods below
|
50
|
+
|
51
|
+
private
|
52
|
+
|
48
53
|
# Probability density function of a binomial distribution (equivalent
|
49
54
|
# to R dbinom function).
|
50
55
|
# _x should be an integer
|
51
56
|
# returns the probability that a stochastic variable x has the value _x,
|
52
57
|
# i.e. P(x = _x)
|
53
|
-
def
|
54
|
-
|
55
|
-
|
56
|
-
for i in (0 ... _x.length)
|
57
|
-
check_range(_x[i], 0.0, @n)
|
58
|
-
pdf_vals[i] = binomial(@n, _x[i]) * (1-@p)**(@n-_x[i])
|
59
|
-
end
|
60
|
-
return pdf_vals
|
61
|
-
else
|
62
|
-
check_range(_x, 0.0, @n)
|
63
|
-
return binomial(@n, _x) * @p**_x * (1-@p)**(@n-_x)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
# Cumulative binomial distribution function (equivalent to R pbinom function).
|
68
|
-
# _x should be integer-valued and can be single integer or array of integers
|
69
|
-
# returns single value or array containing probability that a stochastic
|
70
|
-
# variable x is less then X, i.e. P(x < _x).
|
71
|
-
def cdf(_x)
|
72
|
-
if _x.class == Array
|
73
|
-
pdf_vals = []
|
74
|
-
for i in (0 ..._x.length)
|
75
|
-
pdf_vals[i] = get_cdf(_x[i])
|
76
|
-
end
|
77
|
-
return pdf_vals
|
78
|
-
else
|
79
|
-
return get_cdf(_x)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Inverse of the cumulative binomial distribution function
|
84
|
-
# (equivalent to R qbinom function).
|
85
|
-
# returns the value X for which P(x < _x).
|
86
|
-
def get_icdf(prob)
|
87
|
-
if prob.class == Array
|
88
|
-
inv_vals = []
|
89
|
-
for i in (0 ...prob.length)
|
90
|
-
check_range(prob[i])
|
91
|
-
inv_vals[i] = (find_root(prob[i], @n/2, 0.0, @n)).floor
|
92
|
-
end
|
93
|
-
return inv_vals
|
94
|
-
else
|
95
|
-
check_range(prob)
|
96
|
-
return (find_root(prob, @n/2, 0.0, @n)).floor
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# Wrapper for binomial RNG function (equivalent to R rbinom function).
|
101
|
-
# returns random deviate given trials and p
|
102
|
-
def rng(num_vals = 1)
|
103
|
-
if num_vals < 1
|
104
|
-
raise "Error num_vals must be greater than or equal to 1"
|
105
|
-
end
|
106
|
-
if num_vals == 1
|
107
|
-
return get_rng
|
108
|
-
else
|
109
|
-
rand_vals = []
|
110
|
-
for i in (0 ...num_vals)
|
111
|
-
rand_vals[i] = get_rng
|
112
|
-
end
|
113
|
-
return rand_vals
|
114
|
-
end
|
58
|
+
def get_pdf(x)
|
59
|
+
check_range(x, 0, @n)
|
60
|
+
binomial(@n, x) * @p**x * (1-@p)**(@n-x)
|
115
61
|
end
|
116
62
|
|
117
|
-
# Private methods below
|
118
|
-
|
119
|
-
private
|
120
|
-
|
121
63
|
# Private shared function for getting cumulant for particular x
|
122
64
|
# param _x should be integer-valued
|
123
65
|
# returns the probability that a stochastic variable x is less than _x
|
@@ -128,71 +70,40 @@ module Rubystats
|
|
128
70
|
for i in (0 .. _x)
|
129
71
|
sum = sum + pdf(i)
|
130
72
|
end
|
131
|
-
|
73
|
+
sum
|
132
74
|
end
|
133
75
|
|
134
|
-
#
|
135
|
-
#
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
# port conforms to the Press et al. copyright.
|
146
|
-
def get_rng
|
147
|
-
nold = -1
|
148
|
-
pold = -1
|
149
|
-
p = (if @p <= 0.5 then @p else 1.0 - @p end)
|
150
|
-
am = @n * p
|
151
|
-
if @n < 25
|
152
|
-
bnl = 0.0
|
153
|
-
(1...@n).each do
|
154
|
-
if Kernel.rand < p
|
155
|
-
bnl = bnl.next
|
156
|
-
end
|
157
|
-
end
|
158
|
-
elsif am < 1.0
|
159
|
-
g = Math.exp(-am)
|
160
|
-
t = 1.0
|
161
|
-
for j in (0 ... @n)
|
162
|
-
t = t * Kernel.rand
|
163
|
-
break if t < g
|
164
|
-
end
|
165
|
-
bnl = (if j <= @n then j else @n end)
|
166
|
-
else
|
167
|
-
if n != nold
|
168
|
-
en = @n
|
169
|
-
oldg = log_gamma(en + 1.0)
|
170
|
-
nold = n
|
171
|
-
end
|
172
|
-
if p != pold
|
173
|
-
pc = 1.0 - p
|
174
|
-
plog = Math.log(p)
|
175
|
-
pclog = Math.log(pc)
|
176
|
-
pold = p
|
177
|
-
end
|
178
|
-
sq = Math.sqrt(2.0 * am * pc)
|
179
|
-
until Kernel.rand <= t do
|
180
|
-
until (em >= 0.0 || em < (en + 1.0)) do
|
181
|
-
angle = Pi * Kernel.rand
|
182
|
-
y = Math.tan(angle)
|
183
|
-
em = sq * y + am
|
184
|
-
end
|
185
|
-
em = em.floor
|
186
|
-
t = 1.2 * sq * (1.0 + y * y) *
|
187
|
-
Math.exp(oldg - log_gamma(em + 1.0) -
|
188
|
-
log_gamma(en - em + 1.0) + em * plog + (en - em) * pclog)
|
189
|
-
end
|
190
|
-
bnl = em
|
191
|
-
end
|
192
|
-
if p != @p
|
193
|
-
bnl = @n - bnl
|
194
|
-
end
|
195
|
-
return bnl
|
76
|
+
# Inverse of the cumulative binomial distribution function
|
77
|
+
# returns the value X for which P(x < _x).
|
78
|
+
def get_icdf(prob)
|
79
|
+
check_range(prob)
|
80
|
+
sum = 0.0
|
81
|
+
k = 0
|
82
|
+
until prob <= sum
|
83
|
+
sum += get_pdf(k)
|
84
|
+
k += 1
|
85
|
+
end
|
86
|
+
k - 1
|
196
87
|
end
|
88
|
+
|
89
|
+
# Private binomial RNG function
|
90
|
+
# Variation of Luc Devroye's "Second Waiting Time Method"
|
91
|
+
# on page 522 of his text "Non-Uniform Random Variate Generation."
|
92
|
+
# There are faster methods based on acceptance/rejection techniques,
|
93
|
+
# but they are substantially more complex to implement.
|
94
|
+
def get_rng
|
95
|
+
p = (@p <= 0.5) ? @p : (1.0 - @p)
|
96
|
+
log_q = Math.log(1.0 - p)
|
97
|
+
sum = 0.0
|
98
|
+
k = 0
|
99
|
+
loop do
|
100
|
+
sum += Math.log(Kernel.rand) / (@n - k)
|
101
|
+
if (sum < log_q)
|
102
|
+
return (p != @p) ? (@n - k) : k
|
103
|
+
end
|
104
|
+
k += 1
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
197
108
|
end
|
198
109
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rubystats/probability_distribution'
|
2
|
+
module Rubystats
|
3
|
+
class CauchyDistribution < Rubystats::ProbabilityDistribution
|
4
|
+
|
5
|
+
def initialize(location=1.0,scale=1.0)
|
6
|
+
if scale <= 0.0
|
7
|
+
raise ArgumentError.new("Scale parameter in Cauchy distribution should be greater than zero.")
|
8
|
+
end
|
9
|
+
@location = location.to_f
|
10
|
+
@scale = scale.to_f
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def get_mean
|
16
|
+
Float::NAN
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_variance
|
20
|
+
Float::NAN
|
21
|
+
end
|
22
|
+
|
23
|
+
# Private method to obtain single PDF value.
|
24
|
+
# x should be greater than 0
|
25
|
+
# returns the probability that a stochastic variable x has the value X, i.e. P(x=X).
|
26
|
+
def get_pdf(x)
|
27
|
+
1.0 / (Math::PI * @scale * (1.0 + ((x - @location) / @scale)**2))
|
28
|
+
end
|
29
|
+
|
30
|
+
# Private method to obtain single CDF value.
|
31
|
+
# param x should be greater than 0
|
32
|
+
# return the probability that a stochastic variable x is less then X, i.e. P(x<X).
|
33
|
+
def get_cdf(x)
|
34
|
+
(1.0 / Math::PI) * Math.atan((x - @location) / @scale) + 0.5
|
35
|
+
end
|
36
|
+
|
37
|
+
# Private method to obtain single inverse CDF value.
|
38
|
+
# return the value X for which P(x<X).
|
39
|
+
def get_icdf(p)
|
40
|
+
check_range(p)
|
41
|
+
@location + @scale * Math.tan(Math::PI * (p - 0.5))
|
42
|
+
end
|
43
|
+
|
44
|
+
# Private method to obtain single RNG value.
|
45
|
+
def get_rng
|
46
|
+
self.icdf(Kernel.rand)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -10,11 +10,11 @@ module Rubystats
|
|
10
10
|
include Rubystats::SpecialMath
|
11
11
|
include Rubystats::ExtraMath
|
12
12
|
|
13
|
-
def initialize(decay=1)
|
13
|
+
def initialize(decay=1.0)
|
14
14
|
if decay < 0.0
|
15
15
|
raise ArgumentError.new("Decay parameter should be positive.")
|
16
16
|
end
|
17
|
-
@rate = decay
|
17
|
+
@rate = decay.to_f
|
18
18
|
end
|
19
19
|
|
20
20
|
private
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'rubystats/normal_distribution'
|
2
|
+
require 'rubystats/probability_distribution'
|
3
|
+
module Rubystats
|
4
|
+
class GammaDistribution < Rubystats::ProbabilityDistribution
|
5
|
+
include Rubystats::NumericalConstants
|
6
|
+
include Rubystats::SpecialMath
|
7
|
+
|
8
|
+
def initialize(shape=1.0, scale=1.0)
|
9
|
+
if shape <= 0.0 || scale <= 0.0
|
10
|
+
raise ArgumentError.new("Input parameter should be greater than zero.")
|
11
|
+
end
|
12
|
+
@shape = shape.to_f
|
13
|
+
@scale = scale.to_f
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def get_mean
|
19
|
+
@scale * @shape
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_variance
|
23
|
+
@shape * (@scale)**2
|
24
|
+
end
|
25
|
+
|
26
|
+
# Private method to obtain single PDF value.
|
27
|
+
# x should be greater than or equal to 0.0
|
28
|
+
# returns the probability that a stochastic variable x has the value X, i.e. P(x=X).
|
29
|
+
def get_pdf(x)
|
30
|
+
check_range(x, 0.0, MAX_VALUE)
|
31
|
+
1.0 / (Math.gamma(@shape) * (@scale**@shape)) * (x**(@shape-1.0)) * Math.exp(-1.0 * x / @scale)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Private method to obtain single CDF value.
|
35
|
+
# param x should be greater than 0
|
36
|
+
# return the probability that a stochastic variable x is less then X, i.e. P(x<X).
|
37
|
+
def get_cdf(x)
|
38
|
+
check_range(x,0.0,MAX_VALUE)
|
39
|
+
@scale * incomplete_gamma(@shape, x/@scale) / Math.gamma(@shape)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Private method to obtain single inverse CDF value.
|
43
|
+
# return the value X for which P(x<X).
|
44
|
+
def get_icdf(p)
|
45
|
+
check_range(p)
|
46
|
+
raise "Inverse CDF for gamma not implemented yet."
|
47
|
+
end
|
48
|
+
|
49
|
+
# Private method to obtain single RNG value.
|
50
|
+
# Generate gamma random variate with
|
51
|
+
# Marsaglia's squeeze method.
|
52
|
+
def get_rng
|
53
|
+
raise "Gamma RNG not working for shape < 1" if @shape < 1.0
|
54
|
+
norm = Rubystats::NormalDistribution.new(0,1)
|
55
|
+
d = @shape - 1.0 / 3.0
|
56
|
+
c = 1.0 / Math.sqrt(9.0 * d)
|
57
|
+
MAX_ITERATIONS.times do
|
58
|
+
x = norm.rng
|
59
|
+
v = (1.0 + c * x)**(3.0)
|
60
|
+
next if v <= 0.0
|
61
|
+
u = Kernel.rand
|
62
|
+
if (u < 1.0 - 0.03331 * (x**4)) || (Math.log(u) < 0.5 * x**2 + d * (1.0 - v + Math.log(v)))
|
63
|
+
return (d * v) * @scale
|
64
|
+
end
|
65
|
+
end
|
66
|
+
raise "Gamma RNG not converged after max_iterations = #{MAX_ITERATIONS}"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|