means 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.
Files changed (5) hide show
  1. data/VERSION +1 -1
  2. data/lib/means.rb +38 -12
  3. data/means.gemspec +2 -2
  4. data/spec/means_spec.rb +57 -4
  5. metadata +13 -13
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.2.0
data/lib/means.rb CHANGED
@@ -8,23 +8,23 @@ class Mean
8
8
 
9
9
  # Calculate the arithmetic mean
10
10
  # @param [Array<Numeric>] data the data values
11
- # @return [Numeric] the arithmetic mean
11
+ # @return [Numeric, nil] the arithmetic mean or nil if there is no data
12
12
  def Mean.arithmetic(data)
13
13
  data.reduce(:+) / data.size unless data.empty?
14
14
  end
15
15
 
16
16
  # Calculate the geometric mean
17
17
  # @param (see Mean.arithmetic)
18
- # @return [Numeric] the geometric mean
18
+ # @return [Numeric, nil] the geometric mean or nil if there is no data or any elements are zero or negative
19
19
  def Mean.geometric(data)
20
- data.reduce(:*) ** (1 / data.size) unless data.empty?
20
+ data.reduce(:*) ** (1 / data.size) unless data.empty? or includes_zero_or_negative? data
21
21
  end
22
22
 
23
23
  # Calculate the harmonic mean
24
24
  # @param (see Mean.arithmetic)
25
- # @return [Numeric] the harmonic mean
25
+ # @return [Numeric, nil] the harmonic mean or nil if there is no data or any elements are zero or negative
26
26
  def Mean.harmonic(data)
27
- data.size / data.reduce(0) {|sum, element| sum += (1 / element)} unless data.empty?
27
+ data.size / data.reduce(0) {|sum, element| sum += (1 / element)} unless data.empty? or includes_zero_or_negative? data
28
28
  end
29
29
 
30
30
  # Remember the initial state.
@@ -34,37 +34,63 @@ class Mean
34
34
  # [geometric mean] needs `:product` and `:count`
35
35
  # [harmonic mean] needs `:sum_of_reciprocals` and `:count`
36
36
  # @param [Hash{Symbol => Numeric}]
37
+ # @raise if `:count` is negative
38
+ # @raise if `:sum_of_reciprocals` is negative
39
+ # @raise if `:product` is negative
37
40
  def initialize(params={})
41
+ raise_error_for_negative params, :count
42
+ raise_error_for_negative params, :sum_of_reciprocals
43
+ raise_error_for_negative params, :product
44
+
38
45
  @sum ||= params[:sum] ||= 0
39
46
  @sum_of_reciprocals ||= params[:sum_of_reciprocals] ||= 0
40
47
  @product ||= params[:product] ||= 1
41
48
  @count ||= params[:count] ||= 0
49
+ @includes_zero_or_negative = false
42
50
  end
43
51
 
44
52
  # Add element to the data set
45
53
  # @param [Numeric] element the element to add
46
54
  def push(element)
55
+ if Mean.zero_or_negative? element
56
+ @includes_zero_or_negative = true
57
+ end
58
+
47
59
  @sum += element
48
- @sum_of_reciprocals += (1 / element)
49
- @product *= element
60
+ @sum_of_reciprocals += (1 / element) unless @includes_zero_or_negative
61
+ @product *= element unless @includes_zero_or_negative
50
62
  @count += 1
51
63
  end
52
64
 
53
65
  # Calculate the arithmetic mean
54
- # @return [Numeric] the arithmetic mean
66
+ # @return [Numeric, nil] the arithmetic mean or nil if there is no data
55
67
  def arithmetic_mean
56
68
  @sum / @count unless @count.zero?
57
69
  end
58
70
 
59
71
  # Calculate the geometric mean
60
- # @return [Numeric] the geometric mean
72
+ # @return [Numeric, nil] the geometric mean or nil if there is no data or any elements are zero or negative
61
73
  def geometric_mean
62
- @product ** (1 / @count) unless @count.zero?
74
+ @product ** (1 / @count) unless @count.zero? or @includes_zero_or_negative
63
75
  end
64
76
 
65
77
  # Calculate the harmonic mean
66
- # @return [Numeric] the harmonic mean
78
+ # @return [Numeric, nil] the harmonic mean or nil if there is no data or any elements are zero or negative
67
79
  def harmonic_mean
68
- @count / @sum_of_reciprocals unless @count.zero?
80
+ @count / @sum_of_reciprocals unless @count.zero? or @includes_zero_or_negative
69
81
  end
82
+
83
+ private
84
+
85
+ def Mean.includes_zero_or_negative? data
86
+ data.any? { |element| zero_or_negative? element }
87
+ end
88
+
89
+ def Mean.zero_or_negative? element
90
+ element <= 0
91
+ end
92
+
93
+ def raise_error_for_negative params, key
94
+ raise %{cannot have negative "#{key.to_s}" of "#{params[key]}"} if params.has_key? key and params[key] < 0
95
+ end
70
96
  end
data/means.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "means"
8
- s.version = "1.1.0"
8
+ s.version = "1.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nigel Lowry"]
12
- s.date = "2012-03-21"
12
+ s.date = "2012-04-12"
13
13
  s.description = "Calculates the different means for a data set (arithmetic, geometric and harmonic)."
14
14
  s.email = "nigel-lowry@ultra.eclipse.co.uk"
15
15
  s.extra_rdoc_files = [
data/spec/means_spec.rb CHANGED
@@ -20,9 +20,14 @@ end
20
20
 
21
21
  shared_examples_for "non-arithmetic means" do
22
22
  [:geometric, :harmonic].each do |mean|
23
- it "is zero when there are any zeroes" do
23
+ it "is nil when there are any zeroes" do
24
24
  data = [1.1, 2.2, 0.0]
25
- Mean.send(mean, data).should == 0
25
+ Mean.send(mean, data).should be_nil
26
+ end
27
+
28
+ it "is nil when there are any negative numbers" do
29
+ data = [1, -1]
30
+ Mean.send(mean, data).should be_nil
26
31
  end
27
32
  end
28
33
  end
@@ -56,6 +61,16 @@ describe "Mean" do
56
61
  data = [1, 2, 3]
57
62
  Mean.arithmetic(data).should == 2
58
63
  end
64
+
65
+ it "works with zero" do
66
+ data = [0, 2]
67
+ Mean.arithmetic(data).should == 1
68
+ end
69
+
70
+ it "works with negative numbers" do
71
+ data = [-1, 3]
72
+ Mean.arithmetic(data).should == 1
73
+ end
59
74
  end
60
75
 
61
76
  describe ".geometric" do
@@ -107,26 +122,52 @@ describe "Mean" do
107
122
  @m.arithmetic_mean.should == 1.5
108
123
  @m.push 3
109
124
  @m.arithmetic_mean.should == 2
125
+ @m.push 0
126
+ @m.arithmetic_mean.should == 1.5
110
127
  end
111
128
  end
112
129
 
113
130
  describe "#geometric_mean" do
114
- it "accumulates the geometric mean" do
131
+ it "accumulates the geometric mean until zero" do
132
+ @m.geometric_mean.should be_nil
133
+ @m.push 2
134
+ @m.geometric_mean.should == 2
135
+ @m.push 8
136
+ @m.geometric_mean.should == 4
137
+ @m.push 0
138
+ @m.geometric_mean.should be_nil
139
+ end
140
+
141
+ it "accumulates the geometric mean until negative" do
115
142
  @m.geometric_mean.should be_nil
116
143
  @m.push 2
117
144
  @m.geometric_mean.should == 2
118
145
  @m.push 8
119
146
  @m.geometric_mean.should == 4
147
+ @m.push -1
148
+ @m.geometric_mean.should be_nil
120
149
  end
121
150
  end
122
151
 
123
152
  describe "#harmonic_mean" do
124
- it "accumulates the harmonic mean" do
153
+ it "accumulates the harmonic mean until zero" do
125
154
  @m.harmonic_mean.should be_nil
126
155
  @m.push 1
127
156
  @m.harmonic_mean.should == 1
128
157
  @m.push 2
129
158
  @m.harmonic_mean.should == 4.0 / 3.0
159
+ @m.push 0
160
+ @m.harmonic_mean.should be_nil
161
+ end
162
+
163
+ it "accumulates the harmonic mean until negative" do
164
+ @m.harmonic_mean.should be_nil
165
+ @m.push 1
166
+ @m.harmonic_mean.should == 1
167
+ @m.push 2
168
+ @m.harmonic_mean.should == 4.0 / 3.0
169
+ @m.push -1
170
+ @m.harmonic_mean.should be_nil
130
171
  end
131
172
  end
132
173
  end
@@ -152,5 +193,17 @@ describe "Mean" do
152
193
  m.push 2
153
194
  m.harmonic_mean.should == 4.0 / 3.0
154
195
  end
196
+
197
+ it "rejects a negative count" do
198
+ lambda { Mean.new(count: -1) }.should raise_error
199
+ end
200
+
201
+ it "rejects negative sum of reciprocals" do
202
+ lambda { Mean.new(sum_of_reciprocals: -1) }.should raise_error
203
+ end
204
+
205
+ it "rejects negative product" do
206
+ lambda { Mean.new(product: -1) }.should raise_error
207
+ end
155
208
  end
156
209
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: means
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:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-21 00:00:00.000000000 Z
12
+ date: 2012-04-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70238701358200 !ruby/object:Gem::Requirement
16
+ requirement: &70325868075600 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 2.9.0
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70238701358200
24
+ version_requirements: *70325868075600
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: yard
27
- requirement: &70238701356180 !ruby/object:Gem::Requirement
27
+ requirement: &70325868073840 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.7.4
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70238701356180
35
+ version_requirements: *70325868073840
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: bundler
38
- requirement: &70238701353120 !ruby/object:Gem::Requirement
38
+ requirement: &70325868072040 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.0.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70238701353120
46
+ version_requirements: *70325868072040
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: jeweler
49
- requirement: &70238701415900 !ruby/object:Gem::Requirement
49
+ requirement: &70325868132480 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.6.4
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70238701415900
57
+ version_requirements: *70325868132480
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rcov
60
- requirement: &70238701415140 !ruby/object:Gem::Requirement
60
+ requirement: &70325868131760 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70238701415140
68
+ version_requirements: *70325868131760
69
69
  description: Calculates the different means for a data set (arithmetic, geometric
70
70
  and harmonic).
71
71
  email: nigel-lowry@ultra.eclipse.co.uk
@@ -103,7 +103,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
103
103
  version: '0'
104
104
  segments:
105
105
  - 0
106
- hash: 3343725683295383752
106
+ hash: 2648056903560015944
107
107
  required_rubygems_version: !ruby/object:Gem::Requirement
108
108
  none: false
109
109
  requirements: