croupier 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE.txt +1 -1
- data/README.md +8 -8
- data/RUNNING_TESTS.md +4 -4
- data/bin/croupier +1 -1
- data/lib/croupier/cli/application.rb +10 -10
- data/lib/croupier/cli/trollop.rb +4 -4
- data/lib/croupier/distribution.rb +8 -7
- data/lib/croupier/distributions/exponential.rb +7 -7
- data/lib/croupier/distributions/normal.rb +56 -0
- data/lib/croupier/distributions/uniform.rb +6 -6
- data/lib/croupier/exceptions.rb +1 -1
- data/test/distributions/R_tests/test_normal.R +52 -0
- data/test/distributions/generated_samples/normal_0_1.data +10000 -0
- data/test/distributions/generated_samples/normal_5_6.data +10000 -0
- data/test/test_distribution_class.rb +7 -5
- data/test/testsuite.R +1 -1
- metadata +9 -2
data/MIT-LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -7,14 +7,14 @@ Croupier generates random samples of numbers with specific probability distribut
|
|
7
7
|
You need to have Ruby and Rubygems installed in your system. Then install croupier with:
|
8
8
|
|
9
9
|
$ gem install croupier
|
10
|
-
|
10
|
+
|
11
11
|
## Getting Started
|
12
12
|
|
13
13
|
Once you have croupier installed in your machine, you can run the gem using the `croupier` executable:
|
14
14
|
|
15
15
|
$ croupier
|
16
16
|
|
17
|
-
The common case for invoking
|
17
|
+
The common case for invoking Croupier is:
|
18
18
|
|
19
19
|
$ croupier <distribution> <sample_size> [<options>]
|
20
20
|
|
@@ -33,7 +33,7 @@ where:
|
|
33
33
|
### Available distributions and options
|
34
34
|
|
35
35
|
To get a list of all the available distributions use the ```--help``` (or ```-h```) option with croupier:
|
36
|
-
|
36
|
+
|
37
37
|
$ croupier --help
|
38
38
|
|
39
39
|
To get a list of all the available options for a given distribution use ```--help``` (or ```-h```) option with any available distribution:
|
@@ -48,11 +48,11 @@ First of all require the croupier library:
|
|
48
48
|
|
49
49
|
require 'croupier'
|
50
50
|
|
51
|
-
And then use the the distribution you want to generate the sample. You can get just one number (using ```.
|
51
|
+
And then use the the distribution you want to generate the sample. You can get just one number (using ```.generate_number```) or an array of any given size (using ```.generate_sample(N)```)
|
52
52
|
|
53
|
-
dist =
|
54
|
-
dist.
|
55
|
-
dist.
|
53
|
+
dist = Croupier::Distributions::Exponential.new(:lambda => 1.7)
|
54
|
+
dist.generate_sample(100) #=> returns an array of 100 random numbers following an exponential with rate 1.7
|
55
|
+
dist.generate_number #=> returns one random number following an exponential with rate 1.7
|
56
56
|
|
57
57
|
## License
|
58
58
|
|
@@ -60,4 +60,4 @@ Croupier is released under the MIT license.
|
|
60
60
|
|
61
61
|
## Credits
|
62
62
|
|
63
|
-
Developed by Juanjo Bazán [`@xuanxu`](http://twitter.com/xuanxu) & Sergio Arbeo [`@serabe`](http://twitter.com/serabe)
|
63
|
+
Developed by Juanjo Bazán [`@xuanxu`](http://twitter.com/xuanxu) & Sergio Arbeo [`@serabe`](http://twitter.com/serabe)
|
data/RUNNING_TESTS.md
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
The tests suite of the gem is located in the /test folder, and it's divided in two sets that run separately:
|
4
4
|
|
5
5
|
* Ruby Tests
|
6
|
-
* R tests
|
6
|
+
* R tests
|
7
7
|
|
8
8
|
### Ruby Tests
|
9
9
|
|
10
|
-
The ruby set of tests are meant to test the general behaviour of the library.
|
10
|
+
The ruby set of tests are meant to test the general behaviour of the library.
|
11
11
|
You can run these tests using the default Rake task:
|
12
12
|
|
13
13
|
$ rake
|
@@ -15,12 +15,12 @@ You can run these tests using the default Rake task:
|
|
15
15
|
### R tests
|
16
16
|
|
17
17
|
There's a group of tests validating the probability distribution of several samples of numbers generated with Croupier.
|
18
|
-
These tests are statistical tests included in R scripts.
|
18
|
+
These tests are statistical tests included in R scripts.
|
19
19
|
To run them you need to have R installed in your system. You can get the R software from ```http://www.r-project.org/```
|
20
20
|
The testsuite uses the 'testthat' package. You can install it from the R console with the folowing command:
|
21
21
|
|
22
22
|
install.packages("testthat", repos = "http://cran.r-project.org/", type="source")
|
23
|
-
|
23
|
+
|
24
24
|
Once you have R and all the dependencies installed in your system, you can run the test/testsuite.R file:
|
25
25
|
|
26
26
|
Using Rscript:
|
data/bin/croupier
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Croupier
|
2
2
|
module CLI
|
3
3
|
#####################################################################
|
4
|
-
# Croupier main command line interface application object.
|
5
|
-
#
|
6
|
-
# When invoking +croupier+ from the command line,
|
4
|
+
# Croupier main command line interface application object.
|
5
|
+
#
|
6
|
+
# When invoking +croupier+ from the command line,
|
7
7
|
# a Croupier::CLI::Application object is created and run.
|
8
8
|
#
|
9
9
|
class Application
|
@@ -30,11 +30,11 @@ module Croupier
|
|
30
30
|
def run
|
31
31
|
Croupier.trap_interrupt
|
32
32
|
distribution, sample_size, params = parse_distribution_options
|
33
|
-
distribution.new(params).
|
33
|
+
distribution.new(params).generate_sample(sample_size).each{|n| Croupier.message n}
|
34
34
|
end
|
35
35
|
|
36
36
|
private
|
37
|
-
|
37
|
+
|
38
38
|
def parse_distribution_options
|
39
39
|
#Trollop needs this vars locally
|
40
40
|
available_distribution_list = @distribution_list.keys
|
@@ -48,11 +48,11 @@ Currently you can use this distributions:
|
|
48
48
|
[#{available_distribution_list.join(', ')}]
|
49
49
|
|
50
50
|
Usage:
|
51
|
-
croupier <distribution> <n> [options]
|
51
|
+
croupier <distribution> <n> [options]
|
52
52
|
where <n> is the quantity of numbers Croupier will generate.
|
53
53
|
|
54
54
|
Get options list for any distribution with: croupier <distrib> --help
|
55
|
-
|
55
|
+
|
56
56
|
EOS
|
57
57
|
stop_on available_distribution_list
|
58
58
|
end
|
@@ -69,12 +69,12 @@ Get options list for any distribution with: croupier <distrib> --help
|
|
69
69
|
else
|
70
70
|
Trollop::die "unknown distribution #{dist.inspect}"
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
sample_size = ARGV.shift.to_i # get the sample size
|
74
74
|
Trollop::die "Sample size must be a positive integer" if sample_size.nil? || sample_size < 1
|
75
|
-
|
75
|
+
|
76
76
|
return [@distribution_list[dist], sample_size, dist_opts]
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
80
|
-
end
|
80
|
+
end
|
data/lib/croupier/cli/trollop.rb
CHANGED
@@ -12,7 +12,7 @@ VERSION = "1.16.2"
|
|
12
12
|
## Thrown by Parser in the event of a commandline error. Not needed if
|
13
13
|
## you're using the Trollop::options entry.
|
14
14
|
class CommandlineError < StandardError; end
|
15
|
-
|
15
|
+
|
16
16
|
## Thrown by Parser if the user passes in '-h' or '--help'. Handled
|
17
17
|
## automatically by Trollop#options.
|
18
18
|
class HelpNeeded < StandardError; end
|
@@ -250,7 +250,7 @@ class Parser
|
|
250
250
|
syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
|
251
251
|
@constraints << [:depends, syms]
|
252
252
|
end
|
253
|
-
|
253
|
+
|
254
254
|
## Marks two (or more!) options as conflicting.
|
255
255
|
def conflicts *syms
|
256
256
|
syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
|
@@ -427,7 +427,7 @@ class Parser
|
|
427
427
|
# call this unless the cursor's at the beginning of a line.
|
428
428
|
|
429
429
|
left = {}
|
430
|
-
@specs.each do |name, spec|
|
430
|
+
@specs.each do |name, spec|
|
431
431
|
left[name] = "--#{spec[:long]}" +
|
432
432
|
(spec[:short] && spec[:short] != :none ? ", -#{spec[:short]}" : "") +
|
433
433
|
case spec[:type]
|
@@ -656,7 +656,7 @@ private
|
|
656
656
|
start = 0
|
657
657
|
ret = []
|
658
658
|
until start > str.length
|
659
|
-
nextt =
|
659
|
+
nextt =
|
660
660
|
if start + width >= str.length
|
661
661
|
str.length
|
662
662
|
else
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Croupier
|
2
|
-
|
2
|
+
|
3
3
|
#####################################################################
|
4
4
|
# Distribution represents the probability distribution used to
|
5
5
|
# generate the sample of random numbers.
|
@@ -26,14 +26,15 @@ module Croupier
|
|
26
26
|
end
|
27
27
|
|
28
28
|
# Main method to generate n random numbers using the current probability distribution
|
29
|
-
def
|
30
|
-
(1..n).map {
|
29
|
+
def generate_sample(n=1)
|
30
|
+
(1..n).map { generate_number }
|
31
31
|
end
|
32
32
|
|
33
33
|
# Generates one random number using the current probability distribution
|
34
|
-
def
|
34
|
+
def generate_number
|
35
|
+
generate_sample 1
|
35
36
|
end
|
36
|
-
|
37
|
+
|
37
38
|
# Defines a hash with banner and all available CLI options.
|
38
39
|
# It is a hash with two keys:
|
39
40
|
# :banner => A string used as banner in the command line help
|
@@ -45,7 +46,7 @@ module Croupier
|
|
45
46
|
# [:mean, 'The mean of the distribution', {:default => 33}],
|
46
47
|
# [:median, 'Median of the distribution',{:default => 33.0, :type => :float}]
|
47
48
|
# ]
|
48
|
-
# }
|
49
|
+
# }
|
49
50
|
def self.cli_options
|
50
51
|
{:banner => nil, :options=>[]}
|
51
52
|
end
|
@@ -58,4 +59,4 @@ module Croupier
|
|
58
59
|
|
59
60
|
module Distributions
|
60
61
|
end
|
61
|
-
end
|
62
|
+
end
|
@@ -1,20 +1,20 @@
|
|
1
1
|
module Croupier
|
2
2
|
module Distributions
|
3
|
-
|
3
|
+
|
4
4
|
#####################################################################
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# the
|
5
|
+
# Exponential Distribution
|
6
|
+
# Continuous probability distribution with a lambda param rate
|
7
|
+
# describing the time between events in a Poisson process
|
8
8
|
#
|
9
9
|
class Exponential < ::Croupier::Distribution
|
10
|
-
|
10
|
+
|
11
11
|
def initialize(options={})
|
12
12
|
@name = "Exponential distribution"
|
13
13
|
@description = "Continuous probability distribution with a lambda param rate describing the time between events in a Poisson process"
|
14
14
|
configure(options)
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
17
|
+
def generate_number
|
18
18
|
raise Croupier::InputParamsError, "Invalid interval values" if @parameters[:lambda] <= 0
|
19
19
|
(-1/@parameters[:lambda]) * Math.log(rand)
|
20
20
|
end
|
@@ -36,4 +36,4 @@ module Croupier
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
39
|
-
end
|
39
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Croupier
|
2
|
+
module Distributions
|
3
|
+
|
4
|
+
#####################################################################
|
5
|
+
# Normal Distribution
|
6
|
+
# Continuous distribution (mu,sigma) (defaults to (0,1) ) where
|
7
|
+
# mu is the mean and sigma the standard deviation.
|
8
|
+
#
|
9
|
+
class Normal < ::Croupier::Distribution
|
10
|
+
|
11
|
+
def initialize(options={})
|
12
|
+
@name = "Uniform distribution"
|
13
|
+
@description = "Continuous distribution (mu,sigma) (defaults to (0,1) ) where mu is the mean and sigma the standard deviation."
|
14
|
+
configure(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate_sample(n=1)
|
18
|
+
sample = n.odd? ? n+1 : n
|
19
|
+
|
20
|
+
# Generate
|
21
|
+
gen = (1..sample).map do |x|
|
22
|
+
1 - rand # because uniform need to be in (0,1]
|
23
|
+
end.each_slice(2).flat_map do |x, y|
|
24
|
+
[
|
25
|
+
Math.sqrt(-2*Math.log(x)) * Math.cos(2*Math::PI*y),
|
26
|
+
Math.sqrt(-2*Math.log(x)) * Math.sin(2*Math::PI*y)
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Adjust parameters.
|
31
|
+
gen.map!{ |x| x * @parameters[:std] } if @parameters[:std] != 1
|
32
|
+
gen.map!{ |x| x + @parameters[:mean] } if @parameters[:mean] != 0
|
33
|
+
|
34
|
+
# Adjust length
|
35
|
+
n.odd? ? gen[0..-2] : gen
|
36
|
+
end
|
37
|
+
|
38
|
+
def default_parameters
|
39
|
+
{:mean => 0, :std => 1}
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.cli_name
|
43
|
+
"normal"
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.cli_options
|
47
|
+
{:options => [
|
48
|
+
[:mean, 'mean of the distribution', {:type=>:float, :default => 0.0}],
|
49
|
+
[:std, 'standard deviation of the distribution', {:type=>:float, :default => 1.0}]
|
50
|
+
],
|
51
|
+
:banner => "Normal distribution. Generate numbers following a continuous distribution the real line with mean :mean and standard deviation :std."
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,20 +1,20 @@
|
|
1
1
|
module Croupier
|
2
2
|
module Distributions
|
3
|
-
|
3
|
+
|
4
4
|
#####################################################################
|
5
|
-
# Uniform Distribution
|
6
|
-
# Continous distribution where all points in an interval have
|
5
|
+
# Uniform Distribution
|
6
|
+
# Continous distribution where all points in an interval have
|
7
7
|
# the same probability.
|
8
8
|
#
|
9
9
|
class Uniform < ::Croupier::Distribution
|
10
|
-
|
10
|
+
|
11
11
|
def initialize(options={})
|
12
12
|
@name = "Uniform distribution"
|
13
13
|
@description = "Continuous distribution on [a,b] (defaults to [0,1]) where all points in the interval are equally likely"
|
14
14
|
configure(options)
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
17
|
+
def generate_number
|
18
18
|
raise Croupier::InputParamsError, "Invalid interval values" if @parameters[:b] < @parameters[:a]
|
19
19
|
rand Range.new(@parameters[:a], @parameters[:b])
|
20
20
|
end
|
@@ -37,4 +37,4 @@ module Croupier
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
|
-
end
|
40
|
+
end
|
data/lib/croupier/exceptions.rb
CHANGED
@@ -0,0 +1,52 @@
|
|
1
|
+
context('** Normal Distribution **')
|
2
|
+
# Run a K-S test on the Croupier sample against a exponential R-generated sample
|
3
|
+
# Accept Croupier's sample as normal if p-value > 0.05
|
4
|
+
# Also check for a statistic near 0
|
5
|
+
|
6
|
+
context('Kolmogorov-Smirnov test for default mean = 0 and std = 1')
|
7
|
+
croupier_normal <- read.table("../generated_samples/normal_0_1.data")
|
8
|
+
ks_result<-ks.test(croupier_normal$V1, "pnorm")
|
9
|
+
|
10
|
+
test_that("p-value > 0.05", {
|
11
|
+
expect_true(ks_result$p.value > 0.05)
|
12
|
+
})
|
13
|
+
|
14
|
+
test_that("statistic converging to 0", {
|
15
|
+
expect_true(as.numeric(ks_result$statistic) < 0.05)
|
16
|
+
})
|
17
|
+
|
18
|
+
context('Shapiro test for default mean = 0 and std = 1')
|
19
|
+
croupier_normal <- read.table("../generated_samples/normal_0_1.data")
|
20
|
+
s_result<-shapiro.test(croupier_normal$V1[1:5000])
|
21
|
+
|
22
|
+
test_that("p-value > 0.05", {
|
23
|
+
expect_true(s_result$p.value > 0.05)
|
24
|
+
})
|
25
|
+
|
26
|
+
test_that("statistic converging to 0", {
|
27
|
+
expect_true(as.numeric(ks_result$statistic) < 0.05)
|
28
|
+
})
|
29
|
+
|
30
|
+
context('Kolmogorov-Smirnov test for given mean = 5 and std = 6')
|
31
|
+
croupier_normal <- read.table("../generated_samples/normal_5_6.data")
|
32
|
+
ks_result<-ks.test((croupier_normal$V1 - 5)/6, "pnorm")
|
33
|
+
|
34
|
+
test_that("p-value > 0.05", {
|
35
|
+
expect_true(ks_result$p.value > 0.05)
|
36
|
+
})
|
37
|
+
|
38
|
+
test_that("statistic converging to 0", {
|
39
|
+
expect_true(as.numeric(ks_result$statistic) < 0.05)
|
40
|
+
})
|
41
|
+
|
42
|
+
context('Shapiro test for default mean = 5 and std = 1')
|
43
|
+
croupier_normal <- read.table("../generated_samples/normal_0_1.data")
|
44
|
+
s_result<-shapiro.test((croupier_normal$V1[1:5000] - 5 ) / 6)
|
45
|
+
|
46
|
+
test_that("p-value > 0.05", {
|
47
|
+
expect_true(s_result$p.value > 0.05)
|
48
|
+
})
|
49
|
+
|
50
|
+
test_that("statistic converging to 0", {
|
51
|
+
expect_true(as.numeric(ks_result$statistic) < 0.05)
|
52
|
+
})
|