simple_stats 1.0.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.
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +6 -0
- data/README.md +109 -0
- data/Rakefile +1 -0
- data/lib/simple_stats/core_ext/enumerable.rb +66 -0
- data/lib/simple_stats/version.rb +3 -0
- data/lib/simple_stats.rb +2 -0
- data/simple_stats.gemspec +22 -0
- data/spec/enumerable/frequencies_spec.rb +29 -0
- data/spec/enumerable/mean_spec.rb +28 -0
- data/spec/enumerable/median_spec.rb +28 -0
- data/spec/enumerable/modes_spec.rb +29 -0
- data/spec/enumerable/sorted_frequencies_spec.rb +32 -0
- data/spec/enumerable/sum_spec.rb +27 -0
- data/spec/spec_helper.rb +3 -0
- metadata +88 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.8.7
|
4
|
+
- 1.9.2
|
5
|
+
- 1.9.3
|
6
|
+
- jruby-18mode # JRuby in 1.8 mode
|
7
|
+
- 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
|
10
|
+
# uncomment this line if your project needs to run something other than `rake`:
|
11
|
+
script: bundle exec rspec spec
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
# SimpleStats [](http://travis-ci.org/brianhempel/simple_stats)
|
2
|
+
|
3
|
+
|
4
|
+
Install without Bundler:
|
5
|
+
|
6
|
+
gem install simple_stats --no-ri --no-rdoc
|
7
|
+
|
8
|
+
Install with Bundler:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem "simple_stats"
|
12
|
+
```
|
13
|
+
|
14
|
+
Then get some statistics on arrays or enumerables:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
require 'rubygems' # if not using Bundler
|
18
|
+
require 'simple_stats'
|
19
|
+
|
20
|
+
samples = [3, 1, 0, 8, 2, 2, 3, 9]
|
21
|
+
|
22
|
+
samples.sum # 28
|
23
|
+
samples.mean # 3.5
|
24
|
+
samples.median # 2.5
|
25
|
+
samples.modes # [2, 3]
|
26
|
+
samples.frequencies
|
27
|
+
# {
|
28
|
+
# 0 => 1,
|
29
|
+
# 1 => 1,
|
30
|
+
# 2 => 2,
|
31
|
+
# 3 => 2,
|
32
|
+
# 8 => 1,
|
33
|
+
# 9 => 1
|
34
|
+
# }
|
35
|
+
samples.sorted_frequencies
|
36
|
+
# most common elements first
|
37
|
+
# [
|
38
|
+
# [2, 2],
|
39
|
+
# [3, 2],
|
40
|
+
# [0, 1],
|
41
|
+
# [1, 1],
|
42
|
+
# [8, 1],
|
43
|
+
# [9, 1]
|
44
|
+
# ]
|
45
|
+
|
46
|
+
# of course, you can take modes/frequencies on non-numeric data...
|
47
|
+
"banana nanny!".chars.modes
|
48
|
+
# ["n"]
|
49
|
+
"banana nanny!".chars.frequencies
|
50
|
+
# {
|
51
|
+
# " " => 1,
|
52
|
+
# "! "=> 1,
|
53
|
+
# "a" => 4,
|
54
|
+
# "b" => 1,
|
55
|
+
# "n" => 5,
|
56
|
+
# "y" => 1
|
57
|
+
# }
|
58
|
+
"banana nanny!".chars.sorted_frequencies
|
59
|
+
# [
|
60
|
+
# ["n", 5],
|
61
|
+
# ["a", 4],
|
62
|
+
# [" ", 1],
|
63
|
+
# ["!", 1],
|
64
|
+
# ["b", 1],
|
65
|
+
# ["y", 1]
|
66
|
+
# ]
|
67
|
+
|
68
|
+
# sum of nothing is 0
|
69
|
+
[].sum # 0
|
70
|
+
|
71
|
+
# mean and median of nothing is undefined
|
72
|
+
[].mean # nil
|
73
|
+
[].median # nil
|
74
|
+
|
75
|
+
# modes and frequencies return empty containers
|
76
|
+
[].modes # []
|
77
|
+
[].frequencies # {}
|
78
|
+
[].sorted_frequencies # []
|
79
|
+
|
80
|
+
# if elements are preset, mean and median always return floats
|
81
|
+
[1, 2, 3].mean # 2.0
|
82
|
+
[1, 2, 3].median # 2.0
|
83
|
+
[1, 2, 3, 4].mean # 2.5
|
84
|
+
[1, 2, 3, 4].median # 2.5
|
85
|
+
|
86
|
+
# sum, mode, and frequencies preserve the object class
|
87
|
+
[1, 2, 3].sum # 6
|
88
|
+
[1.0, 2.0, 3.0].sum # 6.0
|
89
|
+
|
90
|
+
# but no guarantees on class here:
|
91
|
+
[2.0, 2, 1].modes # [2]
|
92
|
+
[2, 2.0, 1].modes # [2.0]
|
93
|
+
```
|
94
|
+
|
95
|
+
## Help make it better!
|
96
|
+
|
97
|
+
Need something added? [Open an issue](https://github.com/brianhempel/simple_stats/issues). Or, even better, code it yourself and send a pull request:
|
98
|
+
|
99
|
+
# fork it on github, then clone:
|
100
|
+
git clone git@github.com:your_username/simple_stats.git
|
101
|
+
bundle install
|
102
|
+
rspec
|
103
|
+
# hack away
|
104
|
+
git push
|
105
|
+
# then make a pull request
|
106
|
+
|
107
|
+
## License
|
108
|
+
|
109
|
+
Public domain. (I, Brian Hempel, the original author release this code to the public domain.)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,66 @@
|
|
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
|
10
|
+
end
|
11
|
+
|
12
|
+
def median
|
13
|
+
sorted = sort
|
14
|
+
count = sorted.size
|
15
|
+
i = count / 2
|
16
|
+
|
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
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
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
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
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
|
40
|
+
end
|
41
|
+
end
|
42
|
+
current_item = nil;
|
43
|
+
|
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
|
52
|
+
end
|
53
|
+
end
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
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]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/simple_stats.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "simple_stats/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "simple_stats"
|
7
|
+
s.version = SimpleStats::VERSION
|
8
|
+
s.authors = ["Brian Hempel"]
|
9
|
+
s.email = ["plasticchicken@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/brianhempel/simple_stats"
|
11
|
+
s.summary = "Simple mean, median, modes, sum, and frequencies for Ruby arrays and enumerables. Tested and sensible."
|
12
|
+
s.description = s.summary
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
# specify any dependencies here; for example:
|
20
|
+
# s.add_development_dependency "rspec"
|
21
|
+
# s.add_runtime_dependency "rest-client"
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
frequencies_expectations = {
|
4
|
+
[] => {},
|
5
|
+
[1] => { 1 => 1 },
|
6
|
+
[1.0] => { 1.0 => 1 },
|
7
|
+
[1, -1] => { -1 => 1, 1 => 1 },
|
8
|
+
[3, 1, 3] => { 3 => 2, 1 => 1 },
|
9
|
+
[3, 1, 3.0] => { 3 => 2, 1 => 1 },
|
10
|
+
[3.0, 1, 3] => { 3.0 => 2, 1 => 1 },
|
11
|
+
(0...0) => {},
|
12
|
+
(1..1) => { 1 => 1 },
|
13
|
+
(0..3) => { 0 => 1, 1 => 1, 2 => 1, 3 => 1 },
|
14
|
+
[BigDecimal("0.0")] => { BigDecimal("0.0") => 1 },
|
15
|
+
[-2, BigDecimal("1.0")] => { -2 => 1, BigDecimal("1.0") => 1 },
|
16
|
+
[:b, :c, :b, :c, :c, :a] => { :c => 3, :b => 2, :a => 1 }
|
17
|
+
}
|
18
|
+
|
19
|
+
describe Enumerable do
|
20
|
+
describe "#frequencies" do
|
21
|
+
frequencies_expectations.each do |data, expected|
|
22
|
+
it "is #{expected.inspect} for #{data.inspect}" do
|
23
|
+
frequencies = data.frequencies
|
24
|
+
frequencies.should == expected
|
25
|
+
frequencies.class.should == expected.class
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
mean_expectations = {
|
4
|
+
[] => nil,
|
5
|
+
[1] => 1.0,
|
6
|
+
[1.0] => 1.0,
|
7
|
+
[1, -1] => 0.0,
|
8
|
+
[1, 2, 3] => 2.0,
|
9
|
+
[1, 2, 3.0] => 2.0,
|
10
|
+
[2, 2, 3.0] => 7.0 / 3.0,
|
11
|
+
(0...0) => nil,
|
12
|
+
(1..1) => 1.0,
|
13
|
+
(0..3) => 1.5,
|
14
|
+
[BigDecimal("0.0")] => 0.0,
|
15
|
+
[-2, BigDecimal("1.0")] => -0.5
|
16
|
+
}
|
17
|
+
|
18
|
+
describe Enumerable do
|
19
|
+
describe "#mean" do
|
20
|
+
mean_expectations.each do |data, expected|
|
21
|
+
it "is #{expected.inspect} for #{data.inspect}" do
|
22
|
+
mean = data.mean
|
23
|
+
mean.should == expected
|
24
|
+
mean.class.should == expected.class
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
median_expectations = {
|
4
|
+
[] => nil,
|
5
|
+
[1] => 1.0,
|
6
|
+
[1.0] => 1.0,
|
7
|
+
[1, -1] => 0.0,
|
8
|
+
[1, 2, 3000] => 2.0,
|
9
|
+
[1, 2, 3000.0] => 2.0,
|
10
|
+
[2, 2, 3000.0] => 2.0,
|
11
|
+
(0...0) => nil,
|
12
|
+
(1..1) => 1.0,
|
13
|
+
(0..3) => 1.5,
|
14
|
+
[BigDecimal("0.0")] => 0.0,
|
15
|
+
[-2, BigDecimal("1.0")] => -0.5
|
16
|
+
}
|
17
|
+
|
18
|
+
describe Enumerable do
|
19
|
+
describe "#median" do
|
20
|
+
median_expectations.each do |data, expected|
|
21
|
+
it "is #{expected.inspect} for #{data.inspect}" do
|
22
|
+
median = data.median
|
23
|
+
median.should == expected
|
24
|
+
median.class.should == expected.class
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
modes_expectations = {
|
4
|
+
[] => [],
|
5
|
+
[1] => [1],
|
6
|
+
[1.0] => [1.0],
|
7
|
+
[1, -1] => [-1, 1],
|
8
|
+
[3, 1, 3] => [3],
|
9
|
+
[3, 1, 3.0] => [3],
|
10
|
+
[3.0, 1, 3] => [3.0],
|
11
|
+
(0...0) => [],
|
12
|
+
(1..1) => [1],
|
13
|
+
(0..3) => [0, 1, 2, 3],
|
14
|
+
[BigDecimal("0.0")] => [BigDecimal("0.0")],
|
15
|
+
[BigDecimal("1.0"), -2, BigDecimal("1.0")] => [BigDecimal("1.0")],
|
16
|
+
[:b, :c, :b, :c, :c, :a] => [:c]
|
17
|
+
}
|
18
|
+
|
19
|
+
describe Enumerable do
|
20
|
+
describe "#modes" do
|
21
|
+
modes_expectations.each do |data, expected|
|
22
|
+
it "is #{expected.inspect} for #{data.inspect}" do
|
23
|
+
modes = data.modes
|
24
|
+
modes.should == expected
|
25
|
+
modes.class.should == expected.class
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Enumerable do
|
4
|
+
describe "#sorted_frequencies" do
|
5
|
+
it "returns #frequencies sorted by [frequency, value]" do
|
6
|
+
enumerable = (0...0)
|
7
|
+
enumerable.stub(:frequencies) do
|
8
|
+
{
|
9
|
+
"White-breasted Nuthatch" => 1,
|
10
|
+
"Red-breasted Nuthatch" => 1,
|
11
|
+
"Brown Creeper" => 1,
|
12
|
+
"Squirrel" => 1,
|
13
|
+
"Marmelade" => 1000,
|
14
|
+
:yeah => 3,
|
15
|
+
:man => 3,
|
16
|
+
11 => 12
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
enumerable.sorted_frequencies.should == [
|
21
|
+
["Marmelade", 1000],
|
22
|
+
[11, 12],
|
23
|
+
[:man, 3],
|
24
|
+
[:yeah, 3],
|
25
|
+
["Brown Creeper", 1],
|
26
|
+
["Red-breasted Nuthatch", 1],
|
27
|
+
["Squirrel", 1],
|
28
|
+
["White-breasted Nuthatch", 1]
|
29
|
+
]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
sum_expectations = {
|
4
|
+
[] => 0, # see http://en.wikipedia.org/wiki/Empty_sum
|
5
|
+
[1] => 1,
|
6
|
+
[1.0] => 1.0,
|
7
|
+
[1, -1] => 0,
|
8
|
+
[1, 2, 3] => 6,
|
9
|
+
[1, 2, 3.0] => 6.0,
|
10
|
+
(0...0) => 0, # see http://en.wikipedia.org/wiki/Empty_sum
|
11
|
+
(1..1) => 1,
|
12
|
+
(0..3) => 6,
|
13
|
+
[BigDecimal("0.0")] => BigDecimal("0.0"),
|
14
|
+
[3, BigDecimal("1.0")] => BigDecimal("4.0")
|
15
|
+
}
|
16
|
+
|
17
|
+
describe Enumerable do
|
18
|
+
describe "#sum" do
|
19
|
+
sum_expectations.each do |data, expected|
|
20
|
+
it "is #{expected.inspect} for #{data.inspect}" do
|
21
|
+
sum = data.sum
|
22
|
+
sum.should == expected
|
23
|
+
sum.class.should == expected.class
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_stats
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Brian Hempel
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-02-10 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Simple mean, median, modes, sum, and frequencies for Ruby arrays and enumerables. Tested and sensible.
|
22
|
+
email:
|
23
|
+
- plasticchicken@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- .rspec
|
33
|
+
- .travis.yml
|
34
|
+
- Gemfile
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- lib/simple_stats.rb
|
38
|
+
- lib/simple_stats/core_ext/enumerable.rb
|
39
|
+
- lib/simple_stats/version.rb
|
40
|
+
- simple_stats.gemspec
|
41
|
+
- spec/enumerable/frequencies_spec.rb
|
42
|
+
- spec/enumerable/mean_spec.rb
|
43
|
+
- spec/enumerable/median_spec.rb
|
44
|
+
- spec/enumerable/modes_spec.rb
|
45
|
+
- spec/enumerable/sorted_frequencies_spec.rb
|
46
|
+
- spec/enumerable/sum_spec.rb
|
47
|
+
- spec/spec_helper.rb
|
48
|
+
homepage: https://github.com/brianhempel/simple_stats
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.8.15
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Simple mean, median, modes, sum, and frequencies for Ruby arrays and enumerables. Tested and sensible.
|
81
|
+
test_files:
|
82
|
+
- spec/enumerable/frequencies_spec.rb
|
83
|
+
- spec/enumerable/mean_spec.rb
|
84
|
+
- spec/enumerable/median_spec.rb
|
85
|
+
- spec/enumerable/modes_spec.rb
|
86
|
+
- spec/enumerable/sorted_frequencies_spec.rb
|
87
|
+
- spec/enumerable/sum_spec.rb
|
88
|
+
- spec/spec_helper.rb
|