simple_stats 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,9 +3,9 @@ rvm:
3
3
  - 1.8.7
4
4
  - 1.9.2
5
5
  - 1.9.3
6
+ - 2.0.0
6
7
  - jruby-18mode # JRuby in 1.8 mode
7
8
  - jruby-19mode # JRuby in 1.9 mode
8
- - rbx-18mode
9
- - rbx-19mode # currently in active development, may or may not work for your project
9
+ - rbx
10
10
  # uncomment this line if your project needs to run something other than `rake`:
11
- script: bundle exec rspec spec
11
+ # script: bundle exec rspec spec
data/Gemfile CHANGED
@@ -3,4 +3,6 @@ source "http://rubygems.org"
3
3
  # Specify your gem's dependencies in simple_stats.gemspec
4
4
  gemspec
5
5
 
6
- gem 'rspec'
6
+ gem 'rubysl', :platform => :rbx
7
+ gem 'rake'
8
+ gem 'rspec'
data/README.md CHANGED
@@ -77,7 +77,7 @@ samples.sorted_frequencies
77
77
  [].frequencies # {}
78
78
  [].sorted_frequencies # []
79
79
 
80
- # if elements are preset, mean and median always return floats
80
+ # if elements are present, mean and median always return floats
81
81
  [1, 2, 3].mean # 2.0
82
82
  [1, 2, 3].median # 2.0
83
83
  [1, 2, 3, 4].mean # 2.5
@@ -92,6 +92,30 @@ samples.sorted_frequencies
92
92
  [2, 2.0, 1].modes # [2.0]
93
93
  ```
94
94
 
95
+ ## Mapping
96
+
97
+ If the thing you want stats on is buried in your objects, you can pass a block to any SimpleStats method.
98
+
99
+ ```ruby
100
+ # these two lines do the same thing
101
+ cities.mean {|city| city.population}
102
+ cities.map {|city| city.population}.mean
103
+
104
+ # other examples
105
+ cities.sum(&:public_school_count)
106
+ cities.mean(&:public_school_count)
107
+ cities.frequencies(&:professional_team_count)
108
+
109
+ # more complicated examples
110
+ cities.median {|city| city.municipal_income / city.schools.sum(&:students)}
111
+ cities.map(&:schools).flatten.modes(&:team_name) # most common school sports team name
112
+ cities.map(&:professional_teams).flatten.sorted_frequencies(&:kind) # number of different kinds of sports teams
113
+ ```
114
+
115
+ ## Interaction with other gems
116
+
117
+ If any of SimpleStats' methods are already defined on Enumerable, SimpleStats _will not_ replace them with its own definition. In particular, ActiveSupport [defines a `sum` method](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/enumerable.rb). If both ActiveSupport and SimpleStats are used, the `sum` method will come from ActiveSupport. Don't worry, ActiveSupport and SimpleStats should work fine together.
118
+
95
119
  ## Help make it better!
96
120
 
97
121
  Need something added? [Open an issue](https://github.com/brianhempel/simple_stats/issues). Or, even better, code it yourself and send a pull request:
@@ -106,4 +130,4 @@ Need something added? [Open an issue](https://github.com/brianhempel/simple_stat
106
130
 
107
131
  ## License
108
132
 
109
- Public domain. (I, Brian Hempel, the original author release this code to the public domain.)
133
+ Public domain. (I, Brian Hempel, the original author release this code to the public domain. February 10, 2012.)
data/Rakefile CHANGED
@@ -1 +1,10 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ task :default => :test
4
+
5
+ desc "Run the tests (rspec)"
6
+ task(:test) do
7
+ require 'rspec/autorun'
8
+ $LOAD_PATH.unshift(File.expand_path '../spec', __FILE__)
9
+ Dir.glob(File.expand_path '../spec/**/*_spec.rb', __FILE__).each {|s| require s}
10
+ end
@@ -1,65 +1,91 @@
1
1
  module Enumerable
2
- def sum
3
- inject(0) { |sum, x| sum + x }
4
- end
5
-
6
- def mean
7
- count = self.count # count has to iterate if there's no size method
8
- return nil if count == 0
9
- sum / count.to_f
2
+ unless method_defined? :sum
3
+ def sum(&block)
4
+ return map(&block).sum if block_given?
5
+ inject(0) { |sum, x| sum + x }
6
+ end
10
7
  end
11
8
 
12
- def median
13
- sorted = sort
14
- count = sorted.size
15
- i = count / 2
9
+ unless method_defined? :mean
10
+ def mean(&block)
11
+ return map(&block).mean if block_given?
16
12
 
17
- return nil if count == 0
18
- if count % 2 == 1
19
- sorted[i].to_f
20
- else
21
- ( sorted[i-1] + sorted[i] ) / 2.0
13
+ count = self.count # count has to iterate if there's no size method
14
+ return nil if count == 0
15
+ (sum / count.to_f).to_f
22
16
  end
23
17
  end
24
18
 
25
- def modes
26
- sorted_frequencies = self.sorted_frequencies
27
- sorted_frequencies.select do |item, frequency|
28
- frequency == sorted_frequencies.first[1]
29
- end.map do |item, frequency|
30
- item
19
+ unless method_defined? :median
20
+ def median(&block)
21
+ return map(&block).median if block_given?
22
+
23
+ sorted = sort
24
+ count = sorted.size
25
+ i = count / 2
26
+
27
+ return nil if count == 0
28
+ if count % 2 == 1
29
+ sorted[i]
30
+ else
31
+ ( sorted[i-1] + sorted[i] ) / 2.0
32
+ end.to_f
31
33
  end
32
34
  end
33
35
 
34
- def frequencies
35
- begin
36
- sorted = sort
37
- rescue NoMethodError # i.e. undefined method `<=>' for :my_symbol:Symbol
38
- sorted = sort_by do |item|
39
- item.respond_to?(:"<=>") ? item : item.to_s
36
+ unless method_defined? :modes
37
+ def modes(&block)
38
+ return map(&block).modes if block_given?
39
+
40
+ sorted_frequencies = self.sorted_frequencies
41
+ sorted_frequencies.select do |item, frequency|
42
+ frequency == sorted_frequencies.first[1]
43
+ end.map do |item, frequency|
44
+ item
40
45
  end
41
46
  end
42
- current_item = nil;
47
+ end
43
48
 
44
- Hash[
45
- sorted.reduce([]) do |frequencies, item|
46
- if frequencies.size == 0 || item != current_item
47
- current_item = item
48
- frequencies << [item, 1]
49
- else
50
- frequencies.last[1] += 1
51
- frequencies
49
+ unless method_defined? :frequencies
50
+ # This function is oddly written in order to group 1 (integer) and 1.0 (float) together.
51
+ # If we loaded a hash or used group_by, 1 and 1.0 would be counted as separate things.
52
+ # Instead, we use the == operator for grouping.
53
+ def frequencies(&block)
54
+ return map(&block).frequencies if block_given?
55
+
56
+ begin
57
+ sorted = sort
58
+ rescue NoMethodError # i.e. undefined method `<=>' for :my_symbol:Symbol
59
+ sorted = sort_by do |item|
60
+ item.respond_to?(:"<=>") ? item : item.to_s
52
61
  end
53
62
  end
54
- ]
63
+ current_item = nil;
64
+
65
+ Hash[
66
+ sorted.reduce([]) do |frequencies, item|
67
+ if frequencies.size == 0 || item != current_item
68
+ current_item = item
69
+ frequencies << [item, 1]
70
+ else
71
+ frequencies.last[1] += 1
72
+ frequencies
73
+ end
74
+ end
75
+ ]
76
+ end
55
77
  end
56
78
 
57
- def sorted_frequencies
58
- frequencies.sort_by do |item, frequency|
59
- if item.respond_to?(:"<=>")
60
- [-frequency, item]
61
- else
62
- [-frequency, item.to_s]
79
+ unless method_defined? :sorted_frequencies
80
+ def sorted_frequencies(&block)
81
+ return map(&block).sorted_frequencies if block_given?
82
+
83
+ frequencies.sort_by do |item, frequency|
84
+ if item.respond_to?(:"<=>")
85
+ [-frequency, item]
86
+ else
87
+ [-frequency, item.to_s]
88
+ end
63
89
  end
64
90
  end
65
91
  end
@@ -1,3 +1,3 @@
1
1
  module SimpleStats
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -25,5 +25,15 @@ describe Enumerable do
25
25
  frequencies.class.should == expected.class
26
26
  end
27
27
  end
28
+
29
+ it "calls map first if a block is given" do
30
+ f = Struct.new(:x)
31
+ data = [f.new(:a), f.new(:b), f.new(:b)]
32
+ frequencies = data.frequencies(&:x)
33
+ frequencies.should == {
34
+ :a => 1,
35
+ :b => 2
36
+ }
37
+ end
28
38
  end
29
- end
39
+ end
@@ -24,5 +24,11 @@ describe Enumerable do
24
24
  mean.class.should == expected.class
25
25
  end
26
26
  end
27
+
28
+ it "calls map first if a block is given" do
29
+ f = Struct.new(:x)
30
+ data = [f.new(4), f.new(1), f.new(6)]
31
+ data.mean(&:x).should == 11 / 3.0
32
+ end
27
33
  end
28
34
  end
@@ -24,5 +24,11 @@ describe Enumerable do
24
24
  median.class.should == expected.class
25
25
  end
26
26
  end
27
+
28
+ it "calls map first if a block is given" do
29
+ f = Struct.new(:x)
30
+ data = [f.new(4), f.new(1), f.new(6)]
31
+ data.median(&:x).should == 4.0
32
+ end
27
33
  end
28
34
  end
@@ -25,5 +25,11 @@ describe Enumerable do
25
25
  modes.class.should == expected.class
26
26
  end
27
27
  end
28
+
29
+ it "calls map first if a block is given" do
30
+ f = Struct.new(:x)
31
+ data = [f.new(:a), f.new(:b), f.new(:b)]
32
+ data.modes(&:x).should == [:b]
33
+ end
28
34
  end
29
35
  end
@@ -28,5 +28,11 @@ describe Enumerable do
28
28
  ["White-breasted Nuthatch", 1]
29
29
  ]
30
30
  end
31
+
32
+ it "calls map first if a block is given" do
33
+ f = Struct.new(:x)
34
+ data = [f.new(:a), f.new(:b), f.new(:b)]
35
+ data.sorted_frequencies(&:x).should == [[:b, 2], [:a, 1]]
36
+ end
31
37
  end
32
38
  end
@@ -23,5 +23,11 @@ describe Enumerable do
23
23
  sum.class.should == expected.class
24
24
  end
25
25
  end
26
+
27
+ it "calls map first if a block is given" do
28
+ f = Struct.new(:x)
29
+ data = [f.new(4), f.new(1), f.new(6)]
30
+ data.sum(&:x).should == 11
31
+ end
26
32
  end
27
33
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_stats
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
+ - 1
8
9
  - 0
9
- - 0
10
- version: 1.0.0
10
+ version: 1.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brian Hempel
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-02-10 00:00:00 Z
18
+ date: 2013-12-16 00:00:00 Z
19
19
  dependencies: []
20
20
 
21
21
  description: Simple mean, median, modes, sum, and frequencies for Ruby arrays and enumerables. Tested and sensible.
@@ -74,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
74
  requirements: []
75
75
 
76
76
  rubyforge_project:
77
- rubygems_version: 1.8.15
77
+ rubygems_version: 1.8.26
78
78
  signing_key:
79
79
  specification_version: 3
80
80
  summary: Simple mean, median, modes, sum, and frequencies for Ruby arrays and enumerables. Tested and sensible.