croupier 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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