ar_result_calculations 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ar_result_calculations.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 [name of plugin creator]
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.
data/README.textile ADDED
@@ -0,0 +1,35 @@
1
+ h1. Description
2
+
3
+ p. ar_result_calculations adds methods to Array for when Array is an ActiveRecord result set. For example:
4
+
5
+ bc. Product.all.sum(:price)
6
+ Product.all.average(:price)
7
+ Product.all.regression(:sales_volumn)
8
+
9
+ p. Will return the sum of the attribute :price from all rows. Note this is not the same as Product.sum(:price) which will create an SQL statement that does the summing.
10
+
11
+ h2. Methods created on Array
12
+
13
+ p. All methods take one parameter (column name).
14
+
15
+ |_. Method|_. Description|
16
+ |sum|Sum the given column. Delegates to super() if not an AR result set|
17
+ |mean|Mean of the given column. Delegates to super() if not an AR result set. Aliases of **avg** and **average**|
18
+ |count|Count the given column. Delegates to super() if not an AR result set|
19
+ |min|Min the given column. Delegates to super() if not an AR result set|
20
+ |max|Max the given column. Delegates to super() if not an AR result set|
21
+ |regression|Ordinary Least Squares regression on the given column. Returns the regression as an array|
22
+ |slope|Returns the slope of a regression on a given column|
23
+ |make_numeric_|Coerces a column to be numeric. Useful if you have derived columns returned from a query that are not in the model definition and hence are otherwise returned as strings. Returns **self** so is composable|
24
+
25
+ h1. License
26
+
27
+ (The MIT License)
28
+
29
+ Copyright © 2010 Kip Cole
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
32
+
33
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
34
+
35
+ THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ar_result_calculations/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ar_result_calculations"
7
+ s.version = ArResultCalculations::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Kip Cole"]
10
+ s.email = ["kipcole9@gmail.com"]
11
+ s.homepage = "http://github.com/kipcole9/ar_result_calculations"
12
+ s.summary = %q{Calculations on ActiveRecord result sets.}
13
+ s.description = <<-EOF
14
+ Defines Array#calculation methods for ActiveRecord result sets. Provides
15
+ #sum, #min, #max, #count, #mean, #regression, #slope. Delegates to super()
16
+ if not an AR result set where appropriate.
17
+ EOF
18
+
19
+ s.rubyforge_project = "ar_result_calculations"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,6 @@
1
+ require File.dirname(__FILE__) + '/ar_result_calculations/ar_result_calculations.rb'
2
+ Array.send :include, ArResultCalculations::Calculations
3
+
4
+ module ArResultCalculations
5
+
6
+ end
@@ -0,0 +1,159 @@
1
+ module ArResultCalculations
2
+ module Calculations
3
+ def self.included(base)
4
+ base.class_eval do
5
+ extend ClassMethods
6
+ include InstanceMethods
7
+ end
8
+ end
9
+
10
+ module InstanceMethods
11
+ # Return the sum of a column from an active record result set
12
+ # Calls super() if not a result set
13
+ #
14
+ # column: The column name to sum
15
+ def sum(column = nil)
16
+ return super() unless column && first && first.class.respond_to?(:descends_from_active_record?)
17
+ inject( 0 ) { |sum, x| x[column].nil? ? sum : sum + x[column] }
18
+ end
19
+
20
+ # Return the average of a column from an active record result set
21
+ # Calls super() if not a result set
22
+ #
23
+ # column: The column name to average
24
+ def mean(column = nil)
25
+ return super() unless column && first && first.class.respond_to?(:descends_from_active_record?)
26
+ (length > 0) ? sum(column) / length : 0
27
+ end
28
+ alias :avg :mean
29
+ alias :average :mean
30
+
31
+ # Return the count of a column from an active record result set
32
+ # Calls super() if not a result set. nil values are not counted
33
+ #
34
+ # column: The column name to count
35
+ def count(column = nil)
36
+ return super() unless column && first && first.class.respond_to?(:descends_from_active_record?)
37
+ inject( 0 ) { |sum, x| x[column].nil? ? sum : sum + 1 }
38
+ end
39
+
40
+ # Return the max of a column from an active record result set
41
+ # Calls super() if not a result set
42
+ #
43
+ # column: The column name to max
44
+ def max(column = nil)
45
+ return super() unless column && first && first.class.respond_to?(:descends_from_active_record?)
46
+ map(&column.to_sym).max
47
+ end
48
+ alias :maximum :max
49
+
50
+ # Return the min of a column from an active record result set
51
+ # Calls super() if not a result set
52
+ #
53
+ # column: The column name to sum
54
+ def min(column = nil)
55
+ return super() unless column && first && first.class.respond_to?(:descends_from_active_record?)
56
+ map(&column.to_sym).min
57
+ end
58
+ alias :minimum :min
59
+
60
+ # Return a regression (OLS) of a column from an active record result set
61
+ # Calls super() if not a result set
62
+ #
63
+ # column: The column name to regress
64
+ def regression(column = nil)
65
+ return nil unless first
66
+ unless is_numeric?(first)
67
+ raise ArgumentError, "Regression needs an array of ActiveRecord objects" unless column && first && first.class.respond_to?(:descends_from_active_record?)
68
+ series = map { |x| x[column] }
69
+ end
70
+ Array::LinearRegression.new(series || self).fit
71
+ end
72
+
73
+ # Return the slope of a regression on a column from an active record result set
74
+ # Calls super() if not a result set
75
+ #
76
+ # column: The column name to regress
77
+ def slope(column = nil)
78
+ return nil unless first
79
+ unless is_numeric?(first)
80
+ column ||= first_numeric_column
81
+ series = map { |x| x[column] }
82
+ end
83
+ Array::LinearRegression.new(series || self).slope
84
+ end
85
+ alias :trend :slope
86
+
87
+ # Force a column to be numeric. Useful if you have derived
88
+ # columns from a query that is not part of the base model.
89
+ #
90
+ # column: The column name to sum
91
+ #
92
+ # returns self so you can compose other methods.
93
+ def make_numeric(column)
94
+ return self unless column && first && first.class.respond_to?(:descends_from_active_record?)
95
+ each do |row|
96
+ next if is_numeric?(row[column])
97
+ row[column] = row[column] =~ /[-+]?[0-9]+(\.[0-9]+)/ ? row[column].to_f : row[column].to_i
98
+ end
99
+ self
100
+ end
101
+ alias :coerce_numeric :make_numeric
102
+
103
+ private
104
+ def first_numeric_column
105
+ raise ArgumentError, "Slope needs an array of ActiveRecord objects" unless first && first.class.respond_to?(:descends_from_active_record?)
106
+ first.attributes.each {|attribute, value| return attribute if is_numeric?(value) }
107
+ raise ArgumentError, "Slope could not detect a numberic attribute. Please provide an attribute name as an argument"
108
+ end
109
+
110
+ def is_numeric?(val)
111
+ is_integer?(val) || is_float?(val)
112
+ end
113
+
114
+ def is_integer?(val)
115
+ val.is_a?(Fixnum) || val.is_a?(Integer) || val.is_a?(Bignum)
116
+ end
117
+
118
+ def is_float?(val)
119
+ val.is_a?(Float) || val.is_a?(Rational)
120
+ end
121
+ end
122
+
123
+ module ClassMethods
124
+ # Courtesy of http://blog.internautdesign.com/2008/4/21/simple-linear-regression-best-fit
125
+ class Array::LinearRegression
126
+ attr_accessor :slope, :offset
127
+
128
+ def initialize dx, dy=nil
129
+ @size = dx.size
130
+ dy,dx = dx,axis() unless dy # make 2D if given 1D
131
+ raise ArgumentError, "[regression] Arguments are not same length!" unless @size == dy.size
132
+ sxx = sxy = sx = sy = 0
133
+ dx.zip(dy).each do |x,y|
134
+ sxy += x*y
135
+ sxx += x*x
136
+ sx += x
137
+ sy += y
138
+ end
139
+ @slope = ( @size * sxy - sx * sy ) / ( @size * sxx - sx * sx ) rescue 0
140
+ @offset = (sy - @slope * sx) / @size
141
+ end
142
+
143
+ def fit
144
+ return axis.map{|data| predict(data) }
145
+ end
146
+
147
+ def predict( x )
148
+ y = @slope * x + @offset
149
+ end
150
+
151
+ def axis
152
+ (0...@size).to_a
153
+ end
154
+ end
155
+
156
+ end
157
+ end
158
+ end
159
+
@@ -0,0 +1,3 @@
1
+ module ArResultCalculations
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ar_result_calculations
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Kip Cole
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-30 00:00:00 +08:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: " Defines Array#calculation methods for ActiveRecord result sets. Provides\n #sum, #min, #max, #count, #mean, #regression, #slope. Delegates to super()\n if not an AR result set where appropriate.\n"
23
+ email:
24
+ - kipcole9@gmail.com
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - MIT-LICENSE
35
+ - README.textile
36
+ - Rakefile
37
+ - ar_result_calculations.gemspec
38
+ - lib/ar_result_calculations.rb
39
+ - lib/ar_result_calculations/ar_result_calculations.rb
40
+ - lib/ar_result_calculations/version.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/kipcole9/ar_result_calculations
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options: []
47
+
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 3
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ requirements: []
69
+
70
+ rubyforge_project: ar_result_calculations
71
+ rubygems_version: 1.3.7
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: Calculations on ActiveRecord result sets.
75
+ test_files: []
76
+