Skip to content

Commit 7040f39

Browse files
author
David Roberts
committed
Working gem
1 parent 1a82650 commit 7040f39

File tree

12 files changed

+216
-34
lines changed

12 files changed

+216
-34
lines changed

.ruby-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2.1.3

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
source 'https://rubygems.org'
22

3-
# Specify your gem's dependencies in activerecord_pg_histogram.gemspec
3+
# Specify your gem's dependencies in pg_histogram.gemspec
44
gemspec

README.md

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,51 @@
1-
# ActiverecordPgHistogram
1+
# PostgreSQL Histogram (for ActiveRecord)
2+
3+
This gem allows for you to efficiently create a histogram from large data sets in your Rails applications.
4+
5+
It uses PostgreSQL's [width_bucket](http://www.postgresql.org/docs/9.3/static/functions-math.html) function to handle the majority of the processing in the database, and only requires 3 database queries.
6+
27

3-
TODO: Write a gem description
48

59
## Installation
610

711
Add this line to your application's Gemfile:
812

9-
gem 'activerecord_pg_histogram'
13+
gem 'pg_histogram'
1014

1115
And then execute:
1216

1317
$ bundle
1418

1519
Or install it yourself as:
1620

17-
$ gem install activerecord_pg_histogram
21+
$ gem install pg_histogram
1822

1923
## Usage
2024

21-
TODO: Write usage instructions here
25+
Create a Histogram object using the following there parameters:
26+
1. ActiveRecord query to use
27+
2. Name of column to count frequency of
28+
3. Bucket size (OPTIONAL - default is 0.5)
29+
30+
31+
histogram = PgHistogram::Histogram.new(Widget.all, 'price', 0.5)
32+
33+
34+
Call the results method to retrieve a Hash of bucket minimums and frequency counts
35+
36+
# create sample data
37+
5.times do { Widget.create(price: 1.2) }
38+
10.times do { Widget.create(price: 2.9 ) }
39+
40+
# get the results
41+
@histogram_data = histogram.results
42+
=> {1.0=>5, 2.5=>10}
43+
44+
45+
The results can be used by your favorite charting libary, such as [Chartkick](https://github.com/ankane/chartkick), to plot the data.
46+
47+
<%= column_chart @histogram_data %>
2248

23-
## Contributing
49+
## Dependencies
2450

25-
1. Fork it
26-
2. Create your feature branch (`git checkout -b my-new-feature`)
27-
3. Commit your changes (`git commit -am 'Add some feature'`)
28-
4. Push to the branch (`git push origin my-new-feature`)
29-
5. Create new Pull Request
51+
This gem has been tested with Ruby 2.1.3 and ActiveRecord 4.1.6. Please open an issue or PR if you experience issues with other versions.

lib/activerecord_pg_histogram.rb

Lines changed: 0 additions & 5 deletions
This file was deleted.

lib/activerecord_pg_histogram/histogram.rb

Whitespace-only changes.

lib/activerecord_pg_histogram/version.rb

Lines changed: 0 additions & 3 deletions
This file was deleted.

lib/pg_histogram.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
require 'pg_histogram/version'
2+
require 'pg_histogram/histogram'
3+
4+
module PgHistogram
5+
end

lib/pg_histogram/histogram.rb

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
module PgHistogram
2+
class Histogram
3+
attr_reader :query, :column, :bucket_size
4+
5+
BUCKET_COL = 'bucket'
6+
FREQUENCY_COL = 'frequency'
7+
ROUND_METHODS_BY_DIRECTION = {
8+
nil => :round,
9+
down: :floor,
10+
up: :ceil
11+
}
12+
13+
# column_name name must be safe for SQL injection
14+
def initialize(query, column_name, bucket_size = 0.5)
15+
@query = query
16+
@column = column_name.to_s
17+
@bucket_size = bucket_size
18+
end
19+
20+
# returns histogram as hash
21+
# bucket minimum as a key
22+
# frequency as value
23+
def results
24+
# error handling case
25+
if max == min
26+
{ min => query.where("#{column} = ?", min).count }
27+
else
28+
labeled_histogram
29+
end
30+
end
31+
32+
def min
33+
@min ||= round_to_increment(query.minimum(column), :down)
34+
end
35+
36+
def max
37+
@max ||= round_to_increment(query.maximum(column), :up)
38+
end
39+
40+
private
41+
42+
def num_buckets
43+
@buckets ||= ((max - min) / bucket_size).to_i
44+
end
45+
46+
# returns the bucket label (minimum which can be in bucket) based on bucket #
47+
def bucket_num_to_label(bucket_num)
48+
min + bucket_size * (bucket_num - 1)
49+
end
50+
51+
# rounds to the nearest bucket_size increment
52+
# can optionally pass :up or :down to always round in one direction
53+
def round_to_increment(num, direction = nil)
54+
return 0 if num.nil?
55+
round_method = ROUND_METHODS_BY_DIRECTION[direction]
56+
denominator = 1 / bucket_size
57+
(num * denominator).send(round_method) / denominator.to_f
58+
end
59+
60+
# executes the query and converts bucket numbers to minimum step in bucket
61+
def labeled_histogram
62+
query_for_buckets.each_with_object({}) do |row, results|
63+
results[bucket_num_to_label(row[BUCKET_COL].to_i)] = row[FREQUENCY_COL].to_i \
64+
unless row[BUCKET_COL].nil?
65+
end
66+
end
67+
68+
def query_for_buckets
69+
ActiveRecord::Base.connection.execute(
70+
<<-SQL
71+
SELECT width_bucket(#{column}, #{min}, #{max}, #{num_buckets}) as #{BUCKET_COL},
72+
count(*) as #{FREQUENCY_COL}
73+
FROM (#{subquery.to_sql}) as subq_results
74+
GROUP BY #{BUCKET_COL}
75+
ORDER BY #{BUCKET_COL}
76+
SQL
77+
)
78+
end
79+
80+
# use passed AR query as a subquery to not interfere with group clause
81+
def subquery
82+
# override default order
83+
query.select(column).order('1')
84+
end
85+
end
86+
end

lib/pg_histogram/version.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module PgHistogram
2+
VERSION = "0.1"
3+
end

activerecord_pg_histogram.gemspec renamed to pg_histogram.gemspec

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# coding: utf-8
22
lib = File.expand_path('../lib', __FILE__)
33
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4-
require 'activerecord_pg_histogram/version'
4+
require 'pg_histogram/version'
55

66
Gem::Specification.new do |spec|
7-
spec.name = "activerecord_pg_histogram"
8-
spec.version = ActiverecordPgHistogram::VERSION
7+
spec.name = "pg_histogram"
8+
spec.version = PgHistogram::VERSION
99
spec.authors = ["David Roberts"]
1010
spec.email = ["[email protected]"]
1111
spec.description = %q{Creates a Histogram fron an ActiveRecord query}
@@ -22,5 +22,4 @@ Gem::Specification.new do |spec|
2222
spec.add_dependency "pg"
2323
spec.add_development_dependency "bundler", "~> 1.3"
2424
spec.add_development_dependency "rake"
25-
spec.add_development_dependency "factory_girl"
2625
end

0 commit comments

Comments
 (0)