statistical 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/Gemfile +5 -1
- data/Rakefile +3 -2
- data/data/template/distribution.erb +2 -4
- data/data/template/rng.erb +8 -9
- data/data/template/spec.erb +45 -88
- data/lib/statistical.rb +6 -1
- data/lib/statistical/core_extensions.rb +44 -0
- data/lib/statistical/distribution.rb +4 -0
- data/lib/statistical/distribution/frechet.rb +125 -0
- data/lib/statistical/distribution/gumbel.rb +98 -0
- data/lib/statistical/distribution/laplace.rb +2 -4
- data/lib/statistical/distribution/uniform_discrete.rb +5 -1
- data/lib/statistical/distribution/weibull.rb +2 -4
- data/lib/statistical/helpers.rb +2 -2
- data/lib/statistical/rng.rb +4 -0
- data/lib/statistical/rng/frechet.rb +59 -0
- data/lib/statistical/rng/gumbel.rb +59 -0
- data/lib/statistical/version.rb +1 -1
- metadata +7 -3
- data/lib/core_extensions.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4dbc8862a8de1c1bd3f982635434403c1664683e
|
4
|
+
data.tar.gz: a1a52cffbb67236dcb224fe72634d8eb1434a4d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70b321f214492e41ba6b0f18be837404d2ba8d6b3687b21345825865c0e6d0ceead63c9b905fca095ab68980add4e3ae15405437e2afcb8fb9bdbd81e97c4c2e
|
7
|
+
data.tar.gz: ea3e9138092628d5d1551ad0d2eb8a26d6c929001c3a84a1d949a5afa1750a8ac2a00041794317b9c294396cae6bd4d7e7497ef799722f7fcfc12235f33e5d66
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
|
3
|
+
group :test do
|
4
|
+
gem 'codeclimate-test-reporter', require: nil
|
5
|
+
gem 'rspec-prof'
|
6
|
+
end
|
7
|
+
|
4
8
|
gem 'yard', require: false
|
5
9
|
|
6
10
|
# Other dependencies go in statistical.gemspec
|
data/Rakefile
CHANGED
@@ -10,6 +10,7 @@ require 'statistical/version'
|
|
10
10
|
|
11
11
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
12
12
|
t.verbose = false
|
13
|
+
t.rspec_opts = '-f p'
|
13
14
|
end
|
14
15
|
|
15
16
|
# Add a cop task for code linting
|
@@ -23,7 +24,7 @@ end
|
|
23
24
|
|
24
25
|
# Build the gem
|
25
26
|
task :build do
|
26
|
-
system
|
27
|
+
system 'gem build statistical.gemspec'
|
27
28
|
end
|
28
29
|
|
29
30
|
# Install gem locally
|
@@ -34,7 +35,7 @@ end
|
|
34
35
|
# Release gem to github
|
35
36
|
task :release => :build do
|
36
37
|
system "git tag -a v#{Statistical::VERSION} -m 'Version #{Statistical::VERSION}'"
|
37
|
-
system
|
38
|
+
system 'git push --tags'
|
38
39
|
system "gem push statistical-#{Statistical::VERSION}.gem"
|
39
40
|
end
|
40
41
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'statistical/
|
1
|
+
require 'statistical/helpers'
|
2
2
|
|
3
3
|
module Statistical
|
4
4
|
module Distribution
|
@@ -70,9 +70,7 @@ module Statistical
|
|
70
70
|
# @return [Boolean] true if-and-only-if two instances are of the same
|
71
71
|
# class and have the same parameters.
|
72
72
|
def eql?(other)
|
73
|
-
return
|
74
|
-
# Compare parameters and other stuff here
|
75
|
-
return true
|
73
|
+
return other.is_a?(self.class)
|
76
74
|
end
|
77
75
|
|
78
76
|
alias :== :eql?
|
data/data/template/rng.erb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
|
-
require 'statistical/exceptions'
|
2
1
|
require 'statistical/distribution/<%= distribution%>'
|
3
2
|
|
4
3
|
module Statistical
|
5
|
-
# Companion RNG class for the continuous
|
4
|
+
# Companion RNG class for the continuous <%= distribution.capitalize.camelcase %> distribution. Requires a
|
6
5
|
# distrbution object of the corresponding distribution
|
7
6
|
#
|
8
7
|
# @author Vaibhav Yenamandra
|
9
8
|
#
|
10
|
-
# @attr_reader [Numeric] lower The lower bound of the
|
11
|
-
# @attr_reader [Numeric] upper The upper bound of the
|
9
|
+
# @attr_reader [Numeric] lower The lower bound of the <%= distribution.capitalize.camelcase %> distribution.
|
10
|
+
# @attr_reader [Numeric] upper The upper bound of the <%= distribution.capitalize.camelcase %> distribution.
|
12
11
|
module Rng
|
13
12
|
class <%= distribution.capitalize.camelcase %>
|
14
13
|
attr_reader :generator, # other params go here
|
@@ -18,15 +17,16 @@ module Statistical
|
|
18
17
|
raise TypeError, "Expected Distribution object or nil, found #{dobj.class}"
|
19
18
|
end
|
20
19
|
dobj = Statistical::Distribution::<%= distribution.capitalize.camelcase %>.new if dobj.nil?
|
21
|
-
@generator =
|
20
|
+
@generator = Random.new(seed)
|
22
21
|
# Map other parameters here
|
23
22
|
@sdist = dobj
|
24
23
|
end
|
25
24
|
|
26
25
|
# Return the next random number from the sequence
|
27
26
|
#
|
28
|
-
# @return next random number in the sequence
|
27
|
+
# @return [Float] next random number in the sequence
|
29
28
|
def rand
|
29
|
+
@sdist.quantile(@generator.rand)
|
30
30
|
end
|
31
31
|
|
32
32
|
# Compare against another rng to see if they are the same
|
@@ -34,9 +34,8 @@ module Statistical
|
|
34
34
|
# @return true if and only if, source distributions are the same and the
|
35
35
|
# prng has the same initial state
|
36
36
|
def eql?(other)
|
37
|
-
return
|
38
|
-
|
39
|
-
@generator == other.generator
|
37
|
+
return other.is_a?(self.class) &&
|
38
|
+
@generator == other.generator
|
40
39
|
end
|
41
40
|
|
42
41
|
# Return the type of the source distribution
|
data/data/template/spec.erb
CHANGED
@@ -3,17 +3,11 @@ require 'statistical/rng/<%= distribution%>'
|
|
3
3
|
require 'statistical/distribution/<%= distribution %>'
|
4
4
|
|
5
5
|
describe Statistical::Rng::<%= distribution.capitalize.camelcase %> do
|
6
|
-
it 'passes the G-test at a 95% significance level' do
|
7
|
-
end
|
8
6
|
|
9
7
|
describe '.new' do
|
10
|
-
context 'when called with no arguments'
|
11
|
-
fail
|
12
|
-
end
|
8
|
+
context 'when called with no arguments'
|
13
9
|
|
14
|
-
context 'when parameters are specified'
|
15
|
-
fail
|
16
|
-
end
|
10
|
+
context 'when parameters are specified'
|
17
11
|
|
18
12
|
context 'when initialized with a seed' do
|
19
13
|
it 'should be repeatable for the same arguments' do
|
@@ -23,17 +17,11 @@ describe Statistical::Rng::<%= distribution.capitalize.camelcase %> do
|
|
23
17
|
end
|
24
18
|
|
25
19
|
describe '#rand' do
|
26
|
-
it '
|
27
|
-
fail
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'returns a bounded value when bounds are specified' do
|
31
|
-
fail
|
32
|
-
end
|
20
|
+
it 'passes the G-test at a 95% significance level'
|
33
21
|
end
|
34
22
|
|
35
23
|
describe '#==' do
|
36
|
-
context 'when compared against another
|
24
|
+
context 'when compared against another <%= distribution.capitalize.camelcase %> distribution' do
|
37
25
|
it 'should return true if the bounds and seed are the same' do
|
38
26
|
fail
|
39
27
|
end
|
@@ -42,101 +30,70 @@ describe Statistical::Rng::<%= distribution.capitalize.camelcase %> do
|
|
42
30
|
fail
|
43
31
|
end
|
44
32
|
end
|
45
|
-
|
46
|
-
context 'when compared against any other distribution class' do
|
47
|
-
it 'should return false if classes are different' do
|
48
|
-
fail
|
49
|
-
end
|
50
|
-
end
|
51
33
|
end
|
52
34
|
end
|
53
35
|
|
54
36
|
|
55
37
|
describe Statistical::Distribution::<%= distribution.capitalize.camelcase %> do
|
56
38
|
describe '.new' do
|
57
|
-
context 'when called with no arguments'
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
context 'when upper and lower bounds are specified' do
|
62
|
-
fail
|
63
|
-
end
|
39
|
+
context 'when called with no arguments'
|
40
|
+
|
41
|
+
context 'when upper and lower bounds are specified'
|
64
42
|
end
|
65
|
-
|
66
|
-
|
43
|
+
|
44
|
+
|
67
45
|
describe '#pdf' do
|
68
|
-
context 'when called with x < lower_bound'
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
context 'when called with x
|
73
|
-
fail
|
74
|
-
end
|
75
|
-
|
76
|
-
context 'when called with x in [lower_bound, upper_bound]' do
|
77
|
-
fail
|
78
|
-
end
|
46
|
+
context 'when called with x < lower_bound'
|
47
|
+
|
48
|
+
context 'when called with x > upper_bound'
|
49
|
+
|
50
|
+
context 'when called with x in [lower_bound, upper_bound]'
|
79
51
|
end
|
80
|
-
|
52
|
+
|
81
53
|
describe '#cdf' do
|
82
|
-
context 'when called with x < lower'
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
context 'when called with x
|
87
|
-
fail
|
88
|
-
end
|
89
|
-
|
90
|
-
context 'when called with x in [lower, upper]' do
|
91
|
-
fail
|
92
|
-
end
|
54
|
+
context 'when called with x < lower'
|
55
|
+
|
56
|
+
context 'when called with x > upper'
|
57
|
+
|
58
|
+
context 'when called with x in [lower, upper]'
|
93
59
|
end
|
94
|
-
|
60
|
+
|
95
61
|
describe '#quantile' do
|
96
|
-
context 'when called for x > 1' do
|
97
|
-
fail
|
98
|
-
end
|
99
|
-
|
100
62
|
context 'when called for x < 0' do
|
101
|
-
|
63
|
+
let(:<%= distribution[0] %>dist) {Statistical::Distribution::<%= distribution.capitalize.camelcase %>.new}
|
64
|
+
it {
|
65
|
+
expect {<%= distribution[0] %>dist.quantile(-Float::EPSILON)}.to raise_error(RangeError)
|
66
|
+
}
|
102
67
|
end
|
103
|
-
|
68
|
+
|
69
|
+
context 'when called for x > 1' do
|
70
|
+
let(:<%= distribution[0] %>dist) {Statistical::Distribution::<%= distribution.capitalize.camelcase %>.new}
|
71
|
+
it {
|
72
|
+
expect {<%= distribution[0] %>dist.quantile(1 + Float::EPSILON)}.to raise_error(RangeError)
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
104
76
|
context 'when called for x in [0, 1]' do
|
105
|
-
|
77
|
+
let(:<%= distribution[0] %>dist) {Statistical::Distribution::<%= distribution.capitalize.camelcase %>.new}
|
106
78
|
end
|
107
79
|
end
|
108
|
-
|
80
|
+
|
109
81
|
describe '#p_value' do
|
110
|
-
it 'should be the same as #quantile'
|
111
|
-
|
112
|
-
|
113
|
-
end
|
114
|
-
|
82
|
+
it 'should be the same as #quantile'
|
83
|
+
end
|
84
|
+
|
115
85
|
describe '#mean' do
|
116
|
-
it 'should return the correct mean'
|
117
|
-
fail
|
118
|
-
end
|
86
|
+
it 'should return the correct mean'
|
119
87
|
end
|
120
|
-
|
88
|
+
|
121
89
|
describe '#variance' do
|
122
|
-
it 'should return the correct variance'
|
123
|
-
fail
|
124
|
-
end
|
90
|
+
it 'should return the correct variance'
|
125
91
|
end
|
126
|
-
|
92
|
+
|
127
93
|
describe '#==' do
|
128
|
-
context 'when compared against another
|
129
|
-
it 'returns `true` if they have the same parameters'
|
130
|
-
|
131
|
-
end
|
132
|
-
|
133
|
-
it 'returns `false` if they have different parameters' do
|
134
|
-
fail
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
context 'when compared against any distribution type' do
|
139
|
-
fail
|
94
|
+
context 'when compared against another <%= distribution.capitalize.camelcase %> distribution' do
|
95
|
+
it 'returns `true` if they have the same parameters'
|
96
|
+
it 'returns `false` if they have different parameters'
|
140
97
|
end
|
141
98
|
end
|
142
99
|
end
|
data/lib/statistical.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'statistical/version'
|
2
2
|
require 'statistical/distribution'
|
3
3
|
require 'statistical/rng'
|
4
|
-
require 'core_extensions'
|
4
|
+
require 'statistical/core_extensions'
|
5
5
|
|
6
6
|
module Statistical
|
7
|
+
using Statistical::StringExtensions
|
8
|
+
using Statistical::ArrayExtensions
|
9
|
+
|
10
|
+
# Truncated Euler-Mascheroni constant
|
11
|
+
EULER_GAMMA = 0.5772156649015328
|
7
12
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# All core class modifications go here
|
2
|
+
module Statistical
|
3
|
+
# Module to contain all String refinements
|
4
|
+
module StringExtensions
|
5
|
+
refine String do
|
6
|
+
# Convert CamelCase to snake_case
|
7
|
+
def snakecase
|
8
|
+
gsub(/::/, '/')
|
9
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
10
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
11
|
+
.tr('-', '_')
|
12
|
+
.downcase
|
13
|
+
end
|
14
|
+
|
15
|
+
# convert snake_case to CamelCase
|
16
|
+
def camelcase
|
17
|
+
split('_').map(&:capitalize).join
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Module to contain all Array refinements
|
23
|
+
module ArrayExtensions
|
24
|
+
refine Array do
|
25
|
+
def sum
|
26
|
+
inject(:+)
|
27
|
+
end
|
28
|
+
|
29
|
+
def mean
|
30
|
+
sum.fdiv(count)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Population variance
|
34
|
+
def pvariance
|
35
|
+
map { |x| (x - mean)**2}.sum.fdiv(count)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sample variance
|
39
|
+
def svariance
|
40
|
+
map { |x| (x - mean)**2}.sum.fdiv(count - 1)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -5,11 +5,15 @@ require 'statistical/distribution/bernoulli'
|
|
5
5
|
require 'statistical/distribution/exponential'
|
6
6
|
require 'statistical/distribution/laplace'
|
7
7
|
require 'statistical/distribution/weibull'
|
8
|
+
require 'statistical/distribution/frechet'
|
9
|
+
require 'statistical/distribution/gumbel'
|
8
10
|
|
9
11
|
module Statistical
|
10
12
|
# Factory module used to create instances of various distributions classes
|
11
13
|
# nested under itself
|
12
14
|
module Distribution
|
15
|
+
using Statistical::StringExtensions
|
16
|
+
|
13
17
|
# @private
|
14
18
|
# No need to document this
|
15
19
|
# Dynamically add constants when called
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'statistical/helpers'
|
2
|
+
|
3
|
+
module Statistical
|
4
|
+
module Distribution
|
5
|
+
# This class models the Frechet (or inverse Weibull) distribution which is
|
6
|
+
# a type-2 extreme value distribution.
|
7
|
+
#
|
8
|
+
# @author Vaibhav Yenamandra
|
9
|
+
# @attr_reader [Numeric] alpha Mandatory shape parameter of the distribution
|
10
|
+
# @attr_reader [Numeric] location Optional location parameter
|
11
|
+
# @attr_reader [Numeric] scale Optional scale parameter
|
12
|
+
# @attr_reader [Numeric] support Support / domain of the distribution's
|
13
|
+
# PDF / CDF
|
14
|
+
class Frechet
|
15
|
+
attr_reader :alpha, :location, :scale, :support
|
16
|
+
|
17
|
+
# Returns a new `Statistical::Distribution::Frechet` instance
|
18
|
+
#
|
19
|
+
# @param [Numeric] alpha Mandatory shape parameter of the distribution
|
20
|
+
# @param [Numeric] location Optional location parameter
|
21
|
+
# @param [Numeric] scale Optional scale parameter
|
22
|
+
# @return [Frechet] `Statistical::Distribution::Frechet` instance
|
23
|
+
def initialize(alpha = nil, location = 0, scale = 1)
|
24
|
+
raise ArgumentError if alpha.nil?
|
25
|
+
|
26
|
+
@alpha = alpha.to_f
|
27
|
+
@location = location.to_f
|
28
|
+
@scale = scale.to_f
|
29
|
+
@support = Domain[@location, Float::INFINITY, :full_open]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns value of probability density function at a point.
|
33
|
+
#
|
34
|
+
# @param [Numeric] x A real valued point
|
35
|
+
# @return [Float] Probability density function evaluated at `x`
|
36
|
+
def pdf(x)
|
37
|
+
xs = (x - @location) / @scale
|
38
|
+
|
39
|
+
# Not following the usual array lookup idiom here since the PDF
|
40
|
+
# expression is ill-defined (Complex) for x < location. Lazily evaluated
|
41
|
+
# expressions would help retain the array lookup idiom, but that's for
|
42
|
+
# later
|
43
|
+
if (@support <=> x).zero?
|
44
|
+
return (@alpha / @scale) * xs**(-1 - @alpha) * Math.exp(-(xs**-@alpha))
|
45
|
+
else
|
46
|
+
return 0
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns value of cumulative density function upto the specified point
|
51
|
+
#
|
52
|
+
# @param [Numeric] x A real valued point
|
53
|
+
# @return [Float] Cumulative density function evaluated for F(u <= x)
|
54
|
+
def cdf(x)
|
55
|
+
xs = (x - @location) / @scale
|
56
|
+
|
57
|
+
case @support <=> x
|
58
|
+
when 0
|
59
|
+
return Math.exp(-(xs**-@alpha))
|
60
|
+
when 1
|
61
|
+
return 1
|
62
|
+
when -1
|
63
|
+
return 0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns value of inverse CDF for a given probability
|
68
|
+
#
|
69
|
+
# @see #p_value
|
70
|
+
#
|
71
|
+
# @param [Numeric] p a value within [0, 1]
|
72
|
+
# @return [Float] Inverse CDF for valid p
|
73
|
+
# @raise [RangeError] if p > 1 or p < 0
|
74
|
+
def quantile(p)
|
75
|
+
raise RangeError, "`p` must be in [0, 1], found: #{p}" if p < 0 || p > 1
|
76
|
+
return @location + @scale * ((-Math.log(p))**(-1 / @alpha))
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the mean value for the calling instance. Calculated mean, and
|
80
|
+
# not inferred from simulations
|
81
|
+
#
|
82
|
+
# @return Mean of the distribution
|
83
|
+
def mean
|
84
|
+
return [
|
85
|
+
Float::INFINITY,
|
86
|
+
@location + @scale * Math.gamma(1 - 1 / @alpha),
|
87
|
+
Float::INFINITY
|
88
|
+
][@alpha <=> 1.0]
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns the expected value of variance for the calling instance.
|
92
|
+
#
|
93
|
+
# @return Variance of the distribution
|
94
|
+
def variance
|
95
|
+
return [
|
96
|
+
Float::INFINITY,
|
97
|
+
@scale * @scale * (
|
98
|
+
Math.gamma(1 - 2 / @alpha) -
|
99
|
+
Math.gamma(1 - 1 / @alpha)**2
|
100
|
+
),
|
101
|
+
Float::INFINITY
|
102
|
+
][@alpha <=> 2.0]
|
103
|
+
end
|
104
|
+
|
105
|
+
# Compares two distribution instances and returns a boolean outcome
|
106
|
+
# Available publicly as #==
|
107
|
+
#
|
108
|
+
# @private
|
109
|
+
#
|
110
|
+
# @return [Boolean] true if-and-only-if two instances are of the same
|
111
|
+
# class and have the same parameters.
|
112
|
+
def eql?(other)
|
113
|
+
return other.is_a?(self.class) &&
|
114
|
+
@alpha == other.alpha &&
|
115
|
+
@location == other.location &&
|
116
|
+
@scale == other.scale
|
117
|
+
end
|
118
|
+
|
119
|
+
alias :== :eql?
|
120
|
+
alias :p_value :quantile
|
121
|
+
|
122
|
+
private :eql?
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'statistical/helpers'
|
2
|
+
|
3
|
+
module Statistical
|
4
|
+
module Distribution
|
5
|
+
# Distribution class for Extreme value type-3 or Gumbel distribution
|
6
|
+
#
|
7
|
+
# @author Vaibhav Yenamandra
|
8
|
+
#
|
9
|
+
# @attr_reader location [Float] The distributions location parameter
|
10
|
+
# @attr_reader scale [Float] The dstribution's scale parameter
|
11
|
+
# @attr_reader support [Float] The region of the real line where this
|
12
|
+
# distribution is defined to exist
|
13
|
+
class Gumbel
|
14
|
+
attr_reader :location, :scale, :support
|
15
|
+
|
16
|
+
# Returns a new `Statistical::Distribution::Gumbel` instance
|
17
|
+
#
|
18
|
+
# @param [Types] location Description
|
19
|
+
# @return `Statistical::Distribution::Gumbel` instance
|
20
|
+
def initialize(location = 0, scale = 1)
|
21
|
+
@location = location.to_f
|
22
|
+
@scale = scale.to_f
|
23
|
+
@support = Domain[-Float::INFINITY, Float::INFINITY, :full_open]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns value of probability density function at a point.
|
27
|
+
#
|
28
|
+
# @param [Numeric] x A real valued point
|
29
|
+
# @return Probability density function evaluated at `x`
|
30
|
+
def pdf(x)
|
31
|
+
xa = (x - @location) / @scale
|
32
|
+
return [
|
33
|
+
Math.exp(-xa - Math.exp(-xa)) / @scale,
|
34
|
+
0.0,
|
35
|
+
0.0
|
36
|
+
][@support <=> x]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns value of cumulative density function upto a point.
|
40
|
+
#
|
41
|
+
# @param [Numeric] x A real valued point
|
42
|
+
# @return Cumulative density function evaluated for F(u <= x)
|
43
|
+
def cdf(x)
|
44
|
+
xa = (x - @location) / @scale
|
45
|
+
return [
|
46
|
+
Math.exp(-Math.exp(-xa)),
|
47
|
+
1.0,
|
48
|
+
0.0
|
49
|
+
][@support <=> x]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns value of inverse CDF for a given probability
|
53
|
+
#
|
54
|
+
# @see #p_value
|
55
|
+
#
|
56
|
+
# @param [Numeric] p a value within [0, 1]
|
57
|
+
# @return Inverse CDF for valid p
|
58
|
+
# @raise [RangeError] if p > 1 or p < 0
|
59
|
+
def quantile(p)
|
60
|
+
raise RangeError, "`p` must be in [0, 1], found: #{p}" if p < 0 || p > 1
|
61
|
+
return @location - @scale * Math.log(-Math.log(p))
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the expected value of the mean for the calling instance.
|
65
|
+
#
|
66
|
+
# @return Mean of the distribution
|
67
|
+
def mean
|
68
|
+
return @location + @scale * Statistical::EULER_GAMMA
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the expected value of variance for the calling instance.
|
72
|
+
#
|
73
|
+
# @return Variance of the distribution
|
74
|
+
def variance
|
75
|
+
return ((Math::PI * @scale)**2) / 6
|
76
|
+
end
|
77
|
+
|
78
|
+
# Compares two distribution instances and returns a boolean outcome
|
79
|
+
# Available publicly as #==
|
80
|
+
#
|
81
|
+
# @private
|
82
|
+
#
|
83
|
+
# @param other A distribution object (preferred)
|
84
|
+
# @return [Boolean] true if-and-only-if two instances are of the same
|
85
|
+
# class and have the same parameters.
|
86
|
+
def eql?(other)
|
87
|
+
return other.is_a?(self.class) &&
|
88
|
+
@location == other.location &&
|
89
|
+
@scale == other.scale
|
90
|
+
end
|
91
|
+
|
92
|
+
alias :== :eql?
|
93
|
+
alias :p_value :quantile
|
94
|
+
|
95
|
+
private :eql?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -43,8 +43,7 @@ module Statistical
|
|
43
43
|
def cdf(x)
|
44
44
|
return [0.5,
|
45
45
|
1.0 - 0.5 * Math.exp((@location - x).fdiv(@scale)),
|
46
|
-
0.5 * Math.exp((x - @location).fdiv(@scale))
|
47
|
-
][x <=> @location]
|
46
|
+
0.5 * Math.exp((x - @location).fdiv(@scale))][x <=> @location]
|
48
47
|
end
|
49
48
|
|
50
49
|
# Returns value of inverse CDF for a given probability
|
@@ -59,8 +58,7 @@ module Statistical
|
|
59
58
|
|
60
59
|
return [@location,
|
61
60
|
@location - @scale * Math.log(2 * (1.0 - p)),
|
62
|
-
@scale * Math.log(2 * p) + @location
|
63
|
-
][p <=> 0.5]
|
61
|
+
@scale * Math.log(2 * p) + @location][p <=> 0.5]
|
64
62
|
end
|
65
63
|
|
66
64
|
# Returns the expected mean value for the calling instance.
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'statistical/core_extensions'
|
2
|
+
|
1
3
|
module Statistical
|
2
4
|
# Module to collect all abstractions of distributions
|
3
5
|
module Distribution
|
@@ -8,6 +10,8 @@ module Statistical
|
|
8
10
|
# @attr_reader [Array, Numeric] support The support set of valid values a
|
9
11
|
# random variate from the distribution can take. Must have at least 1 value
|
10
12
|
class UniformDiscrete
|
13
|
+
using Statistical::ArrayExtensions
|
14
|
+
|
11
15
|
attr_reader :count, :support, :lower, :upper
|
12
16
|
# Returns a model for the discrete uniform distribution on all elements
|
13
17
|
# present in the given set of elemets `elems`
|
@@ -108,7 +112,7 @@ module Statistical
|
|
108
112
|
#
|
109
113
|
# @return [Float] Variance of the distribution
|
110
114
|
def variance
|
111
|
-
return @support.
|
115
|
+
return @support.pvariance
|
112
116
|
end
|
113
117
|
|
114
118
|
# Compares two distribution instances and returns a boolean
|
@@ -33,8 +33,7 @@ module Statistical
|
|
33
33
|
return [(@shape / @scale) * ((x / @scale)**(@shape - 1)) *
|
34
34
|
Math.exp(-((x / @scale)**@shape)),
|
35
35
|
0.0,
|
36
|
-
0.0
|
37
|
-
][@support <=> x]
|
36
|
+
0.0][@support <=> x]
|
38
37
|
end
|
39
38
|
|
40
39
|
# Returns value of cumulative density function at a point. Calculated
|
@@ -45,8 +44,7 @@ module Statistical
|
|
45
44
|
def cdf(x)
|
46
45
|
return [1 - Math.exp(-((x / @scale)**@shape)),
|
47
46
|
1.0,
|
48
|
-
0.0
|
49
|
-
][@support <=> x]
|
47
|
+
0.0][@support <=> x]
|
50
48
|
end
|
51
49
|
|
52
50
|
# Returns value of inverse CDF for a given probability
|
data/lib/statistical/helpers.rb
CHANGED
@@ -83,9 +83,9 @@ module Statistical
|
|
83
83
|
@exclusions.each do |e|
|
84
84
|
case e
|
85
85
|
when Fixnum, Bignum, Float
|
86
|
-
has_val = has_val || (e == val)
|
86
|
+
has_val = (has_val || (e == val))
|
87
87
|
when Range
|
88
|
-
has_val
|
88
|
+
has_val = (has_val || e.include?(val))
|
89
89
|
end
|
90
90
|
end
|
91
91
|
return has_val
|
data/lib/statistical/rng.rb
CHANGED
@@ -5,11 +5,15 @@ require 'statistical/rng/bernoulli'
|
|
5
5
|
require 'statistical/rng/exponential'
|
6
6
|
require 'statistical/rng/laplace'
|
7
7
|
require 'statistical/rng/weibull'
|
8
|
+
require 'statistical/rng/frechet'
|
9
|
+
require 'statistical/rng/gumbel'
|
8
10
|
|
9
11
|
module Statistical
|
10
12
|
# Factory module to create instances of the various classes
|
11
13
|
# nested under itself
|
12
14
|
module Rng
|
15
|
+
using Statistical::StringExtensions
|
16
|
+
|
13
17
|
# @private
|
14
18
|
# No need to document this
|
15
19
|
# Dynamically add constants when called
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'statistical/distribution/frechet'
|
2
|
+
|
3
|
+
module Statistical
|
4
|
+
# Companion RNG class for the continuous uniform distribution. Requires a
|
5
|
+
# distrbution object of the corresponding distribution
|
6
|
+
#
|
7
|
+
# @author Vaibhav Yenamandra
|
8
|
+
#
|
9
|
+
# @attr_reader [Numeric] lower The lower bound of the uniform distribution.
|
10
|
+
# @attr_reader [Numeric] upper The upper bound of the uniform distribution.
|
11
|
+
module Rng
|
12
|
+
class Frechet
|
13
|
+
attr_reader :generator, :alpha, :location, :scale
|
14
|
+
|
15
|
+
def initialize(dobj = nil, seed = Random.new_seed)
|
16
|
+
if dobj.nil?
|
17
|
+
raise ArgumentError, 'Need a alpha-parametrized Frechet object!'
|
18
|
+
elsif !dobj.is_a?(Statistical::Distribution::Frechet)
|
19
|
+
raise TypeError, "Expected Frechet Distribution found #{dobj.class}"
|
20
|
+
end
|
21
|
+
|
22
|
+
@generator = Random.new(seed)
|
23
|
+
@alpha = dobj.alpha
|
24
|
+
@location = dobj.location
|
25
|
+
@scale = dobj.scale
|
26
|
+
@sdist = dobj
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return the next random number from the sequence
|
30
|
+
#
|
31
|
+
# @return [Float] next random number in the sequence
|
32
|
+
def rand
|
33
|
+
return @sdist.quantile(@generator.rand)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Compare against another rng to see if they are the same
|
37
|
+
#
|
38
|
+
# @return true if and only if, source distributions are the same and the
|
39
|
+
# prng has the same initial state
|
40
|
+
def eql?(other)
|
41
|
+
return other.is_a?(self.class) &&
|
42
|
+
@generator == other.generator &&
|
43
|
+
@alpha == other.alpha &&
|
44
|
+
@location == other.location &&
|
45
|
+
@scale == other.scale
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the type of the source distribution
|
49
|
+
#
|
50
|
+
# @return source distribution's type
|
51
|
+
def type
|
52
|
+
@sdist.class
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method :==, :eql?
|
56
|
+
private :eql?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'statistical/distribution/gumbel'
|
2
|
+
|
3
|
+
module Statistical
|
4
|
+
# Companion RNG class for the continuous Gumbel distribution. Requires a
|
5
|
+
# distrbution object of the corresponding distribution
|
6
|
+
#
|
7
|
+
# @author Vaibhav Yenamandra
|
8
|
+
#
|
9
|
+
# @attr_reader [Float] location The location parameter of the Gumbel
|
10
|
+
# distribution
|
11
|
+
# @attr_reader [Float] scale The scale parameter of the Gumbel distribution
|
12
|
+
# @attr_reader [Float] generator The underlying uniform variate source used
|
13
|
+
# to power `Gumbel#rand`
|
14
|
+
module Rng
|
15
|
+
class Gumbel
|
16
|
+
attr_reader :generator, :location, :scale
|
17
|
+
|
18
|
+
def initialize(dobj = nil, seed = Random.new_seed)
|
19
|
+
unless dobj.nil? || dobj.is_a?(Statistical::Distribution::Gumbel)
|
20
|
+
raise TypeError,
|
21
|
+
"Expected Distribution object or nil, found #{dobj.class}"
|
22
|
+
end
|
23
|
+
dobj = Statistical::Distribution::Gumbel.new if dobj.nil?
|
24
|
+
@generator = Random.new(seed)
|
25
|
+
@location = dobj.location
|
26
|
+
@scale = dobj.scale
|
27
|
+
@sdist = dobj
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the next random number from the sequence
|
31
|
+
#
|
32
|
+
# @return [Float] next random number in the sequence
|
33
|
+
def rand
|
34
|
+
@sdist.quantile(@generator.rand)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Compare against another rng to see if they are the same
|
38
|
+
#
|
39
|
+
# @return true if and only if, source distributions are the same and the
|
40
|
+
# prng has the same initial state
|
41
|
+
def eql?(other)
|
42
|
+
return other.is_a?(self.class) &&
|
43
|
+
@generator == other.generator &&
|
44
|
+
@location == other.location &&
|
45
|
+
@scale == other.scale
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the type of the source distribution
|
49
|
+
#
|
50
|
+
# @return source distribution's type
|
51
|
+
def type
|
52
|
+
@sdist.class
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method :==, :eql?
|
56
|
+
private :eql?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/statistical/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: statistical
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vaibhav Yenamandra
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
11
|
+
date: 2016-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -116,11 +116,13 @@ files:
|
|
116
116
|
- data/template/distribution.erb
|
117
117
|
- data/template/rng.erb
|
118
118
|
- data/template/spec.erb
|
119
|
-
- lib/core_extensions.rb
|
120
119
|
- lib/statistical.rb
|
120
|
+
- lib/statistical/core_extensions.rb
|
121
121
|
- lib/statistical/distribution.rb
|
122
122
|
- lib/statistical/distribution/bernoulli.rb
|
123
123
|
- lib/statistical/distribution/exponential.rb
|
124
|
+
- lib/statistical/distribution/frechet.rb
|
125
|
+
- lib/statistical/distribution/gumbel.rb
|
124
126
|
- lib/statistical/distribution/laplace.rb
|
125
127
|
- lib/statistical/distribution/two_point.rb
|
126
128
|
- lib/statistical/distribution/uniform.rb
|
@@ -130,6 +132,8 @@ files:
|
|
130
132
|
- lib/statistical/rng.rb
|
131
133
|
- lib/statistical/rng/bernoulli.rb
|
132
134
|
- lib/statistical/rng/exponential.rb
|
135
|
+
- lib/statistical/rng/frechet.rb
|
136
|
+
- lib/statistical/rng/gumbel.rb
|
133
137
|
- lib/statistical/rng/laplace.rb
|
134
138
|
- lib/statistical/rng/two_point.rb
|
135
139
|
- lib/statistical/rng/uniform.rb
|
data/lib/core_extensions.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
# All core class modifications go here
|
2
|
-
String.class_eval do
|
3
|
-
# Convert CamelCase to snake_case
|
4
|
-
def snakecase
|
5
|
-
gsub(/::/, '/')
|
6
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
7
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
8
|
-
.tr('-', '_')
|
9
|
-
.downcase
|
10
|
-
end
|
11
|
-
|
12
|
-
# convert snake_case to CamelCase
|
13
|
-
def camelcase
|
14
|
-
split('_').map(&:capitalize).join
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
Array.class_eval do
|
19
|
-
def sum
|
20
|
-
inject(:+)
|
21
|
-
end
|
22
|
-
|
23
|
-
def mean
|
24
|
-
sum.fdiv(count)
|
25
|
-
end
|
26
|
-
|
27
|
-
def variance
|
28
|
-
map { |x| (x - mean)**2}.sum.fdiv(count)
|
29
|
-
end
|
30
|
-
|
31
|
-
def comprehend(&block)
|
32
|
-
return self if block.nil?
|
33
|
-
collect(&block).compact
|
34
|
-
end
|
35
|
-
end
|