rubystats 0.2.3 → 0.4.1
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 +7 -0
- data/.github/workflows/test.yml +21 -0
- data/.gitignore +20 -0
- data/Gemfile +3 -0
- data/History.txt +34 -0
- data/{README.txt → README.rdoc} +18 -7
- data/Rakefile +11 -44
- data/examples/uniform.rb +14 -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/fishers_exact_test.rb +9 -9
- data/lib/rubystats/gamma_distribution.rb +70 -0
- data/lib/rubystats/lognormal_distribution.rb +59 -0
- data/lib/rubystats/modules.rb +57 -48
- data/lib/rubystats/multivariate_normal_distribution.rb +73 -0
- data/lib/rubystats/normal_distribution.rb +4 -3
- data/lib/rubystats/poisson_distribution.rb +78 -0
- data/lib/rubystats/probability_distribution.rb +4 -4
- data/lib/rubystats/student_t_distribution.rb +62 -0
- data/lib/rubystats/uniform_distribution.rb +70 -0
- data/lib/rubystats/version.rb +3 -0
- data/lib/rubystats/weibull_distribution.rb +56 -0
- data/lib/rubystats.rb +39 -5
- data/rubystats.gemspec +28 -0
- data/test/tc_beta.rb +90 -69
- data/test/tc_binomial.rb +30 -16
- data/test/tc_cauchy.rb +39 -0
- data/test/tc_exponential.rb +13 -3
- data/test/tc_fisher.rb +15 -15
- 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 +35 -6
- data/test/tc_poisson.rb +35 -0
- data/test/tc_require_all.rb +5 -5
- data/test/tc_studentt.rb +43 -0
- data/test/tc_unif.rb +48 -0
- data/test/tc_weibull.rb +51 -0
- data/test/ts_stats.rb +1 -1
- metadata +115 -47
data/lib/rubystats/modules.rb
CHANGED
@@ -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
|
@@ -36,6 +42,9 @@ module Rubystats
|
|
36
42
|
|
37
43
|
include Rubystats::NumericalConstants
|
38
44
|
|
45
|
+
attr_reader :log_gamma_cache_res, :log_gamma_cache_x,
|
46
|
+
:log_beta_cache_res, :log_beta_cache_p, :log_beta_cache_q
|
47
|
+
|
39
48
|
@log_gamma_cache_res = 0.0
|
40
49
|
@log_gamma_cache_x = 0.0
|
41
50
|
@log_beta_cache_res = 0.0
|
@@ -43,7 +52,7 @@ module Rubystats
|
|
43
52
|
@log_beta_cache_q = 0.0
|
44
53
|
|
45
54
|
def log_beta(p,q)
|
46
|
-
if p !=
|
55
|
+
if p != log_beta_cache_p || q != log_beta_cache_q
|
47
56
|
@log_beta_cache_p = p
|
48
57
|
@log_beta_cache_q = q
|
49
58
|
if (p <= 0.0) || (q <= 0.0) || (p + q) > LOG_GAMMA_X_MAX_VALUE
|
@@ -52,7 +61,7 @@ module Rubystats
|
|
52
61
|
@log_beta_cache_res = log_gamma(p) + log_gamma(q) - log_gamma(p + q)
|
53
62
|
end
|
54
63
|
end
|
55
|
-
return
|
64
|
+
return log_beta_cache_res
|
56
65
|
end
|
57
66
|
|
58
67
|
# Gamma function.
|
@@ -84,7 +93,7 @@ module Rubystats
|
|
84
93
|
# </P>
|
85
94
|
# Author:: Jaco van Kooten
|
86
95
|
|
87
|
-
def orig_gamma(x)
|
96
|
+
def orig_gamma(x)
|
88
97
|
# Gamma related constants
|
89
98
|
g_p = [ -1.71618513886549492533811, 24.7656508055759199108314,
|
90
99
|
-379.804256470945635097577, 629.331155312818442661052,
|
@@ -93,7 +102,7 @@ module Rubystats
|
|
93
102
|
g_q = [-30.8402300119738975254353, 315.350626979604161529144,
|
94
103
|
-1015.15636749021914166146, -3107.77167157231109440444,
|
95
104
|
22538.1184209801510330112, 4755.84627752788110767815,
|
96
|
-
-134659.959864969306392456, -115132.259675553483497211 ]
|
105
|
+
-134659.959864969306392456, -115132.259675553483497211 ]
|
97
106
|
g_c = [-0.001910444077728, 8.4171387781295e-4, -5.952379913043012e-4,
|
98
107
|
7.93650793500350248e-4, -0.002777777777777681622553,
|
99
108
|
0.08333333333333333331554247, 0.0057083835261 ]
|
@@ -102,14 +111,14 @@ module Rubystats
|
|
102
111
|
n = 0
|
103
112
|
y = x
|
104
113
|
parity = false
|
105
|
-
if y <= 0.0
|
114
|
+
if y <= 0.0
|
106
115
|
# ----------------------------------------------------------------------
|
107
116
|
# Argument is negative
|
108
117
|
# ----------------------------------------------------------------------
|
109
118
|
y = -(x)
|
110
119
|
y1 = y.to_i
|
111
120
|
res = y - y1
|
112
|
-
if res != 0.0
|
121
|
+
if res != 0.0
|
113
122
|
if y1 != (((y1*0.5).to_i) * 2.0)
|
114
123
|
parity = true
|
115
124
|
fact = -M_pi/sin(M_pi * res)
|
@@ -141,7 +150,7 @@ module Rubystats
|
|
141
150
|
# ----------------------------------------------------------------------
|
142
151
|
z = y
|
143
152
|
y += 1
|
144
|
-
else
|
153
|
+
else
|
145
154
|
# ----------------------------------------------------------------------
|
146
155
|
# 1.0 .LT. argument .LT. 12.0, reduce argument if necessary
|
147
156
|
# ----------------------------------------------------------------------
|
@@ -154,7 +163,7 @@ module Rubystats
|
|
154
163
|
# ----------------------------------------------------------------------
|
155
164
|
xnum = 0.0
|
156
165
|
xden = 1.0
|
157
|
-
for i in (0...8)
|
166
|
+
for i in (0...8)
|
158
167
|
xnum = (xnum + g_p[i]) * z
|
159
168
|
xden = xden * z + g_q[i]
|
160
169
|
end
|
@@ -164,7 +173,7 @@ module Rubystats
|
|
164
173
|
# Adjust result for case 0.0 .LT. argument .LT. 1.0
|
165
174
|
# ----------------------------------------------------------------------
|
166
175
|
res /= y1
|
167
|
-
elsif y1 > y
|
176
|
+
elsif y1 > y
|
168
177
|
# ----------------------------------------------------------------------
|
169
178
|
# Adjust result for case 2.0 .LT. argument .LT. 12.0
|
170
179
|
# ----------------------------------------------------------------------
|
@@ -173,14 +182,14 @@ module Rubystats
|
|
173
182
|
y += 1
|
174
183
|
end
|
175
184
|
end
|
176
|
-
else
|
185
|
+
else
|
177
186
|
# ----------------------------------------------------------------------
|
178
187
|
# Evaluate for argument .GE. 12.0
|
179
188
|
# ----------------------------------------------------------------------
|
180
189
|
if y <= GAMMA_X_MAX_VALUE
|
181
190
|
ysq = y * y
|
182
191
|
sum = g_c[6]
|
183
|
-
for i in(0...6)
|
192
|
+
for i in(0...6)
|
184
193
|
sum = sum / ysq + g_c[i]
|
185
194
|
sum = sum / y - y + log(SQRT2PI)
|
186
195
|
sum += (y - 0.5) * log(y)
|
@@ -276,8 +285,8 @@ module Rubystats
|
|
276
285
|
lg_frtbig = 2.25e76
|
277
286
|
pnt68 = 0.6796875
|
278
287
|
|
279
|
-
if x ==
|
280
|
-
return
|
288
|
+
if x == log_gamma_cache_x
|
289
|
+
return log_gamma_cache_res
|
281
290
|
end
|
282
291
|
|
283
292
|
y = x
|
@@ -359,14 +368,14 @@ module Rubystats
|
|
359
368
|
|
360
369
|
|
361
370
|
# Incomplete Gamma function.
|
362
|
-
# The computation is based on approximations presented in
|
371
|
+
# The computation is based on approximations presented in
|
363
372
|
# Numerical Recipes, Chapter 6.2 (W.H. Press et al, 1992).
|
364
373
|
# @param a require a>=0
|
365
374
|
# @param x require x>=0
|
366
375
|
# @return 0 if x<0, a<=0 or a>2.55E305 to avoid errors and over/underflow
|
367
376
|
# @author Jaco van Kooten
|
368
377
|
|
369
|
-
def incomplete_gamma(a, x)
|
378
|
+
def incomplete_gamma(a, x)
|
370
379
|
if x <= 0.0 || a <= 0.0 || a > LOG_GAMMA_X_MAX_VALUE
|
371
380
|
return 0.0
|
372
381
|
elsif x < (a + 1.0)
|
@@ -381,7 +390,7 @@ module Rubystats
|
|
381
390
|
ap = a
|
382
391
|
del = 1.0 / a
|
383
392
|
sum = del
|
384
|
-
|
393
|
+
(1...MAX_ITERATIONS).each do
|
385
394
|
ap += 1
|
386
395
|
del *= x / ap
|
387
396
|
sum += del
|
@@ -393,14 +402,14 @@ module Rubystats
|
|
393
402
|
end
|
394
403
|
|
395
404
|
# Author:: Jaco van Kooten
|
396
|
-
def gamma_fraction(a, x)
|
405
|
+
def gamma_fraction(a, x)
|
397
406
|
b = x + 1.0 - a
|
398
407
|
c = 1.0 / XMININ
|
399
408
|
d = 1.0 / b
|
400
409
|
h = d
|
401
410
|
del= 0.0
|
402
411
|
an = 0.0
|
403
|
-
for i in (1...MAX_ITERATIONS)
|
412
|
+
for i in (1...MAX_ITERATIONS)
|
404
413
|
if (del-1.0).abs > PRECISION
|
405
414
|
an = -i * (i - a)
|
406
415
|
b += 2.0
|
@@ -424,10 +433,10 @@ module Rubystats
|
|
424
433
|
#
|
425
434
|
# Author:: Jaco van Kooten
|
426
435
|
|
427
|
-
def beta(p, q)
|
436
|
+
def beta(p, q)
|
428
437
|
if p <= 0.0 || q <= 0.0 || (p + q) > LOG_GAMMA_X_MAX_VALUE
|
429
438
|
return 0.0
|
430
|
-
else
|
439
|
+
else
|
431
440
|
return Math.exp(log_beta(p, q))
|
432
441
|
end
|
433
442
|
end
|
@@ -437,17 +446,17 @@ module Rubystats
|
|
437
446
|
# Author:: Jaco van Kooten
|
438
447
|
# Author:: Paul Meagher
|
439
448
|
#
|
440
|
-
# The computation is based on formulas from Numerical Recipes,
|
449
|
+
# The computation is based on formulas from Numerical Recipes,
|
441
450
|
# Chapter 6.4 (W.H. Press et al, 1992).
|
442
451
|
|
443
|
-
def incomplete_beta(x, p, q)
|
452
|
+
def incomplete_beta(x, p, q)
|
444
453
|
if x <= 0.0
|
445
454
|
return 0.0
|
446
455
|
elsif x >= 1.0
|
447
456
|
return 1.0
|
448
457
|
elsif (p <= 0.0) || (q <= 0.0) || (p + q) > LOG_GAMMA_X_MAX_VALUE
|
449
458
|
return 0.0
|
450
|
-
else
|
459
|
+
else
|
451
460
|
beta_gam = Math.exp( -log_beta(p, q) + p * Math.log(x) + q * Math.log(1.0 - x) )
|
452
461
|
if x < (p + 1.0) / (p + q + 2.0)
|
453
462
|
return beta_gam * beta_fraction(x, p, q) / p
|
@@ -462,13 +471,13 @@ module Rubystats
|
|
462
471
|
# Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
|
463
472
|
# Author:: Jaco van Kooten
|
464
473
|
|
465
|
-
def beta_fraction(x, p, q)
|
474
|
+
def beta_fraction(x, p, q)
|
466
475
|
c = 1.0
|
467
476
|
sum_pq = p + q
|
468
477
|
p_plus = p + 1.0
|
469
478
|
p_minus = p - 1.0
|
470
479
|
h = 1.0 - sum_pq * x / p_plus
|
471
|
-
if h.abs < XMININ
|
480
|
+
if h.abs < XMININ
|
472
481
|
h = XMININ
|
473
482
|
end
|
474
483
|
h = 1.0 / h
|
@@ -476,7 +485,7 @@ module Rubystats
|
|
476
485
|
m = 1
|
477
486
|
delta = 0.0
|
478
487
|
|
479
|
-
while (m <= MAX_ITERATIONS) && ((delta - 1.0).abs > PRECISION)
|
488
|
+
while (m <= MAX_ITERATIONS) && ((delta - 1.0).abs > PRECISION)
|
480
489
|
m2 = 2 * m
|
481
490
|
# even index for d
|
482
491
|
d = m * (q - m) * x / ( (p_minus + m2) * (p + m2))
|
@@ -563,14 +572,14 @@ module Rubystats
|
|
563
572
|
# where
|
564
573
|
# P1(s) = degree 6 poly in s
|
565
574
|
# Q1(s) = degree 6 poly in s
|
566
|
-
#
|
575
|
+
#
|
567
576
|
# 3. For x in [1.25,1/0.35(~2.857143)],
|
568
577
|
# erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1)
|
569
578
|
# erf(x) = 1 - erfc(x)
|
570
579
|
# where
|
571
580
|
# R1(z) = degree 7 poly in z, (z=1/x^2)
|
572
581
|
# S1(z) = degree 8 poly in z
|
573
|
-
#
|
582
|
+
#
|
574
583
|
# 4. For x in [1/0.35,28]
|
575
584
|
# erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0
|
576
585
|
# = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6<x<0
|
@@ -580,7 +589,7 @@ module Rubystats
|
|
580
589
|
# where
|
581
590
|
# R2(z) = degree 6 poly in z, (z=1/x^2)
|
582
591
|
# S2(z) = degree 7 poly in z
|
583
|
-
#
|
592
|
+
#
|
584
593
|
# Note1:
|
585
594
|
# To compute exp(-x*x-0.5625+R/S), let s be a single
|
586
595
|
# PRECISION number and s := x then
|
@@ -602,16 +611,16 @@ module Rubystats
|
|
602
611
|
# erf(x) = sign(x) *(1 - tiny) (raise inexact)
|
603
612
|
# erfc(x) = tiny*tiny (raise underflow) if x > 0
|
604
613
|
# = 2 - tiny if x<0
|
605
|
-
#
|
614
|
+
#
|
606
615
|
# 7. Special case:
|
607
616
|
# erf(0) = 0, erf(inf) = 1, erf(-inf) = -1,
|
608
617
|
# erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2,
|
609
618
|
# erfc/erf(NaN) is NaN
|
610
|
-
#
|
619
|
+
#
|
611
620
|
# $efx8 = 1.02703333676410069053e00
|
612
|
-
#
|
621
|
+
#
|
613
622
|
# Coefficients for approximation to erf on [0,0.84375]
|
614
|
-
#
|
623
|
+
#
|
615
624
|
|
616
625
|
# Error function.
|
617
626
|
# Based on C-code for the error function developed at Sun Microsystems.
|
@@ -654,7 +663,7 @@ module Rubystats
|
|
654
663
|
# 0 < |x| < 0.84375
|
655
664
|
if abs_x < 0.84375
|
656
665
|
#|x| < 2**-28
|
657
|
-
if abs_x < 3.7252902984619141e-9
|
666
|
+
if abs_x < 3.7252902984619141e-9
|
658
667
|
retval = abs_x + abs_x * e_efx
|
659
668
|
else
|
660
669
|
s = x * x
|
@@ -666,12 +675,12 @@ module Rubystats
|
|
666
675
|
end
|
667
676
|
elsif abs_x < 1.25
|
668
677
|
s = abs_x - 1.0
|
669
|
-
p = ePa[0] + s * (ePa[1] + s *
|
670
|
-
(ePa[2] + s * (ePa[3] + s *
|
678
|
+
p = ePa[0] + s * (ePa[1] + s *
|
679
|
+
(ePa[2] + s * (ePa[3] + s *
|
671
680
|
(ePa[4] + s * (ePa[5] + s * ePa[6])))))
|
672
681
|
|
673
|
-
q = 1.0 + s * (eQa[0] + s *
|
674
|
-
(eQa[1] + s * (eQa[2] + s *
|
682
|
+
q = 1.0 + s * (eQa[0] + s *
|
683
|
+
(eQa[1] + s * (eQa[2] + s *
|
675
684
|
(eQa[3] + s * (eQa[4] + s * eQa[5])))))
|
676
685
|
retval = e_erx + p / q
|
677
686
|
|
@@ -736,27 +745,27 @@ module Rubystats
|
|
736
745
|
else
|
737
746
|
s = 1.0/(abs_x * abs_x)
|
738
747
|
if abs_x < 2.8571428
|
739
|
-
r = eRa[0] + s * (eRa[1] + s *
|
740
|
-
(eRa[2] + s * (eRa[3] + s * (eRa[4] + s *
|
741
|
-
(eRa[5] + s *(eRa[6] + s * eRa[7])
|
748
|
+
r = eRa[0] + s * (eRa[1] + s *
|
749
|
+
(eRa[2] + s * (eRa[3] + s * (eRa[4] + s *
|
750
|
+
(eRa[5] + s * (eRa[6] + s * eRa[7])
|
742
751
|
)))))
|
743
752
|
|
744
|
-
s = 1.0 + s * (eSa[0] + s * (eSa[1] + s *
|
745
|
-
(eSa[2] + s * (eSa[3] + s * (eSa[4] + s *
|
753
|
+
s = 1.0 + s * (eSa[0] + s * (eSa[1] + s *
|
754
|
+
(eSa[2] + s * (eSa[3] + s * (eSa[4] + s *
|
746
755
|
(eSa[5] + s * (eSa[6] + s * eSa[7])))))))
|
747
756
|
|
748
757
|
else
|
749
|
-
r = eRb[0] + s * (eRb[1] + s *
|
750
|
-
(eRb[2] + s * (eRb[3] + s * (eRb[4] + s *
|
758
|
+
r = eRb[0] + s * (eRb[1] + s *
|
759
|
+
(eRb[2] + s * (eRb[3] + s * (eRb[4] + s *
|
751
760
|
(eRb[5] + s * eRb[6])))))
|
752
761
|
|
753
|
-
s = 1.0 + s * (eSb[0] + s *
|
754
|
-
(eSb[1] + s * (eSb[2] + s * (eSb[3] + s *
|
762
|
+
s = 1.0 + s * (eSb[0] + s *
|
763
|
+
(eSb[1] + s * (eSb[2] + s * (eSb[3] + s *
|
755
764
|
(eSb[4] + s * (eSb[5] + s * eSb[6]))))))
|
756
765
|
end
|
757
766
|
retval = Math.exp(-x * x - 0.5625 + r/s) / abs_x
|
758
767
|
end
|
759
768
|
return ( if x >= 0.0 then retval else 2.0 - retval end )
|
760
|
-
|
769
|
+
end
|
761
770
|
end
|
762
771
|
end
|
@@ -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,14 +11,15 @@ 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)
|
22
|
+
@use_last = nil
|
22
23
|
end
|
23
24
|
|
24
25
|
# Returns the mean of the distribution
|
@@ -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
|
|
@@ -154,7 +154,7 @@ module Rubystats
|
|
154
154
|
_pdf = pdf(x)
|
155
155
|
if _pdf != 0.0
|
156
156
|
dx = error / _pdf
|
157
|
-
x_new = x -dx
|
157
|
+
x_new = x - dx
|
158
158
|
end
|
159
159
|
# If the NR fails to converge (which for example may be the
|
160
160
|
# case if the initial guess is too rough) we apply a bisection
|
@@ -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
|