├── .gitignore ├── .rspec ├── .rvmrc ├── .travis.yml ├── Gemfile ├── Guardfile ├── README.md ├── Rakefile ├── easystats.gemspec ├── lib ├── easystats.rb └── easystats │ └── version.rb └── spec ├── lib └── easystats_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/* 2 | *.gem 3 | .bundle 4 | Gemfile.lock 5 | coverage/ 6 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format progress 3 | -------------------------------------------------------------------------------- /.rvmrc: -------------------------------------------------------------------------------- 1 | rvm 1.9.3 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 1.8.7 3 | - 1.9.2 4 | - 1.9.3 5 | - jruby-18mode 6 | - jruby-19mode 7 | - jruby-head 8 | - rbx-18mode 9 | - rbx-19mode 10 | - ree 11 | - ruby-head 12 | matrix: 13 | allow_failures: 14 | - rvm: jruby-head 15 | - rvm: ruby-head 16 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | gemspec 4 | 5 | group :development, :test do 6 | group :darwin do 7 | gem 'rb-fsevent' 8 | gem 'growl' 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard 'rspec', :version => 2, :rvm => %w[1.9.3] do 2 | watch(%r{^spec/.+_spec\.rb$}) 3 | watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } 4 | watch('spec/spec_helper.rb') { "spec" } 5 | end 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easystats [![Build Status](https://secure.travis-ci.org/mgrigajtis/easystats.png)](https://secure.travis-ci.org/mgrigajtis/easystats) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/mgrigajtis/easystats) 2 | 3 | [![Join the chat at https://gitter.im/mgrigajtis/easystats](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mgrigajtis/easystats?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | > Created by [Matthew Grigajtis](http://www.matthewgrigajtis.com) 6 | 7 | Provides easy to use statistical functions to use on an array 8 | 9 | ## Install 10 | 11 | In your shell: 12 | 13 | ```sh 14 | gem install easystats 15 | ``` 16 | 17 | or in your Gemfile: 18 | 19 | ```rb 20 | gem 'easystats' 21 | ``` 22 | 23 | ## Example 24 | 25 | ```rb 26 | require 'easystats' 27 | 28 | array = [4, 8, 15, 16, 23, 42, 42] 29 | 30 | %w[ 31 | average 32 | median 33 | mode 34 | range 35 | standard_deviation 36 | sum 37 | variance 38 | weighted_moving_average 39 | ].each do |method| 40 | puts "#{method}: #{array.send(method.to_sym)}" 41 | end 42 | ``` 43 | 44 | This will result in: 45 | 46 | ```sh 47 | average: 21.428571428571427 48 | median: 16 49 | mode: 42 50 | range: 38 51 | standard_deviation: 15.295501984321435 52 | sum: 150 53 | variance: 200.53061224489798 54 | weighted_moving_average: 30.476190476190474 55 | ``` 56 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | require 'rake' 3 | require 'rake/testtask' 4 | 5 | Bundler::GemHelper.install_tasks 6 | 7 | require 'rspec/core/rake_task' 8 | 9 | desc 'Default: run specs.' 10 | task :default => :spec 11 | 12 | desc "Run specs" 13 | RSpec::Core::RakeTask.new 14 | 15 | -------------------------------------------------------------------------------- /easystats.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require "easystats/version" 4 | 5 | Gem::Specification.new do |s| 6 | s.name = "easystats" 7 | s.version = Easystats::VERSION 8 | s.platform = Gem::Platform::RUBY 9 | s.authors = ["Matthew Grigajtis", "Justin Campbell"] 10 | s.email = ["matthew@grigajtis.org", "justin@justincampbell.me"] 11 | s.homepage = "https://github.com/mgrigajtis/easystats" 12 | s.summary = %q{Easy to use statistics functions} 13 | s.description = %q{This gem contains statistics functions that are very easy to use. Much easier and much more complete than many of the other statistical gems available out there. If you need a feature added, send me a message on Github!} 14 | 15 | s.rubyforge_project = "easystats" 16 | 17 | s.files = `git ls-files`.split("\n") 18 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 19 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 20 | s.require_paths = ["lib"] 21 | 22 | s.add_development_dependency "guard-rspec" 23 | s.add_development_dependency "rake" 24 | s.add_development_dependency "rspec" 25 | s.add_development_dependency "simplecov" if RUBY_DESCRIPTION.start_with? "ruby 1.9" 26 | end 27 | -------------------------------------------------------------------------------- /lib/easystats.rb: -------------------------------------------------------------------------------- 1 | class Array 2 | def mean 3 | return if empty? 4 | 5 | sum / count.to_f 6 | end unless method_defined? :mean 7 | 8 | alias_method :average, :mean unless method_defined? :average 9 | 10 | def weighted_moving_average 11 | return if empty? 12 | return first if one? 13 | weighted_sum = 0 14 | sum = 0 15 | index = 0 16 | each do |element| 17 | weighted_sum = weighted_sum + (index * element) 18 | sum += index 19 | index += 1 20 | end 21 | weighted_sum.to_f / sum 22 | end unless method_defined? :weighted_moving_average 23 | 24 | def median 25 | return if empty? 26 | 27 | data = sort 28 | 29 | halfway = data.count / 2 30 | 31 | if data.count.even? 32 | (data[halfway] + data[halfway - 1]) / 2.0 33 | else 34 | data[halfway] 35 | end 36 | end unless method_defined? :median 37 | 38 | def mode 39 | return if empty? 40 | return first if one? 41 | 42 | frequencies = each_with_object(Hash.new(0)) { |v, k| k[v] += 1 } 43 | mode_value, mode_frequency = frequencies.max_by { |_, freq| freq } 44 | 45 | # Check if any other value has the same frequency 46 | return if frequencies.values.count(mode_frequency) > 1 47 | 48 | mode_value 49 | end unless method_defined? :mode 50 | 51 | def probability_distribution 52 | return if empty? 53 | 54 | total = count.to_f 55 | 56 | uniq.inject({}) { |result, item| 57 | result.update({ item => count(item) / total }) 58 | } 59 | end unless method_defined? :probability_distribution 60 | 61 | def range 62 | return if empty? 63 | 64 | max - min 65 | end unless method_defined? :range 66 | 67 | def standard_deviation 68 | return if empty? 69 | return 0 if one? 70 | 71 | Math::sqrt sum_of_deviations_squared / (count - 1) 72 | end unless method_defined? :standard_deviation 73 | 74 | def sum 75 | reduce :+ 76 | end unless method_defined? :sum 77 | 78 | def variance 79 | return if empty? 80 | 81 | sum_of_deviations_squared / count.to_f 82 | end unless method_defined? :variance 83 | 84 | protected 85 | 86 | def sum_of_deviations_squared 87 | mean = self.mean 88 | 89 | inject(0) { |total, number| total + ((number - mean) ** 2) } 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/easystats/version.rb: -------------------------------------------------------------------------------- 1 | module Easystats 2 | VERSION = "0.5.1" 3 | end 4 | -------------------------------------------------------------------------------- /spec/lib/easystats_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Array do 4 | DELTA = 1.0e-14 5 | 6 | it { should respond_to :average } 7 | 8 | build_stats_spec [], { 9 | :mean => nil, 10 | :median => nil, 11 | :mode => nil, 12 | :range => nil, 13 | :standard_deviation => nil, 14 | :sum => nil, 15 | :variance => nil, 16 | :probability_distribution => nil, 17 | :weighted_moving_average => nil 18 | } 19 | 20 | build_stats_spec [1,1,1,2,2], { 21 | :probability_distribution => { 22 | 1 => 0.6, 23 | 2 => 0.4 24 | }, 25 | :weighted_moving_average => 1.7 26 | } 27 | 28 | build_stats_spec [0], { 29 | :mean => 0, 30 | :median => 0, 31 | :mode => 0, 32 | :range => 0, 33 | :standard_deviation => 0, 34 | :sum => 0, 35 | :variance => 0.0, 36 | :probability_distribution => { 0 => 1.0 }, 37 | :weighted_moving_average => 0.0 38 | } 39 | 40 | build_stats_spec [1], { 41 | :mean => 1, 42 | :median => 1, 43 | :mode => 1, 44 | :range => 0, 45 | :standard_deviation => 0, 46 | :sum => 1, 47 | :variance => 0.0, 48 | :probability_distribution => { 1 => 1.0 }, 49 | :weighted_moving_average => 1.0 50 | } 51 | 52 | build_stats_spec [1,2], { 53 | :mean => 1.5, 54 | :median => 1.5, 55 | :mode => nil, 56 | :range => 1, 57 | :standard_deviation => 0.707106781186548, 58 | :sum => 3, 59 | :variance => 0.25, 60 | :probability_distribution => { 1 => 0.5, 2 => 0.5 }, 61 | :weighted_moving_average => 2.0 62 | } 63 | 64 | build_stats_spec [1,2,3], { 65 | :mean => 2, 66 | :median => 2, 67 | :mode => nil, 68 | :range => 2, 69 | :standard_deviation => 1, 70 | :sum => 6, 71 | :variance => 2.0 / 3.0, 72 | :probability_distribution => { 1 => 0.3333333333333333, 2 => 0.3333333333333333, 3 => 0.3333333333333333 }, 73 | :weighted_moving_average => 2.6666666666666665 74 | } 75 | 76 | build_stats_spec [1,2,3,4], { 77 | :mean => 2.5, 78 | :median => 2.5, 79 | :mode => nil, 80 | :range => 3, 81 | :standard_deviation => 1.29099444873581, 82 | :sum => 10, 83 | :variance => 1.25, 84 | :probability_distribution => {1 => 0.25, 2 => 0.25, 3 => 0.25, 4 => 0.25 }, 85 | :weighted_moving_average => 3.3333333333333335 86 | 87 | } 88 | 89 | build_stats_spec [1,1,2,2], { 90 | :mean => 1.5, 91 | :median => 1.5, 92 | :mode => nil, 93 | :range => 1, 94 | :standard_deviation => 0.5773502691896257, 95 | :sum => 6, 96 | :variance => 0.25, 97 | :probability_distribution => { 1=>0.5, 2=>0.5 }, 98 | :weighted_moving_average => 1.8333333333333333 99 | } 100 | 101 | build_stats_spec [1,2,2,4], { 102 | :mean => 2.25, 103 | :median => 2, 104 | :mode => 2, 105 | :range => 3, 106 | :standard_deviation => 1.2583057392117916, 107 | :sum => 9, 108 | :variance => 1.1875, 109 | :probability_distribution => { 1 => 0.25, 2 => 0.5, 4 => 0.25 }, 110 | :weighted_moving_average => 3.0 111 | } 112 | 113 | build_stats_spec [1,3,3,4], { 114 | :mean => 2.75, 115 | :median => 3, 116 | :mode => 3, 117 | :range => 3, 118 | :standard_deviation => 1.2583057392117916, 119 | :sum => 11, 120 | :variance => 1.1875, 121 | :probability_distribution => { 1 => 0.25, 3 => 0.5, 4 => 0.25 }, 122 | :weighted_moving_average => 3.5 123 | } 124 | 125 | build_stats_spec (0..100).to_a, { 126 | :mean => 50, 127 | :median => 50, 128 | :mode => nil, 129 | :range => 100, 130 | :standard_deviation => 29.300170647967224, 131 | :sum => 5050, 132 | :variance => 850.0, 133 | :weighted_moving_average => 67 134 | } 135 | 136 | build_stats_spec (1..100).to_a, { 137 | :mean => 50.5, 138 | :median => 50.5, 139 | :mode => nil, 140 | :range => 99, 141 | :standard_deviation => 29.011491975882016, 142 | :sum => 5050, 143 | :variance => 833.25, 144 | :weighted_moving_average => 67.33333333333333 145 | } 146 | 147 | build_stats_spec [-1,0,1], { 148 | :mean => 0, 149 | :median => 0, 150 | :mode => nil, 151 | :range => 2, 152 | :standard_deviation => 1, 153 | :sum => 0, 154 | :variance => 0.6666666666666666, 155 | :probability_distribution => { -1 => 0.3333333333333333, 0 => 0.3333333333333333, 1 => 0.3333333333333333 }, 156 | :weighted_moving_average => 0.6666666666666666 157 | } 158 | 159 | build_stats_spec [1,2.5], { 160 | :mean => 1.75, 161 | :median => 1.75, 162 | :mode => nil, 163 | :range => 1.5, 164 | :standard_deviation => 1.0606601717798212, 165 | :sum => 3.5, 166 | :variance =>0.5625, 167 | :probability_distribution => { 1 => 0.5, 2.5 => 0.5 }, 168 | :weighted_moving_average => 2.5 169 | } 170 | end 171 | 172 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | puts RUBY_DESCRIPTION 2 | 3 | if RUBY_DESCRIPTION.start_with? "ruby 1.9" 4 | require 'simplecov' 5 | SimpleCov.start 6 | end 7 | 8 | require 'easystats' 9 | 10 | def build_stats_spec(array, expectations) 11 | expectations.each do |method, expectation| 12 | describe "##{method}" do 13 | context "when #{array.inspect}" do 14 | if expectation && method == :standard_deviation 15 | it "should be within #{DELTA} of #{expectation.inspect}" do 16 | array.send(method).should be_within(DELTA).of(expectation) 17 | end 18 | elsif expectation && method == :probability_distribution 19 | it "should return hash #{expectation.inspect}" do 20 | array.send(method).each do |key,value| 21 | value.should be_within(DELTA).of(expectation[key]) 22 | end 23 | end 24 | else 25 | it "should be #{expectation.inspect}" do 26 | array.send(method).should == expectation 27 | end 28 | end 29 | end 30 | end 31 | end 32 | end 33 | 34 | --------------------------------------------------------------------------------