standard_deviation 1.0.0 → 1.0.1
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/.travis.yml +8 -0
- data/README.md +3 -0
- data/benchmark/bigdecimal.rb +26 -0
- data/benchmark/ruby_version.rb +25 -0
- data/benchmark/types.rb +24 -0
- data/ext/standard_deviation/standard_deviation.c +20 -8
- data/lib/standard_deviation/version.rb +1 -1
- data/spec/spec_helper.rb +6 -0
- data/spec/standard_deviation/standard_deviation_spec.rb +55 -31
- metadata +12 -8
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
An implementation of the standard deviation calculation in C, with much better performance (50x-100x) than using pure ruby.
|
4
4
|
|
5
|
+
[](http://travis-ci.org/reu/standard_deviation)
|
6
|
+
|
5
7
|
## Installation
|
6
8
|
|
7
9
|
Add this line to your application's Gemfile:
|
@@ -32,6 +34,7 @@ For population standard deviation, call `stdevp`:
|
|
32
34
|
```
|
33
35
|
|
34
36
|
Also, the API exposes the `sample_variance` and `population_variance` calculations:
|
37
|
+
|
35
38
|
``` ruby
|
36
39
|
[1, 2, 3, 4, 5].sample_variance => 2.5
|
37
40
|
[1, 2, 3, 4, 5].population_variance => 2.0
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "standard_deviation"
|
2
|
+
require "benchmark"
|
3
|
+
require 'bigdecimal'
|
4
|
+
|
5
|
+
class Array
|
6
|
+
def stdev_ruby
|
7
|
+
total = inject :+
|
8
|
+
mean = total.to_f / size
|
9
|
+
variance = inject(0) { |variance, value| variance + (value - mean) ** 2 } / (size - 1)
|
10
|
+
|
11
|
+
Math.sqrt(variance)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
n = 50
|
16
|
+
values = (1..10_000).map { |value| BigDecimal.new [value, (rand * 10000000000).to_i].join(".") }
|
17
|
+
|
18
|
+
Benchmark.bm do |b|
|
19
|
+
b.report do
|
20
|
+
n.times { values.stdev }
|
21
|
+
end
|
22
|
+
|
23
|
+
b.report do
|
24
|
+
n.times { values.stdev_ruby }
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "standard_deviation"
|
2
|
+
require "benchmark"
|
3
|
+
|
4
|
+
class Array
|
5
|
+
def stdev_ruby
|
6
|
+
total = inject :+
|
7
|
+
mean = total.to_f / size
|
8
|
+
variance = inject(0) { |variance, value| variance + (value - mean) ** 2 } / (size - 1)
|
9
|
+
|
10
|
+
Math.sqrt(variance)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
n = 1000
|
15
|
+
values = (1..10_000).map(&:to_f)
|
16
|
+
|
17
|
+
Benchmark.bm do |b|
|
18
|
+
b.report do
|
19
|
+
n.times { values.stdev }
|
20
|
+
end
|
21
|
+
|
22
|
+
b.report do
|
23
|
+
n.times { values.stdev_ruby }
|
24
|
+
end
|
25
|
+
end
|
data/benchmark/types.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "standard_deviation"
|
2
|
+
require "bigdecimal"
|
3
|
+
require "benchmark"
|
4
|
+
|
5
|
+
n = 100
|
6
|
+
range = (1..10_000)
|
7
|
+
|
8
|
+
integers = range.to_a
|
9
|
+
floats = range.map(&:to_f)
|
10
|
+
decimals = range.map { |value| BigDecimal.new [value, (rand * 10000000000).to_i].join(".") }
|
11
|
+
|
12
|
+
Benchmark.bm do |b|
|
13
|
+
b.report do
|
14
|
+
n.times { integers.stdev }
|
15
|
+
end
|
16
|
+
|
17
|
+
b.report do
|
18
|
+
n.times { floats.stdev }
|
19
|
+
end
|
20
|
+
|
21
|
+
b.report do
|
22
|
+
n.times { decimals.stdev }
|
23
|
+
end
|
24
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
#include <math.h>
|
3
3
|
|
4
|
-
static double sum(VALUE *array,
|
4
|
+
static double sum(VALUE *array, long size) {
|
5
5
|
int i;
|
6
6
|
double total = 0;
|
7
7
|
|
@@ -12,7 +12,7 @@ static double sum(VALUE *array, int size) {
|
|
12
12
|
return total;
|
13
13
|
}
|
14
14
|
|
15
|
-
static double distance_from_mean(VALUE *array,
|
15
|
+
static double distance_from_mean(VALUE *array, long size) {
|
16
16
|
int i;
|
17
17
|
double mean, distance;
|
18
18
|
|
@@ -25,39 +25,51 @@ static double distance_from_mean(VALUE *array, int size) {
|
|
25
25
|
return distance;
|
26
26
|
}
|
27
27
|
|
28
|
-
static double compute_sample_variance(VALUE *array,
|
28
|
+
static double compute_sample_variance(VALUE *array, long size) {
|
29
29
|
return distance_from_mean(array, size) / (size - 1);
|
30
30
|
}
|
31
31
|
|
32
|
-
static double compute_population_variance(VALUE *array,
|
32
|
+
static double compute_population_variance(VALUE *array, long size) {
|
33
33
|
return distance_from_mean(array, size) / size;
|
34
34
|
}
|
35
35
|
|
36
|
+
static void raise_not_enough_elements() {
|
37
|
+
rb_raise(rb_eZeroDivError, "not enough elements");
|
38
|
+
}
|
39
|
+
|
36
40
|
static VALUE sample_variance(VALUE self) {
|
37
|
-
|
41
|
+
long size = RARRAY_LEN(self);
|
38
42
|
VALUE *array = RARRAY_PTR(self);
|
39
43
|
|
44
|
+
if (size < 2) raise_not_enough_elements();
|
45
|
+
|
40
46
|
return rb_float_new(compute_sample_variance(array, size));
|
41
47
|
}
|
42
48
|
|
43
49
|
static VALUE population_variance(VALUE self) {
|
44
|
-
|
50
|
+
long size = RARRAY_LEN(self);
|
45
51
|
VALUE *array = RARRAY_PTR(self);
|
46
52
|
|
53
|
+
if (size < 2) raise_not_enough_elements();
|
54
|
+
|
47
55
|
return rb_float_new(compute_population_variance(array, size));
|
48
56
|
}
|
49
57
|
|
50
58
|
static VALUE stdev(VALUE self) {
|
51
|
-
|
59
|
+
long size = RARRAY_LEN(self);
|
52
60
|
VALUE *array = RARRAY_PTR(self);
|
53
61
|
|
62
|
+
if (size < 2) raise_not_enough_elements();
|
63
|
+
|
54
64
|
return rb_float_new(sqrt(compute_sample_variance(array, size)));
|
55
65
|
}
|
56
66
|
|
57
67
|
static VALUE stdevp(VALUE self) {
|
58
|
-
|
68
|
+
long size = RARRAY_LEN(self);
|
59
69
|
VALUE *array = RARRAY_PTR(self);
|
60
70
|
|
71
|
+
if (size < 2) raise_not_enough_elements();
|
72
|
+
|
61
73
|
return rb_float_new(sqrt(compute_population_variance(array, size)));
|
62
74
|
}
|
63
75
|
|
data/spec/spec_helper.rb
CHANGED
@@ -1,79 +1,103 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Array do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
let(:values) { [1, 2, 6, 3, -4, 23] }
|
9
|
-
it { should == 9.32559202767667 }
|
4
|
+
shared_examples_for "a serie calculation" do
|
5
|
+
context "when called on a empty array" do
|
6
|
+
let(:values) { [] }
|
7
|
+
it { lambda { subject }.should raise_exception ZeroDivisionError, "not enough elements" }
|
10
8
|
end
|
11
9
|
|
12
|
-
context "with
|
13
|
-
let(:values) { [1
|
14
|
-
it {
|
10
|
+
context "when called on a array with only one element" do
|
11
|
+
let(:values) { [1] }
|
12
|
+
it { lambda { subject }.should raise_exception ZeroDivisionError, "not enough elements" }
|
15
13
|
end
|
14
|
+
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
%w(stdev standard_deviation).each do |method|
|
17
|
+
describe "##{method}" do
|
18
|
+
subject { values.send method }
|
19
|
+
|
20
|
+
it_should_behave_like "a serie calculation"
|
21
|
+
|
22
|
+
context "with integer values" do
|
23
|
+
let(:values) { [1, 2, 6, 3, -4, 23] }
|
24
|
+
it { should be_close_to 9.32559202767667 }
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with float values" do
|
28
|
+
let(:values) { [1.0, 2.0, 6.0, 3.0, -4.0, 23.0] }
|
29
|
+
it { should be_close_to 9.32559202767667 }
|
30
|
+
end
|
31
|
+
|
32
|
+
context "with bigdecimal values" do
|
33
|
+
let(:values) { [BigDecimal("1.0"), BigDecimal("2.0"), BigDecimal("6.0"), BigDecimal("3.0"), BigDecimal("-4.0"), BigDecimal("23.0")] }
|
34
|
+
it { should be_close_to 9.32559202767667 }
|
35
|
+
end
|
20
36
|
end
|
21
37
|
end
|
22
38
|
|
23
39
|
describe "#stdevp" do
|
24
40
|
subject { values.stdevp }
|
25
41
|
|
42
|
+
it_should_behave_like "a serie calculation"
|
43
|
+
|
26
44
|
context "with integer values" do
|
27
45
|
let(:values) { [1, 2, 6, 3, -4, 23] }
|
28
|
-
it { should
|
46
|
+
it { should be_close_to 8.513061859414755 }
|
29
47
|
end
|
30
48
|
|
31
49
|
context "with float values" do
|
32
50
|
let(:values) { [1.0, 2.0, 6.0, 3.0, -4.0, 23.0] }
|
33
|
-
it { should
|
51
|
+
it { should be_close_to 8.513061859414755 }
|
34
52
|
end
|
35
53
|
|
36
54
|
context "with bigdecimal values" do
|
37
55
|
let(:values) { [BigDecimal("1.0"), BigDecimal("2.0"), BigDecimal("6.0"), BigDecimal("3.0"), BigDecimal("-4.0"), BigDecimal("23.0")] }
|
38
|
-
it { should
|
56
|
+
it { should be_close_to 8.513061859414755 }
|
39
57
|
end
|
40
58
|
end
|
41
59
|
|
42
|
-
|
43
|
-
|
60
|
+
%w(variance sample_variance).each do |method|
|
61
|
+
describe "##{method}" do
|
62
|
+
subject { values.send method }
|
44
63
|
|
45
|
-
|
46
|
-
let(:values) { [1, 2, 6, 3, -4, 23] }
|
47
|
-
it { should == 86.96666666666665 }
|
48
|
-
end
|
64
|
+
it_should_behave_like "a serie calculation"
|
49
65
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
66
|
+
context "with integer values" do
|
67
|
+
let(:values) { [1, 2, 6, 3, -4, 23] }
|
68
|
+
it { should be_close_to 86.96666666666665 }
|
69
|
+
end
|
54
70
|
|
55
|
-
|
56
|
-
|
57
|
-
|
71
|
+
context "with float values" do
|
72
|
+
let(:values) { [1.0, 2.0, 6.0, 3.0, -4.0, 23.0] }
|
73
|
+
it { should be_close_to 86.96666666666665 }
|
74
|
+
end
|
75
|
+
|
76
|
+
context "with bigdecimal values" do
|
77
|
+
let(:values) { [BigDecimal("1.0"), BigDecimal("2.0"), BigDecimal("6.0"), BigDecimal("3.0"), BigDecimal("-4.0"), BigDecimal("23.0")] }
|
78
|
+
it { should be_close_to 86.96666666666665 }
|
79
|
+
end
|
58
80
|
end
|
59
81
|
end
|
60
82
|
|
61
83
|
describe "#population_variance" do
|
62
84
|
subject { values.population_variance }
|
63
85
|
|
86
|
+
it_should_behave_like "a serie calculation"
|
87
|
+
|
64
88
|
context "with integer values" do
|
65
89
|
let(:values) { [1, 2, 6, 3, -4, 23] }
|
66
|
-
it { should
|
90
|
+
it { should be_close_to 72.47222222222221 }
|
67
91
|
end
|
68
92
|
|
69
93
|
context "with float values" do
|
70
94
|
let(:values) { [1.0, 2.0, 6.0, 3.0, -4.0, 23.0] }
|
71
|
-
it { should
|
95
|
+
it { should be_close_to 72.47222222222221 }
|
72
96
|
end
|
73
97
|
|
74
98
|
context "with bigdecimal values" do
|
75
99
|
let(:values) { [BigDecimal("1.0"), BigDecimal("2.0"), BigDecimal("6.0"), BigDecimal("3.0"), BigDecimal("-4.0"), BigDecimal("23.0")] }
|
76
|
-
it { should
|
100
|
+
it { should be_close_to 72.47222222222221 }
|
77
101
|
end
|
78
102
|
end
|
79
103
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: standard_deviation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-14 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake-compiler
|
16
|
-
requirement: &
|
16
|
+
requirement: &2156134040 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *2156134040
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &2156133440 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *2156133440
|
36
36
|
description: ! "An implementation of the standard deviation calculation in C,\n with
|
37
37
|
much better performance (50x-100x) than using pure ruby."
|
38
38
|
email:
|
@@ -43,10 +43,14 @@ extensions:
|
|
43
43
|
extra_rdoc_files: []
|
44
44
|
files:
|
45
45
|
- .gitignore
|
46
|
+
- .travis.yml
|
46
47
|
- Gemfile
|
47
48
|
- LICENSE
|
48
49
|
- README.md
|
49
50
|
- Rakefile
|
51
|
+
- benchmark/bigdecimal.rb
|
52
|
+
- benchmark/ruby_version.rb
|
53
|
+
- benchmark/types.rb
|
50
54
|
- ext/standard_deviation/extconf.rb
|
51
55
|
- ext/standard_deviation/standard_deviation.c
|
52
56
|
- lib/standard_deviation.rb
|
@@ -69,7 +73,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
73
|
version: '0'
|
70
74
|
segments:
|
71
75
|
- 0
|
72
|
-
hash:
|
76
|
+
hash: -569388018503389721
|
73
77
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
78
|
none: false
|
75
79
|
requirements:
|
@@ -78,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
82
|
version: '0'
|
79
83
|
segments:
|
80
84
|
- 0
|
81
|
-
hash:
|
85
|
+
hash: -569388018503389721
|
82
86
|
requirements: []
|
83
87
|
rubyforge_project:
|
84
88
|
rubygems_version: 1.8.10
|