croupier 1.0.1 → 1.1.0
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.
- 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
|
+
})
|