statistical 0.1.1 → 0.1.2
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/.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
|