croupier 1.1.0 → 1.2.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/README.md CHANGED
@@ -32,6 +32,8 @@ where:
32
32
 
33
33
  ### Available distributions and options
34
34
 
35
+ Current version provides: Cauchy, exponential, normal, Poisson, triangular and uniform distributions.
36
+
35
37
  To get a list of all the available distributions use the ```--help``` (or ```-h```) option with croupier:
36
38
 
37
39
  $ croupier --help
@@ -54,6 +56,13 @@ And then use the the distribution you want to generate the sample. You can get j
54
56
  dist.generate_sample(100) #=> returns an array of 100 random numbers following an exponential with rate 1.7
55
57
  dist.generate_number #=> returns one random number following an exponential with rate 1.7
56
58
 
59
+ ## How to generate a distribution.
60
+
61
+ There are several ways. The easiest one is to override ```generate_number``` or ```generate_sample``` (one is enough).
62
+
63
+ Nonetheless there is another cool way to implement a distribution: implementing ```inv_cdf```. ``ìnv_cdf``` receives a
64
+ parameter ```n``` that is a sample of a uniform distribution. It should return the inverse of the cdf applied to ```n```.
65
+
57
66
  ## License
58
67
 
59
68
  Croupier is released under the MIT license.
data/RUNNING_TESTS.md CHANGED
@@ -15,13 +15,27 @@ 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
+ To generate samples to be tested, run the script ```generate_test_data.sh```:
19
+
20
+ > ./generate_test_data.sh
21
+
22
+ Now you can run the R testsuite:
18
23
  These tests are statistical tests included in R scripts.
19
24
  To run them you need to have R installed in your system. You can get the R software from ```http://www.r-project.org/```
25
+
26
+ Dependencies:
27
+
20
28
  The testsuite uses the 'testthat' package. You can install it from the R console with the folowing command:
21
29
 
22
30
  install.packages("testthat", repos = "http://cran.r-project.org/", type="source")
23
31
 
24
- Once you have R and all the dependencies installed in your system, you can run the test/testsuite.R file:
32
+ Other R packages needed:
33
+
34
+ * triangle
35
+ * vcd
36
+
37
+ The test script will try to intall all dependencies if they are not found in your R installation.
38
+ You can run the test/testsuite.R file:
25
39
 
26
40
  Using Rscript:
27
41
 
@@ -27,12 +27,25 @@ module Croupier
27
27
 
28
28
  # Main method to generate n random numbers using the current probability distribution
29
29
  def generate_sample(n=1)
30
- (1..n).map { generate_number }
30
+ if self.respond_to? :inv_cdf
31
+ (1..n).map{ inv_cdf(rand) }
32
+ else
33
+ (1..n).map{ generate_number }
34
+ end
31
35
  end
32
36
 
33
37
  # Generates one random number using the current probability distribution
34
38
  def generate_number
35
- generate_sample 1
39
+ if self.respond_to? :inv_cdf
40
+ inv_cdf(rand)
41
+ else
42
+ generate_sample 1
43
+ end
44
+ end
45
+
46
+ # convenience method for lazy programmers
47
+ def params
48
+ @parameters
36
49
  end
37
50
 
38
51
  # Defines a hash with banner and all available CLI options.
@@ -0,0 +1,39 @@
1
+ module Croupier
2
+ module Distributions
3
+
4
+ #####################################################################
5
+ # Cauchy Distribution
6
+ # Continuous probability distribution describing resonance behavior.
7
+ #
8
+ class Cauchy < ::Croupier::Distribution
9
+
10
+ def initialize(options={})
11
+ @name = "Cauchy distribution"
12
+ @description = "Continuous probability distribution describing resonance behavior"
13
+ configure(options)
14
+ raise Croupier::InputParamsError, "Invalid scale value, it must be positive" unless params[:scale] > 0
15
+ end
16
+
17
+ def inv_cdf n
18
+ params[:location] + (params[:scale] * Math.tan(Math::PI * (0.5 - n)))
19
+ end
20
+
21
+ def default_parameters
22
+ {:location => 0.0, :scale => 1.0}
23
+ end
24
+
25
+ def self.cli_name
26
+ "cauchy"
27
+ end
28
+
29
+ def self.cli_options
30
+ {:options => [
31
+ [:location, 'location param', {:type=>:float, :default => 0.0}],
32
+ [:scale, 'scale param', {:type=>:float, :default => 1.0}],
33
+ ],
34
+ :banner => "Cauchy continuous distribution. Generate numbers following a Cauchy distribution with location and scale parameters"
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -12,11 +12,11 @@ module Croupier
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
+ raise Croupier::InputParamsError, "Invalid interval values" if params[:lambda] <= 0
15
16
  end
16
17
 
17
- def generate_number
18
- raise Croupier::InputParamsError, "Invalid interval values" if @parameters[:lambda] <= 0
19
- (-1/@parameters[:lambda]) * Math.log(rand)
18
+ def inv_cdf n
19
+ (-1/params[:lambda]) * Math.log(n)
20
20
  end
21
21
 
22
22
  def default_parameters
@@ -9,7 +9,7 @@ module Croupier
9
9
  class Normal < ::Croupier::Distribution
10
10
 
11
11
  def initialize(options={})
12
- @name = "Uniform distribution"
12
+ @name = "Normal distribution"
13
13
  @description = "Continuous distribution (mu,sigma) (defaults to (0,1) ) where mu is the mean and sigma the standard deviation."
14
14
  configure(options)
15
15
  end
@@ -28,8 +28,8 @@ module Croupier
28
28
  end
29
29
 
30
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
31
+ gen.map!{ |x| x * params[:std] } if params[:std] != 1
32
+ gen.map!{ |x| x + params[:mean] } if params[:mean] != 0
33
33
 
34
34
  # Adjust length
35
35
  n.odd? ? gen[0..-2] : gen
@@ -48,7 +48,7 @@ module Croupier
48
48
  [:mean, 'mean of the distribution', {:type=>:float, :default => 0.0}],
49
49
  [:std, 'standard deviation of the distribution', {:type=>:float, :default => 1.0}]
50
50
  ],
51
- :banner => "Normal distribution. Generate numbers following a continuous distribution the real line with mean :mean and standard deviation :std."
51
+ :banner => "Normal distribution. Generate numbers following a continuous distribution in the real line with mean :mean and standard deviation :std."
52
52
  }
53
53
  end
54
54
  end
@@ -0,0 +1,47 @@
1
+ module Croupier
2
+ module Distributions
3
+
4
+ #####################################################################
5
+ # Poisson Distribution
6
+ # Discrete probability distribution that expresses the probability
7
+ # of a given number of event occurrin in a fixed interval of time
8
+ # and/or space if these events occur with a known average rate
9
+ # and independently of the time since the las event.
10
+ #
11
+ # Wikipedia http://en.wikipedia.org/wiki/Poisson_distribution
12
+ class Poisson < ::Croupier::Distribution
13
+
14
+ def initialize(options={})
15
+ @name = "Poisson distribution"
16
+ @description = "Discrete probability distribution that expresses the probability of a given number of events occurring in a fixed interval of time."
17
+ configure(options)
18
+ end
19
+
20
+ def generate_number
21
+ l = Math.exp(-params[:lambda])
22
+ k = 0; p = 1;
23
+ while p > l
24
+ p *= rand
25
+ k += 1;
26
+ end
27
+ k-1
28
+ end
29
+
30
+ def default_parameters
31
+ {:lambda => 50}
32
+ end
33
+
34
+ def self.cli_name
35
+ "poisson"
36
+ end
37
+
38
+ def self.cli_options
39
+ {:options => [
40
+ [:lambda, 'rate parameter (equal to the mean of the distribution)', {:type=>:integer, :default => 50}]
41
+ ],
42
+ :banner => "Poisson distribution. Discrete probability distribution that expresses the probability of a given number of events occurring in a fixed interval of time."
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,49 @@
1
+ module Croupier
2
+ module Distributions
3
+
4
+ #####################################################################
5
+ # Triangular Distribution
6
+ # Continuous probability distribution whose lower limit
7
+ # is a, upper limit b and mode c (a <= c <= b).
8
+ class Triangular < ::Croupier::Distribution
9
+
10
+ def initialize(options={})
11
+ @name = "Triangular distribution"
12
+ @description = "Continuous probability distribution whose lower limit is a, upper limit b and mode c (a <= c <= b)"
13
+ configure(options)
14
+ raise Croupier::InputParamsError, "Invalid interval values" if params[:a] >= params[:b]
15
+ if params[:c] < params[:a] || params[:b] < params[:c]
16
+ warn("Mode is not in the support. Mode value will be change to median.")
17
+ params[:c] = (params[:a]+params[:b])/2;
18
+ end
19
+ @F_c = (params[:c]-params[:a])/(params[:b]-params[:a])
20
+ end
21
+
22
+ def inv_cdf n
23
+ if n < @F_c
24
+ params[:a] + Math.sqrt( n * (params[:b] - params[:a]) * (params[:c] - params[:a]) )
25
+ else
26
+ params[:b] - Math.sqrt( (1-n) * (params[:b] - params[:a]) * (params[:b] - params[:c]) )
27
+ end
28
+ end
29
+
30
+ def default_parameters
31
+ {:a => 0.0, :b => 1.0, :c => 0.5}
32
+ end
33
+
34
+ def self.cli_name
35
+ "triangular"
36
+ end
37
+
38
+ def self.cli_options
39
+ {:options => [
40
+ [:a, 'lower limit', {:type=>:float, :default => 0.0}],
41
+ [:b, 'upper limit', {:type=>:float, :default => 1.0}],
42
+ [:c, 'mode' , {:type=>:float, :default => 0.5}]
43
+ ],
44
+ :banner => "Triangular distribution. Continuous distribution whose support is the interval (a,b), with mode c."
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -15,12 +15,12 @@ module Croupier
15
15
  end
16
16
 
17
17
  def generate_number
18
- raise Croupier::InputParamsError, "Invalid interval values" if @parameters[:b] < @parameters[:a]
19
- rand Range.new(@parameters[:a], @parameters[:b])
18
+ raise Croupier::InputParamsError, "Invalid interval values" if params[:b] < params[:a]
19
+ rand Range.new(params[:a], params[:b])
20
20
  end
21
21
 
22
22
  def default_parameters
23
- {:a => 0, :b => 1}
23
+ {:a => 0.0, :b => 1.0}
24
24
  end
25
25
 
26
26
  def self.cli_name
data/lib/croupier.rb CHANGED
@@ -4,14 +4,14 @@ require 'croupier/distribution'
4
4
  Dir[File.join(File.dirname(__FILE__), "./croupier/distributions/*.rb")].each {|f| require f}
5
5
  require 'croupier/cli/application'
6
6
  #####################################################################
7
- # Croupier module.
7
+ # Croupier module.
8
8
  # Used as a namespace containing all the Croupier code.
9
9
  # The module defines a Croupier::CLI::Application and interacts with the user output console.
10
10
  #
11
11
  module Croupier
12
12
  STDERR = $stderr
13
13
  STDOUT = $stdout
14
-
14
+
15
15
  # Croupier module singleton methods.
16
16
  #
17
17
  class << self
@@ -53,7 +53,7 @@ module Croupier
53
53
  def clear_line!
54
54
  self.write "\r"
55
55
  end
56
-
56
+
57
57
  # Trap SIGINT signal
58
58
  def trap_interrupt
59
59
  trap('INT') do
@@ -61,4 +61,4 @@ module Croupier
61
61
  end
62
62
  end
63
63
  end
64
- end
64
+ end
@@ -0,0 +1,28 @@
1
+ context('** Cauchy Distribution **')
2
+ # Run a K-S test on the Croupier sample against a Cauchy R-generated sample
3
+ # Accept Croupier's sample as Cauchy if p-value > 0.05
4
+ # Also check for a statistic near 0
5
+
6
+ context('Kolmogorov-Smirnov test for default (location, scale) = (0,1)')
7
+ croupier_cauchy <- read.table("../generated_samples/cauchy_0_1.data")
8
+ ks_result<-ks.test(croupier_cauchy$V1, "pcauchy")
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('Kolmogorov-Smirnov test for given (location, scale) = (12, 3)')
19
+ croupier_cauchy <- read.table("../generated_samples/cauchy_12_3.data")
20
+ ks_result<-ks.test(croupier_cauchy$V1, "pcauchy", location=12, scale=3)
21
+
22
+ test_that("p-value > 0.05", {
23
+ expect_true(ks_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
+ })
@@ -0,0 +1,17 @@
1
+ context('** Poisson Distribution **')
2
+
3
+ context('ChiSquare Goodness of Fit test for mean = 5')
4
+ croupier_poisson <- read.table("../generated_samples/poisson_5.data")
5
+ cs_result<-summary(goodfit(croupier_poisson$V1, type="poisson", method="ML", par=list(lambda=5)))
6
+
7
+ test_that("p-value > 0.05, poisson mean = 5", {
8
+ expect_true(cs_result['Pearson',3] > 0.05)
9
+ })
10
+
11
+ context('ChiSquare Goodness of Fit test for mean = 50')
12
+ croupier_poisson <- read.table("../generated_samples/poisson_50.data")
13
+ cs_result<-summary(goodfit(croupier_poisson$V1, type="poisson", method="ML", par=list(lambda=50)))
14
+
15
+ test_that("err, poisson lambda = 50", {
16
+ expect_true(cs_result['Pearson',3] > 0.05)
17
+ })
@@ -0,0 +1,28 @@
1
+ context('** Triangular Distribution **')
2
+ # Run a K-S test on the Croupier sample against a exponential R-generated sample
3
+ # Accept Croupier's sample as exponential if p-value > 0.05
4
+ # Also check for a statistic near 0
5
+
6
+ context('Kolmogorov-Smirnov test for a = -1, b = 5, c=4')
7
+ croupier_exponential <- read.table("../generated_samples/triangular_-1_5_4.data")
8
+ ks_result<-ks.test(croupier_exponential$V1, "ptriangle", a=-1, b=5, c=4)
9
+
10
+ test_that("p-value > 0.05 triangular, not default parameters", {
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('Kolmogorov-Smirnov test for default values a=0, b=1, c=0.5')
19
+ croupier_exponential <- read.table("../generated_samples/triangular_0_1_05.data")
20
+ ks_result<-ks.test(croupier_exponential$V1, "ptriangle", a=0, b=1, c=0.5)
21
+
22
+ test_that("p-value > 0.05 triangular, default parameters", {
23
+ expect_true(ks_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
+ })
@@ -28,4 +28,21 @@ class TestDistributionClass < MiniTest::Unit::TestCase
28
28
  assert_equal a.generate_sample(15).size, 15
29
29
  end
30
30
 
31
+ def test_generate_number_calls_inv_cdf_if_present
32
+ c = Class.new(Croupier::Distribution) do
33
+ def inv_cdf n; true; end
34
+ def generate_sample n; false; end
35
+ end
36
+ a = c.new
37
+ assert a.generate_number
38
+ end
39
+
40
+ def test_generate_sample_calls_inv_cdf_if_present
41
+ c = Class.new(Croupier::Distribution) do
42
+ def inv_cdf n; true; end
43
+ def generate_number n; false; end
44
+ end
45
+ a = c.new
46
+ assert a.generate_sample(3).all?
47
+ end
31
48
  end
data/test/testsuite.R CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env Rscript
2
2
  #
3
3
  # This script runs all the R tests of the croupier gem for all the distributions.
4
+ #
4
5
  # Running the script:
5
6
  # Option 1, Via Rscript:
6
7
  # $> Rscript testsuite.R
@@ -8,9 +9,29 @@
8
9
  # Option 2, from the R console:
9
10
  # source('testsuite.R')
10
11
  #
11
- # In order to run the tests you need to have installed the testthat package
12
- # If it is not included in your R installation you can do so running this command:
13
- # install.packages("testthat", repos = "http://cran.r-project.org/", type="source")
14
- #
15
- library("testthat")
12
+ # Dependencies:
13
+ # In order to run the tests you need to have installed the following packages:
14
+ #
15
+ # - testthat
16
+ # - triangle
17
+ # - vcd
18
+ #
19
+ # Croupier will try to install them automatically if they are not included
20
+ # in your R installation.
21
+
22
+ if(require('testthat') == FALSE) {
23
+ install.packages('testthat', repos = "http://cran.r-project.org/", type="source")
24
+ require('testthat')
25
+ }
26
+
27
+ if(require('triangle') == FALSE) {
28
+ install.packages('triangle')
29
+ require('triangle')
30
+ }
31
+ if(require('vcd') == FALSE) {
32
+ install.packages('vcd')
33
+ require('vcd')
34
+ }
35
+ options(warn=-1)
16
36
  test_dir("./distributions/R_tests/")
37
+ options(warn=0)
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: croupier
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Juanjo Bazán
9
+ - Sergio Arbeo
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2012-06-09 00:00:00.000000000 Z
13
+ date: 2012-06-21 00:00:00.000000000 Z
13
14
  dependencies: []
14
15
  description: Croupier is a Ruby gem to generate a random sample of numbers with a
15
16
  given probability distribution.
@@ -27,20 +28,20 @@ files:
27
28
  - lib/croupier/cli/application.rb
28
29
  - lib/croupier/cli/trollop.rb
29
30
  - lib/croupier/distribution.rb
31
+ - lib/croupier/distributions/cauchy.rb
30
32
  - lib/croupier/distributions/exponential.rb
31
33
  - lib/croupier/distributions/normal.rb
34
+ - lib/croupier/distributions/poisson.rb
35
+ - lib/croupier/distributions/triangular.rb
32
36
  - lib/croupier/distributions/uniform.rb
33
37
  - lib/croupier/exceptions.rb
34
38
  - lib/croupier.rb
39
+ - test/distributions/R_tests/test_cauchy.R
35
40
  - test/distributions/R_tests/test_exponential.R
36
41
  - test/distributions/R_tests/test_normal.R
42
+ - test/distributions/R_tests/test_poisson.R
43
+ - test/distributions/R_tests/test_triangular.R
37
44
  - test/distributions/R_tests/test_uniform.R
38
- - test/distributions/generated_samples/exponential_1.data
39
- - test/distributions/generated_samples/exponential_1_6.data
40
- - test/distributions/generated_samples/normal_0_1.data
41
- - test/distributions/generated_samples/normal_5_6.data
42
- - test/distributions/generated_samples/uniform_0_1.data
43
- - test/distributions/generated_samples/uniform_5_33.data
44
45
  - test/test_croupier_module.rb
45
46
  - test/test_distribution_class.rb
46
47
  - test/testsuite.R
@@ -73,15 +74,12 @@ signing_key:
73
74
  specification_version: 3
74
75
  summary: Samples of random numbers with specific probability distributions
75
76
  test_files:
77
+ - test/distributions/R_tests/test_cauchy.R
76
78
  - test/distributions/R_tests/test_exponential.R
77
79
  - test/distributions/R_tests/test_normal.R
80
+ - test/distributions/R_tests/test_poisson.R
81
+ - test/distributions/R_tests/test_triangular.R
78
82
  - test/distributions/R_tests/test_uniform.R
79
- - test/distributions/generated_samples/exponential_1.data
80
- - test/distributions/generated_samples/exponential_1_6.data
81
- - test/distributions/generated_samples/normal_0_1.data
82
- - test/distributions/generated_samples/normal_5_6.data
83
- - test/distributions/generated_samples/uniform_0_1.data
84
- - test/distributions/generated_samples/uniform_5_33.data
85
83
  - test/test_croupier_module.rb
86
84
  - test/test_distribution_class.rb
87
85
  - test/testsuite.R