statval 0.1

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