statval 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/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ Stefan Plantikow <stefanp@moviepilot.com>
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+
3
+ group :development do
4
+ gem 'rake'
5
+ gem 'irbtools'
6
+ end
7
+
8
+ group :test do
9
+ gem 'rspec', '2.6.0'
10
+ end
@@ -0,0 +1,63 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ awesome_print (1.0.2)
5
+ boson (1.1.1)
6
+ clipboard (1.0.1)
7
+ coderay (1.0.6)
8
+ diff-lcs (1.1.3)
9
+ every_day_irb (1.2.2)
10
+ fancy_irb (0.7.2)
11
+ paint (>= 0.8.1)
12
+ unicode-display_width (>= 0.1.1)
13
+ g (1.6.0)
14
+ ruby_gntp
15
+ hirb (0.6.2)
16
+ interactive_editor (0.0.10)
17
+ spoon (>= 0.0.1)
18
+ irbtools (1.2.2)
19
+ awesome_print (~> 1.0.2)
20
+ boson (~> 1.1.1)
21
+ clipboard (~> 1.0.1)
22
+ coderay (~> 1.0.5)
23
+ every_day_irb (>= 1.2.2)
24
+ fancy_irb (>= 0.7.2)
25
+ g (>= 1.5.0)
26
+ hirb (~> 0.6.1)
27
+ interactive_editor (>= 0.0.10)
28
+ method_locator (>= 0.0.4)
29
+ method_source (>= 0.7.0)
30
+ methodfinder (>= 1.2.5)
31
+ ori (~> 0.1.0)
32
+ paint (>= 0.8.4)
33
+ sketches (>= 0.1.1)
34
+ wirb (>= 0.4.2)
35
+ zucker (>= 12.1)
36
+ method_locator (0.0.4)
37
+ method_source (0.7.1)
38
+ methodfinder (1.2.5)
39
+ ori (0.1.0)
40
+ paint (0.8.4)
41
+ rake (0.9.2.2)
42
+ rspec (2.6.0)
43
+ rspec-core (~> 2.6.0)
44
+ rspec-expectations (~> 2.6.0)
45
+ rspec-mocks (~> 2.6.0)
46
+ rspec-core (2.6.4)
47
+ rspec-expectations (2.6.0)
48
+ diff-lcs (~> 1.1.2)
49
+ rspec-mocks (2.6.0)
50
+ ruby_gntp (0.3.4)
51
+ sketches (0.1.1)
52
+ spoon (0.0.1)
53
+ unicode-display_width (0.1.1)
54
+ wirb (0.4.2)
55
+ zucker (12.1)
56
+
57
+ PLATFORMS
58
+ ruby
59
+
60
+ DEPENDENCIES
61
+ irbtools
62
+ rake
63
+ rspec (= 2.6.0)
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2012 Stefan Plantikow
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,28 @@
1
+ statval
2
+ =======
3
+
4
+ Very simple incremental statisctics collector
5
+
6
+ require 'statval'
7
+ s = StatVal.new
8
+ => {:avg=>0.0, :std=>0.0, :min=>Infinity, :max=>-Infinity, :num=>0, :sum=>0, :sq_sum=>0, :avg_sq=>0.0, :var=>0.0}
9
+
10
+ s << 10
11
+ s << 20
12
+ s << 30
13
+ s.to_hash
14
+ => {:avg=>20.0, :std=>8.16496580927726, :min=>10, :max=>30}
15
+
16
+ s.to_hash(:all)
17
+ => {:avg=>20.0, :std=>8.16496580927726, :min=>10, :max=>30, :num=>3, :sum=>60, :sq_sum=>1400, :avg_sq=>466.6666666666667, :var=>66.66666666666669}
18
+
19
+ s[:max]=40
20
+ h = { :a => 5, :s => s }
21
+ => {:a=>5, :s=>{:avg=>20.0, :std=>8.16496580927726, :min=>10, :max=>40, :num=>3, :sum=>60, :sq_sum=>1400, :avg_sq=>466.6666666666667, :var=>66.66666666666669}}
22
+
23
+ StatVal.map_hash(h)
24
+ => {:a=>5, :s=>{:avg=>20.0, :std=>8.16496580927726, :min=>10, :max=>40}}
25
+
26
+ StatVal.flatmap_hash(h, :all)
27
+ => {:a=>5, "s_avg"=>20.0, "s_std"=>8.16496580927726, "s_min"=>10, "s_max"=>40, "s_num"=>3, "s_sum"=>60, "s_sq_sum"=>1400, "s_avg_sq"=>466.6666666666667, "s_var"=>66.66666666666669}
28
+
@@ -0,0 +1,27 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rdoc/task'
4
+ require 'rspec'
5
+ require 'rspec/core/rake_task'
6
+
7
+ desc 'Run all rspecs'
8
+ RSpec::Core::RakeTask.new(:spec) do |spec|
9
+ spec.fail_on_error = true
10
+ spec.verbose = false
11
+ end
12
+
13
+ desc 'Run rdoc over project sources'
14
+ RDoc::Task.new(:rdoc) do |rdoc|
15
+ rdoc.rdoc_files.include("lib/**/*.rb")
16
+ end
17
+
18
+ desc 'Run irb in project environment'
19
+ task :console do
20
+ require 'irb'
21
+ ARGV.clear
22
+ IRB.start
23
+ end
24
+
25
+ task :doc => :rdoc
26
+ task :test => :spec
27
+ task :irb => :console
@@ -0,0 +1,2 @@
1
+ require 'statval/version'
2
+ require 'statval/statval'
@@ -0,0 +1,227 @@
1
+ module StatVal
2
+
3
+ class StatVal
4
+
5
+ POS_INFINITY = 1.0/0.0
6
+ NEG_INFINITY = - 1.0/0.0
7
+
8
+ attr_reader :num
9
+ attr_reader :min
10
+ attr_reader :max
11
+ attr_reader :sum
12
+ attr_reader :sq_sum
13
+
14
+ def initialize(options = {}) ; reset(options) end
15
+
16
+ def reset(options = {})
17
+ options[:num] = 0 unless options[:num]
18
+ options[:min] = nil unless options[:min]
19
+ options[:max] = nil unless options[:max]
20
+ options[:sum] = 0 unless options[:sum]
21
+ options[:sq_sum] = 0 unless options[:sq_sum]
22
+ options.each_pair { |k, v| self[k] = v}
23
+ options
24
+ end
25
+
26
+
27
+ def all_keys ; [ :avg, :std, :min, :max, :num, :sum, :sq_sum, :avg_sq, :var ] end
28
+ def default_keys ; [ :avg, :std, :min, :max, :num ] end
29
+ def writable_keys ; [ :num, :min, :max, :sum, :sq_sum ] end
30
+
31
+ alias_method :keys, :default_keys
32
+
33
+ def [](key)
34
+ case key
35
+ when :num then num
36
+ when :min then min
37
+ when :max then max
38
+ when :sum then sum
39
+ when :sq_sum then sq_sum
40
+ when :avg then avg
41
+ when :std then std
42
+ when :avg_sq then avg_sq
43
+ when :var then var
44
+ else
45
+ raise ArgumentError
46
+ end
47
+ end
48
+
49
+ def []=(key, new_val)
50
+ case key
51
+ when :num then self.num = new_val
52
+ when :min then self.min = new_val
53
+ when :max then self.max = new_val
54
+ when :sum then self.sum = new_val
55
+ when :sq_sum then self.sq_sum = new_val
56
+ else
57
+ raise ArgumentError
58
+ end
59
+ end
60
+
61
+ def each_pair
62
+ this = self
63
+ keys.each { | key| yield key, this[key] }
64
+ end
65
+
66
+ alias_method :each, :each_pair
67
+
68
+ def values
69
+ this = self
70
+ keys.map { |key| this[key] }
71
+ end
72
+
73
+ def +(value) ; self.class.new << self << value end
74
+
75
+ def <<(value, *rest)
76
+ this = self
77
+ if value.kind_of? StatVal
78
+ if empty?
79
+ reset(value.to_hash(:writable))
80
+ else
81
+ begin
82
+ @num += value.num
83
+ @sum += value.sum
84
+ @sq_sum += value.sq_sum
85
+ val_min = value.min
86
+ val_max = value.max
87
+ @min = val_min if val_min < @min
88
+ @max = val_max if val_max > @max
89
+ end unless value.empty?
90
+ end
91
+ else
92
+ if value.kind_of? Numeric
93
+ @sum += value
94
+ @sq_sum += value * value
95
+ @num += 1
96
+ @min = value if value < @min
97
+ @max = value if value > @max
98
+ else
99
+ if value.respond_to?(:each_pair)
100
+ value.each_pair { |k, v| this << v }
101
+ else
102
+ if value.respond_to?(:each)
103
+ value.each { |v| this << v }
104
+ else
105
+ raise ArgumentError
106
+ end
107
+ end
108
+ end
109
+ end
110
+ rest.each { |v| this << v } if rest
111
+ this
112
+ end
113
+
114
+ def key_hash(which_keys = nil)
115
+ return which_keys if which_keys.is_a?(Hash)
116
+
117
+ case which_keys
118
+ when nil then iter = keys
119
+ when :all then iter = all_keys
120
+ when :default then iter = default_keys
121
+ when :writable then iter = writable_keys
122
+ else
123
+ if which_keys.repsond_to?(:each)
124
+ iter = which_Keys
125
+ else
126
+ return { which_keys => which_keys }
127
+ end
128
+ end
129
+
130
+ iter.inject({}) { |h, k| h[k] = k; h }
131
+ end
132
+
133
+ def to_hash(which_keys = nil)
134
+ key_hash(which_keys).inject({}) { |h, (name, attr)| h[name] = self[attr]; h }
135
+ end
136
+
137
+ def to_s ; to_hash.to_s end
138
+
139
+ def empty? ; @num == 0 end
140
+
141
+ def bounded? ; ! (abs_is_infinite(@min) || abs_is_infinite(@max)) end
142
+
143
+ def avg ; if empty? then zero_if_unbounded(abs_div(@max - @min, 2)) else abs_div(@sum, @num) end end
144
+
145
+ def avg_sq
146
+ if empty? then zero_if_unbounded(abs_div((@max*@max) - (@min*@min), 2)) else abs_div(@sq_sum, @num) end
147
+ end
148
+
149
+ def var ; avg_sq - (avg * avg) end
150
+
151
+ def std ; Math.sqrt(var) end
152
+
153
+ def num=(new_val)
154
+ raise ArgumentError if new_val < 0
155
+ @num = new_val
156
+ end
157
+
158
+ def sum=(new_val)
159
+ raise ArgumentError if new_val < 0
160
+ @sum= new_val
161
+ end
162
+
163
+ def sq_sum=(new_val)
164
+ raise ArgumentError if new_val < 0
165
+ @sq_sum= new_val
166
+ end
167
+
168
+ def min=(new_val)
169
+ @min = if new_val then new_val else (if empty? then POS_INFINITY else avg-std end) end
170
+ end
171
+
172
+ def max=(new_val)
173
+ @max = if new_val then new_val else (if empty? then NEG_INFINITY else avg+std end) end
174
+ end
175
+
176
+ private
177
+
178
+ def abs_is_infinite(val) ; val.abs.to_f === POS_INFINITY end
179
+
180
+ def zero_if_unbounded(val) ; if bounded? then val else 0.0 end end
181
+
182
+ def abs_div(nom, denom) ; nom.abs.to_f / denom end
183
+
184
+ end
185
+
186
+ # Take hash that contains StatVal values and create new hash that is identical to it but has
187
+ # the StatVal values v replaced by v.to_hash(which_keys)
188
+ #
189
+ # Just copies non-StatVal entries
190
+ #
191
+ def self.map_hash(h, which_keys = nil)
192
+ r = {}
193
+ h.each_pair { |k,v| r[k] = if v.kind_of?(StatVal) then v.to_hash(which_keys) else v end }
194
+ r
195
+ end
196
+
197
+ # Like map_hash, but flattens converted StatVal values such there attributes get pre- or appended
198
+ # with their key in the outer hash
199
+ #
200
+ # All symbols
201
+ # raises on key conflict
202
+ #
203
+ def self.flatmap_hash(h, which_keys = nil, prefix=true, use_symbols=false)
204
+ flat = {}
205
+ h.each_pair do |k,r|
206
+ if r.kind_of? StatVal
207
+ results = r.to_hash(which_keys)
208
+ results.each_pair do |tag,val|
209
+ new_tag = if prefix then "#{tag}_#{k}" else "#{k}_#{tag}" end
210
+ new_tag = new_tag.to_sym if use_symbols
211
+
212
+ raise ArgumentError if flat[new_tag]
213
+ flat[new_tag] = val
214
+ end
215
+ else
216
+ raise ArgumentError if flat[k]
217
+ if k.is_a?(Symbol) && !use_symbols
218
+ flat[k.to_s] = r
219
+ else
220
+ flat[k] = r
221
+ end
222
+ end
223
+ end
224
+ flat
225
+ end
226
+
227
+ end
@@ -0,0 +1,3 @@
1
+ module StatVal
2
+ VERSION = '0.1'
3
+ end
@@ -0,0 +1,78 @@
1
+ require 'set'
2
+
3
+ require 'statval'
4
+
5
+ module StatVal
6
+ describe StatVal do
7
+
8
+ it 'is instantiated' do
9
+ @it = lambda { StatVal.new }
10
+ @it.should_not raise_error
11
+ end
12
+
13
+ it 'collects statistics' do
14
+ @it = StatVal.new
15
+ @it << 0
16
+ @it << 10
17
+ @it << [0, 10]
18
+ @it.<< 0, 10
19
+ @it << @it
20
+ @it << [@it, @it] # :)
21
+ @it.avg.should be == 5.0
22
+ @it.num.should be == 48
23
+ @it.var.should be == 25.0
24
+ @it.std.should be == 5.0
25
+ @it.min.should be == 0
26
+ @it.max.should be == 10
27
+ end
28
+
29
+ it 'is additive' do
30
+ @one = StatVal.new
31
+ @one << 10
32
+ @one << 10
33
+ @two = StatVal.new
34
+ @two << 20
35
+ @two << 20
36
+ @it = @one + @two
37
+ @it.avg.should be == 15.0
38
+ end
39
+
40
+ it 'resets' do
41
+ @it = StatVal.new
42
+ @it.reset num: 2, min: -2, max: +12, sum: 10, sq_sum: 148
43
+ @it.num.should be == 2
44
+ @it.min.should be == -2
45
+ @it.max.should be == 12
46
+ @it.sum.should be == 10
47
+ @it.avg.should be == 5.0
48
+ @it.sq_sum.should be == 148
49
+ @it.var.should be == 49
50
+ @it.std.should be == 7
51
+ end
52
+
53
+ it 'can be accessed like a hash' do
54
+ @it = StatVal.new
55
+ @it[:num] = 2
56
+ @it[:min] = -2
57
+ @it[:max] = 12
58
+ @it[:sum] = 10
59
+ @it[:sq_sum] = 148
60
+ @it[:num].should be == 2
61
+ @it[:min].should be == -2
62
+ @it[:max].should be == 12
63
+ @it[:sum].should be == 10
64
+ @it[:avg].should be == 5.0
65
+ @it[:sq_sum].should be == 148
66
+ @it[:var].should be == 49
67
+ @it[:std].should be == 7
68
+ end
69
+
70
+ it 'renders hashes' do
71
+ @it = StatVal.new
72
+ @it.reset num: 2, min: -2, max: +12, sum: 10, sq_sum: 148
73
+ @it = { a: 7, h: @it }
74
+ ::StatVal.map_hash(@it).keys.to_set.should be == [ :h, :a ].to_set
75
+ ::StatVal.flatmap_hash(@it).keys.to_set.should be == [ 'a', 'num_h', 'std_h', 'min_h', 'max_h', 'avg_h' ].to_set
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler.setup
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require 'statval/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'statval'
7
+ s.version = StatVal::VERSION
8
+ s.summary = 'Very simple statistics collector'
9
+ s.description = 'Utility class for incrementally recording measured values and reporting avg, variance, min, and max'
10
+ s.author = 'Stefan Plantikow'
11
+ s.email = 'stefanp@moviepilot.com'
12
+ s.homepage = 'http://moviepilot.github.com/statval'
13
+ s.rubyforge_project = 'statval'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.bindir = 'script'
21
+ s.executables = `git ls-files -- script/*`.split("\n").map{ |f| File.basename(f) }
22
+ # s.default_executable = 'statval'
23
+ s.licenses = ['PUBLIC DOMAIN WITHOUT ANY WARRANTY']
24
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: statval
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stefan Plantikow
9
+ autorequire:
10
+ bindir: script
11
+ cert_chain: []
12
+ date: 2012-04-16 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Utility class for incrementally recording measured values and reporting
15
+ avg, variance, min, and max
16
+ email: stefanp@moviepilot.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - AUTHORS
22
+ - Gemfile
23
+ - Gemfile.lock
24
+ - LICENSE.txt
25
+ - README.md
26
+ - Rakefile
27
+ - lib/statval.rb
28
+ - lib/statval/statval.rb
29
+ - lib/statval/version.rb
30
+ - spec/lib/statval/statval_spec.rb
31
+ - spec/spec_helper.rb
32
+ - statval.gemspec
33
+ homepage: http://moviepilot.github.com/statval
34
+ licenses:
35
+ - PUBLIC DOMAIN WITHOUT ANY WARRANTY
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ segments:
47
+ - 0
48
+ hash: -3748375320206604171
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubyforge_project: statval
57
+ rubygems_version: 1.8.17
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: Very simple statistics collector
61
+ test_files:
62
+ - spec/lib/statval/statval_spec.rb
63
+ - spec/spec_helper.rb