rails-data-explorer 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +5 -1
- data/README.md +11 -0
- data/Rakefile +62 -0
- data/doc/how_to/release.md +23 -0
- data/doc/how_to/trouble_when_packaging_assets.md +8 -0
- data/lib/rails-data-explorer-no-rails.rb +42 -0
- data/lib/rails-data-explorer.rb +5 -9
- data/lib/rails-data-explorer/chart/box_plot.rb +5 -1
- data/lib/rails-data-explorer/chart/box_plot_group.rb +22 -5
- data/lib/rails-data-explorer/chart/contingency_table.rb +45 -10
- data/lib/rails-data-explorer/chart/histogram_categorical.rb +104 -3
- data/lib/rails-data-explorer/chart/histogram_quantitative.rb +99 -2
- data/lib/rails-data-explorer/chart/histogram_temporal.rb +10 -55
- data/lib/rails-data-explorer/chart/parallel_coordinates.rb +4 -0
- data/lib/rails-data-explorer/chart/parallel_set.rb +4 -0
- data/lib/rails-data-explorer/chart/pie_chart.rb +89 -8
- data/lib/rails-data-explorer/chart/scatterplot.rb +110 -8
- data/lib/rails-data-explorer/chart/stacked_bar_chart_categorical_percent.rb +133 -14
- data/lib/rails-data-explorer/data_series.rb +37 -2
- data/lib/rails-data-explorer/data_type/categorical.rb +72 -2
- data/lib/rails-data-explorer/data_type/quantitative.rb +41 -12
- data/lib/rails-data-explorer/data_type/quantitative/temporal.rb +3 -2
- data/lib/rails-data-explorer/exploration.rb +5 -1
- data/lib/rails-data-explorer/utils/data_binner.rb +31 -0
- data/lib/rails-data-explorer/utils/data_quantizer.rb +66 -0
- data/lib/rails_data_explorer.rb +133 -0
- data/rails-data-explorer.gemspec +4 -4
- data/spec/helper.rb +7 -0
- data/spec/helper_no_rails.rb +10 -0
- data/spec/rails-data-explorer/data_series_spec.rb +45 -0
- data/spec/rails-data-explorer/data_type/categorical_spec.rb +34 -0
- data/spec/rails-data-explorer/exploration_spec.rb +55 -0
- data/spec/rails-data-explorer/utils/data_binner_spec.rb +29 -0
- data/spec/rails-data-explorer/utils/data_quantizer_spec.rb +71 -0
- data/vendor/assets/javascripts/packaged/rails-data-explorer.min.js +1 -0
- data/vendor/assets/javascripts/rails-data-explorer.js +6 -5
- data/vendor/assets/javascripts/{d3.boxplot.js → sources/d3.boxplot.js} +10 -3
- data/vendor/assets/javascripts/{d3.parcoords.js → sources/d3.parcoords.js} +1 -1
- data/vendor/assets/javascripts/{d3.parsets.js → sources/d3.parsets.js} +3 -3
- data/vendor/assets/javascripts/{d3.v3.js → sources/d3.v3.js} +0 -0
- data/vendor/assets/javascripts/{nv.d3.js → sources/nv.d3.js} +0 -0
- data/vendor/assets/javascripts/sources/vega.js +7040 -0
- data/vendor/assets/stylesheets/packaged/rails-data-explorer.min.css +9 -0
- data/vendor/assets/stylesheets/rails-data-explorer.css +7 -7
- data/vendor/assets/stylesheets/{bootstrap-theme.css → sources/bootstrap-theme.css} +0 -0
- data/vendor/assets/stylesheets/{bootstrap.css → sources/bootstrap.css} +0 -0
- data/vendor/assets/stylesheets/{d3.boxplot.css → sources/d3.boxplot.css} +0 -0
- data/vendor/assets/stylesheets/{d3.parcoords.css → sources/d3.parcoords.css} +0 -0
- data/vendor/assets/stylesheets/{d3.parsets.css → sources/d3.parsets.css} +0 -0
- data/vendor/assets/stylesheets/{nv.d3.css → sources/nv.d3.css} +0 -0
- data/vendor/assets/stylesheets/{rde-default-style.css → sources/rde-default-style.css} +0 -0
- metadata +65 -28
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -7,6 +7,10 @@ data in your app using charts and statistics.
|
|
7
7
|
Make sure to go to the thorough [documentation](http://rails-data-explorer.clearcove.ca)
|
8
8
|
to find out more!
|
9
9
|
|
10
|
+
## IMPORTANT NOTE
|
11
|
+
|
12
|
+
This gem is under active development and will see significant changes until it's
|
13
|
+
officially released.
|
10
14
|
|
11
15
|
### Installation
|
12
16
|
|
@@ -32,6 +36,13 @@ or with bundler in your Gemfile:
|
|
32
36
|
* Chart -
|
33
37
|
|
34
38
|
|
39
|
+
### Dependencies
|
40
|
+
|
41
|
+
* ActionView >= 3.0
|
42
|
+
* ActiveSupport >= 3.0
|
43
|
+
* Asset pipeline (for batteries included, otherwise you'll have to pull in a number of assets manually)
|
44
|
+
* jQuery??
|
45
|
+
|
35
46
|
### Resources
|
36
47
|
|
37
48
|
* [Documentation](http://rails-data-explorer.clearcove.ca)
|
data/Rakefile
CHANGED
@@ -16,3 +16,65 @@ Rake::TestTask.new do |t|
|
|
16
16
|
end
|
17
17
|
|
18
18
|
task :default => ['test']
|
19
|
+
|
20
|
+
require 'yui/compressor'
|
21
|
+
|
22
|
+
namespace :rde do
|
23
|
+
|
24
|
+
def minify_js(output_file_name, file_list)
|
25
|
+
uncompressed_source = ''
|
26
|
+
file_list.each do |file|
|
27
|
+
uncompressed_source << File.read(file)
|
28
|
+
end
|
29
|
+
compressor = YUI::JavaScriptCompressor.new
|
30
|
+
compressed_output = compressor.compress(uncompressed_source)
|
31
|
+
File.write(output_file_name, compressed_output)
|
32
|
+
end
|
33
|
+
|
34
|
+
def minify_css(output_file_name, file_list)
|
35
|
+
uncompressed_source = ''
|
36
|
+
file_list.each do |file|
|
37
|
+
uncompressed_source << File.read(file)
|
38
|
+
end
|
39
|
+
compressor = YUI::CssCompressor.new
|
40
|
+
compressed_output = compressor.compress(uncompressed_source)
|
41
|
+
File.write(output_file_name, compressed_output)
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "minify"
|
45
|
+
task :minify => [:minify_js, :minify_css]
|
46
|
+
|
47
|
+
desc "minify javascript"
|
48
|
+
task :minify_js do
|
49
|
+
base_path = File.expand_path("../vendor/assets/javascripts", __FILE__)
|
50
|
+
minify_js(
|
51
|
+
File.join(base_path, 'packaged/rails-data-explorer.min.js'),
|
52
|
+
[
|
53
|
+
File.join(base_path, 'sources/d3.v3.js'),
|
54
|
+
# File.join(base_path, 'sources/nv.d3.js'),
|
55
|
+
File.join(base_path, 'sources/d3.boxplot.js'),
|
56
|
+
File.join(base_path, 'sources/d3.parcoords.js'),
|
57
|
+
File.join(base_path, 'sources/d3.parsets.js'),
|
58
|
+
File.join(base_path, 'sources/vega.js'),
|
59
|
+
]
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "minify css"
|
64
|
+
task :minify_css do
|
65
|
+
base_path = File.expand_path("../vendor/assets/stylesheets", __FILE__)
|
66
|
+
minify_css(
|
67
|
+
File.join(base_path, 'packaged/rails-data-explorer.min.css'),
|
68
|
+
[
|
69
|
+
File.join(base_path, 'sources/bootstrap-theme.css'),
|
70
|
+
File.join(base_path, 'sources/bootstrap.css'),
|
71
|
+
File.join(base_path, 'sources/d3.boxplot.css'),
|
72
|
+
File.join(base_path, 'sources/d3.parcoords.css'),
|
73
|
+
File.join(base_path, 'sources/d3.parsets.css'),
|
74
|
+
# File.join(base_path, 'sources/nv.d3.css'),
|
75
|
+
File.join(base_path, 'sources/rde-default-style.css'),
|
76
|
+
]
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Workflow to Maintain This Gem
|
2
|
+
=============================
|
3
|
+
|
4
|
+
I use the gem-release gem
|
5
|
+
|
6
|
+
For more info see: https://github.com/svenfuchs/gem-release#usage
|
7
|
+
|
8
|
+
Steps for an update
|
9
|
+
-------------------
|
10
|
+
|
11
|
+
1. Update code and commit it.
|
12
|
+
2. Add entry to CHANGELOG and commit it:
|
13
|
+
* h1 for major release (1.0.0)
|
14
|
+
* h2 for minor release (0.1.0)
|
15
|
+
* h3 for patch release (0.0.1)
|
16
|
+
3. Update version in .gemspec and commit
|
17
|
+
4. Release it.
|
18
|
+
* `gem release`
|
19
|
+
5. Create a git tag and push to origin.
|
20
|
+
`gem tag`
|
21
|
+
|
22
|
+
|
23
|
+
http://prioritized.net/blog/gemify-assets-for-rails/
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Trouble when packaging assets
|
2
|
+
=============================
|
3
|
+
|
4
|
+
I had syntax and dot errors when packaging the JS assets. Turns out that the
|
5
|
+
JS libraries used reserved words as method accessors. Here is an example how I
|
6
|
+
fixed it (`in`) is the reserved word:
|
7
|
+
|
8
|
+
`c.in` => `c["in"]`
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Require this file for fast testing outside of rails. Don't use this for normal
|
2
|
+
# usage.
|
3
|
+
require 'action_view'
|
4
|
+
require 'active_support/all' # TODO: once the dust settles, only require the modules we need
|
5
|
+
require 'color'
|
6
|
+
require 'descriptive-statistics'
|
7
|
+
require 'distribution'
|
8
|
+
require 'interpolate'
|
9
|
+
|
10
|
+
# require 'rails-data-explorer/engine'
|
11
|
+
|
12
|
+
require 'rails_data_explorer'
|
13
|
+
require 'rails-data-explorer/chart'
|
14
|
+
require 'rails-data-explorer/data_series'
|
15
|
+
require 'rails-data-explorer/data_set'
|
16
|
+
require 'rails-data-explorer/data_type'
|
17
|
+
require 'rails-data-explorer/exploration'
|
18
|
+
require 'rails-data-explorer/statistics/rng_category'
|
19
|
+
require 'rails-data-explorer/statistics/rng_gaussian'
|
20
|
+
require 'rails-data-explorer/statistics/rng_power_law'
|
21
|
+
require 'rails-data-explorer/utils/color_scale'
|
22
|
+
require 'rails-data-explorer/utils/data_binner'
|
23
|
+
require 'rails-data-explorer/utils/data_quantizer'
|
24
|
+
require 'rails-data-explorer/utils/value_formatter'
|
25
|
+
|
26
|
+
require 'rails-data-explorer/chart/box_plot'
|
27
|
+
require 'rails-data-explorer/chart/box_plot_group'
|
28
|
+
require 'rails-data-explorer/chart/contingency_table'
|
29
|
+
require 'rails-data-explorer/chart/descriptive_statistics_table'
|
30
|
+
require 'rails-data-explorer/chart/histogram_categorical'
|
31
|
+
require 'rails-data-explorer/chart/histogram_quantitative'
|
32
|
+
require 'rails-data-explorer/chart/histogram_temporal'
|
33
|
+
require 'rails-data-explorer/chart/parallel_coordinates'
|
34
|
+
require 'rails-data-explorer/chart/parallel_set'
|
35
|
+
require 'rails-data-explorer/chart/pie_chart'
|
36
|
+
require 'rails-data-explorer/chart/scatterplot'
|
37
|
+
require 'rails-data-explorer/chart/stacked_bar_chart_categorical_percent'
|
38
|
+
require 'rails-data-explorer/data_type/categorical'
|
39
|
+
require 'rails-data-explorer/data_type/quantitative'
|
40
|
+
require 'rails-data-explorer/data_type/quantitative/decimal'
|
41
|
+
require 'rails-data-explorer/data_type/quantitative/integer'
|
42
|
+
require 'rails-data-explorer/data_type/quantitative/temporal'
|
data/lib/rails-data-explorer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'action_view'
|
2
|
+
require 'active_support/all' # TODO: once the dust settles, only require the modules we need
|
1
3
|
require 'color'
|
2
4
|
require 'descriptive-statistics'
|
3
5
|
require 'distribution'
|
@@ -5,6 +7,7 @@ require 'interpolate'
|
|
5
7
|
|
6
8
|
require 'rails-data-explorer/engine'
|
7
9
|
|
10
|
+
require 'rails_data_explorer'
|
8
11
|
require 'rails-data-explorer/chart'
|
9
12
|
require 'rails-data-explorer/data_series'
|
10
13
|
require 'rails-data-explorer/data_set'
|
@@ -14,6 +17,8 @@ require 'rails-data-explorer/statistics/rng_category'
|
|
14
17
|
require 'rails-data-explorer/statistics/rng_gaussian'
|
15
18
|
require 'rails-data-explorer/statistics/rng_power_law'
|
16
19
|
require 'rails-data-explorer/utils/color_scale'
|
20
|
+
require 'rails-data-explorer/utils/data_binner'
|
21
|
+
require 'rails-data-explorer/utils/data_quantizer'
|
17
22
|
require 'rails-data-explorer/utils/value_formatter'
|
18
23
|
|
19
24
|
require 'rails-data-explorer/chart/box_plot'
|
@@ -33,12 +38,3 @@ require 'rails-data-explorer/data_type/quantitative'
|
|
33
38
|
require 'rails-data-explorer/data_type/quantitative/decimal'
|
34
39
|
require 'rails-data-explorer/data_type/quantitative/integer'
|
35
40
|
require 'rails-data-explorer/data_type/quantitative/temporal'
|
36
|
-
|
37
|
-
class RailsDataExplorer
|
38
|
-
|
39
|
-
# Convenience method to instantiate new Exploration
|
40
|
-
def self.new(*args)
|
41
|
-
RailsDataExplorer::Exploration.new(*args)
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
@@ -11,12 +11,14 @@ class RailsDataExplorer
|
|
11
11
|
|
12
12
|
def compute_chart_attrs
|
13
13
|
x_ds = @data_set.data_series.first
|
14
|
+
return false if x_ds.nil?
|
15
|
+
|
14
16
|
{
|
15
17
|
values: [x_ds.values],
|
16
18
|
min: x_ds.min_val,
|
17
19
|
max: x_ds.max_val,
|
18
20
|
base_width: 120,
|
19
|
-
base_height:
|
21
|
+
base_height: 800,
|
20
22
|
axis_tick_format: x_ds.axis_tick_format,
|
21
23
|
}
|
22
24
|
end
|
@@ -24,6 +26,8 @@ class RailsDataExplorer
|
|
24
26
|
def render
|
25
27
|
return '' unless render?
|
26
28
|
ca = compute_chart_attrs
|
29
|
+
return '' unless ca
|
30
|
+
|
27
31
|
%(
|
28
32
|
<div id="#{ dom_id }" class="rde-chart rde-box-plot">
|
29
33
|
<svg class="box" style="height: #{ ca[:base_width] }px;"></svg>
|
@@ -9,6 +9,9 @@ class RailsDataExplorer
|
|
9
9
|
class Chart
|
10
10
|
class BoxPlotGroup < Chart
|
11
11
|
|
12
|
+
# TODO: imitate this:
|
13
|
+
# http://www.stata.com/support/faqs/graphics/gph/graphdocs/horizontal-box-plot-of-variable-by-values-of-categorical-variable/
|
14
|
+
|
12
15
|
def initialize(_data_set, options = {})
|
13
16
|
@data_set = _data_set
|
14
17
|
@options = {}.merge(options)
|
@@ -24,7 +27,6 @@ class RailsDataExplorer
|
|
24
27
|
|
25
28
|
x_ds = x_candidates.first
|
26
29
|
y_ds = (y_candidates - [x_ds]).first
|
27
|
-
|
28
30
|
return false if x_ds.nil? || y_ds.nil?
|
29
31
|
|
30
32
|
min = x_ds.min_val # get global min
|
@@ -36,16 +38,20 @@ class RailsDataExplorer
|
|
36
38
|
}
|
37
39
|
|
38
40
|
y_ds.values.each_with_index { |y_val, idx|
|
41
|
+
next if (y_val.nil? || Float::NAN == y_val)
|
39
42
|
values_hash[y_val] << x_ds.values[idx]
|
40
43
|
}
|
41
44
|
|
42
45
|
{
|
43
46
|
values: values_hash.values,
|
47
|
+
category_labels: values_hash.keys,
|
44
48
|
min: min,
|
45
49
|
max: max,
|
46
50
|
base_width: 120,
|
47
|
-
base_height:
|
51
|
+
base_height: 800,
|
48
52
|
axis_tick_format: x_ds.axis_tick_format,
|
53
|
+
num_box_plots: y_ds.uniq_vals_count,
|
54
|
+
axis_scale: DataSeries.new('_', [min, max]).axis_scale
|
49
55
|
}
|
50
56
|
end
|
51
57
|
|
@@ -53,10 +59,20 @@ class RailsDataExplorer
|
|
53
59
|
return '' unless render?
|
54
60
|
ca = compute_chart_attrs
|
55
61
|
return '' unless ca
|
62
|
+
|
63
|
+
svg_trs = ca[:category_labels].map { |cat_label|
|
64
|
+
%(
|
65
|
+
<tr>
|
66
|
+
<td style="vertical-align: middle;">#{ cat_label }</td>
|
67
|
+
<td style="vertical-align: middle; width: 100%">
|
68
|
+
<svg class="box" style="height: #{ ca[:base_width] }px;"></svg>
|
69
|
+
</td>
|
70
|
+
</tr>
|
71
|
+
)
|
72
|
+
}.join.html_safe
|
56
73
|
%(
|
57
|
-
<div id="#{ dom_id }" class="rde-chart rde-box-plot">
|
58
|
-
<
|
59
|
-
<svg class="box" style="height: #{ ca[:base_width] }px;"></svg>
|
74
|
+
<div id="#{ dom_id }" class="rde-chart rde-box-plot-group">
|
75
|
+
<table>#{ svg_trs }</table>
|
60
76
|
|
61
77
|
<script type="text/javascript">
|
62
78
|
(function() {
|
@@ -78,6 +94,7 @@ class RailsDataExplorer
|
|
78
94
|
var data = #{ ca[:values].to_json };
|
79
95
|
|
80
96
|
chart.domain([min, max]);
|
97
|
+
chart.scale(#{ ca[:axis_scale] });
|
81
98
|
|
82
99
|
var svg = d3.select("##{ dom_id }").selectAll("svg")
|
83
100
|
.data(data)
|
@@ -5,6 +5,8 @@
|
|
5
5
|
# * http://www.quora.com/What-is-the-most-intuitive-explanation-for-the-chi-square-test
|
6
6
|
# * http://people.revoledu.com/kardi/tutorial/Questionnaire/Chi-Square%20IndependentTest.html
|
7
7
|
# * http://stattrek.com/chi-square-test/independence.aspx?Tutorial=AP
|
8
|
+
|
9
|
+
# Contingency table and chi squared test is a good tool for interpreting A/B tests.
|
8
10
|
class RailsDataExplorer
|
9
11
|
class Chart
|
10
12
|
class ContingencyTable < Chart
|
@@ -24,16 +26,23 @@ class RailsDataExplorer
|
|
24
26
|
|
25
27
|
x_ds = x_candidates.first
|
26
28
|
y_ds = (y_candidates - [x_ds]).first
|
29
|
+
return false if x_ds.nil? || y_ds.nil?
|
27
30
|
|
28
31
|
# Compute @observed_vals, @expected_vals, etc.
|
29
32
|
compute_contingency_and_chi_squared!(x_ds, y_ds)
|
30
33
|
|
31
|
-
x_sorted_keys = x_ds.uniq_vals.sort
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
x_sorted_keys = x_ds.uniq_vals.sort(
|
35
|
+
&x_ds.label_sorter(
|
36
|
+
nil,
|
37
|
+
lambda { |a,b| @observed_vals[b][:_sum] <=> @observed_vals[a][:_sum] }
|
38
|
+
)
|
39
|
+
)
|
40
|
+
y_sorted_keys = y_ds.uniq_vals.sort(
|
41
|
+
&y_ds.label_sorter(
|
42
|
+
nil,
|
43
|
+
lambda { |a,b| @observed_vals[:_sum][b] <=> @observed_vals[:_sum][a] }
|
44
|
+
)
|
45
|
+
)
|
37
46
|
|
38
47
|
ca = case @data_set.dimensions_count
|
39
48
|
when 2
|
@@ -97,6 +106,8 @@ class RailsDataExplorer
|
|
97
106
|
def render
|
98
107
|
return '' unless render?
|
99
108
|
ca = compute_chart_attrs
|
109
|
+
return '' unless ca
|
110
|
+
|
100
111
|
content_tag(:div, :class => 'rde-chart rde-contingency-table', :id => dom_id) do
|
101
112
|
content_tag(:h3, "Contingency Table", :class => 'rde-chart-title') +
|
102
113
|
render_html_table(ca)
|
@@ -176,11 +187,35 @@ class RailsDataExplorer
|
|
176
187
|
# Set significance_level
|
177
188
|
@significance_level = 0.05
|
178
189
|
# Compute conclusion
|
179
|
-
|
180
|
-
|
181
|
-
|
190
|
+
all_observed_vals = []
|
191
|
+
x_ds.uniq_vals.each { |x_val|
|
192
|
+
y_ds.uniq_vals.each { |y_val|
|
193
|
+
all_observed_vals << @observed_vals[x_val][y_val]
|
194
|
+
}
|
195
|
+
}
|
196
|
+
observed_vals_less_than_five = all_observed_vals.find_all { |e| e < 5 }
|
197
|
+
ratio_of_observed_vals_below_five = observed_vals_less_than_five.length / all_observed_vals.length.to_f
|
198
|
+
|
199
|
+
if ratio_of_observed_vals_below_five > 0.2
|
200
|
+
@conclusion = [
|
201
|
+
"We did not run the ",
|
202
|
+
%(<a href="http://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test#Test_of_independence">Pearson chi squared test of independence</a> ),
|
203
|
+
"since #{ number_to_percentage(ratio_of_observed_vals_below_five * 100, :precision => 0) } ",
|
204
|
+
"of observed values in the contingency table are below 5 (cutoff is 20%)."
|
205
|
+
].join
|
206
|
+
elsif([x_ds, y_ds].any? { |e| e.uniq_vals.length < 2 })
|
207
|
+
@conclusion = [
|
208
|
+
"We did not run the ",
|
209
|
+
%(<a href="http://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test#Test_of_independence">Pearson chi squared test of independence</a> ),
|
210
|
+
"since there are not enough observed values in the contingency table."
|
211
|
+
].join
|
182
212
|
else
|
183
|
-
|
213
|
+
@conclusion = %(<a href="http://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test#Test_of_independence">Pearson chi squared test of independence</a> suggests that )
|
214
|
+
@conclusion << if @p_value <= @significance_level
|
215
|
+
%("#{ x_ds.name }" and "#{ y_ds.name }" are dependent variables (p_value: #{ number_with_precision(@p_value) }))
|
216
|
+
else
|
217
|
+
%("#{ x_ds.name }" and "#{ y_ds.name }" are independent variables (p_value: #{ number_with_precision(@p_value) }))
|
218
|
+
end
|
184
219
|
end
|
185
220
|
@conclusion = @conclusion.html_safe
|
186
221
|
end
|
@@ -9,12 +9,21 @@ class RailsDataExplorer
|
|
9
9
|
|
10
10
|
def compute_chart_attrs
|
11
11
|
x_ds = @data_set.data_series.first
|
12
|
+
return false if x_ds.nil?
|
13
|
+
|
12
14
|
# compute histogram
|
13
15
|
h = x_ds.values.inject(Hash.new(0)) { |m,e| m[e] += 1; m }
|
14
16
|
{
|
15
|
-
values: h.map { |k,v|
|
17
|
+
values: h.map { |k,v|
|
18
|
+
{ x: k, y: v }
|
19
|
+
}.sort(
|
20
|
+
&x_ds.label_sorter(
|
21
|
+
:x,
|
22
|
+
lambda { |a,b| b[:y] <=> a[:y] }
|
23
|
+
)
|
24
|
+
),
|
16
25
|
x_axis_label: x_ds.name,
|
17
|
-
x_axis_tick_format: "",
|
26
|
+
x_axis_tick_format: "d3.format('r')",
|
18
27
|
y_axis_label: 'Frequency',
|
19
28
|
y_axis_tick_format: "d3.format('r')",
|
20
29
|
}
|
@@ -23,8 +32,88 @@ class RailsDataExplorer
|
|
23
32
|
def render
|
24
33
|
return '' unless render?
|
25
34
|
ca = compute_chart_attrs
|
35
|
+
return '' unless ca
|
36
|
+
render_vega(ca)
|
37
|
+
end
|
38
|
+
|
39
|
+
def render_vega(ca)
|
26
40
|
%(
|
27
|
-
<div class="rde-chart rde-histogram">
|
41
|
+
<div class="rde-chart rde-histogram-categorical">
|
42
|
+
<h3 class="rde-chart-title">Histogram</h3>
|
43
|
+
<div id="#{ dom_id }"></div>
|
44
|
+
<script type="text/javascript">
|
45
|
+
(function() {
|
46
|
+
var spec = {
|
47
|
+
"width": 800,
|
48
|
+
"height": 200,
|
49
|
+
"padding": {"top": 10, "left": 70, "bottom": 50, "right": 10},
|
50
|
+
"data": [
|
51
|
+
{
|
52
|
+
"name": "table",
|
53
|
+
"values": #{ ca[:values].to_json }
|
54
|
+
}
|
55
|
+
],
|
56
|
+
"scales": [
|
57
|
+
{
|
58
|
+
"name": "x",
|
59
|
+
"type": "ordinal",
|
60
|
+
"range": "width",
|
61
|
+
"domain": {"data": "table", "field": "data.x"}
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"name": "y",
|
65
|
+
"range": "height",
|
66
|
+
"nice": true,
|
67
|
+
"domain": {"data": "table", "field": "data.y"}
|
68
|
+
}
|
69
|
+
],
|
70
|
+
"axes": [
|
71
|
+
{
|
72
|
+
"type": "x",
|
73
|
+
"scale": "x",
|
74
|
+
"title": "#{ ca[:x_axis_label] }",
|
75
|
+
"format": #{ ca[:x_axis_tick_format] },
|
76
|
+
},
|
77
|
+
{
|
78
|
+
"type": "y",
|
79
|
+
"scale": "y",
|
80
|
+
"title": "#{ ca[:y_axis_label] }",
|
81
|
+
"titleOffset": 60,
|
82
|
+
"format": #{ ca[:y_axis_tick_format] },
|
83
|
+
}
|
84
|
+
],
|
85
|
+
"marks": [
|
86
|
+
{
|
87
|
+
"type": "rect",
|
88
|
+
"from": {"data": "table"},
|
89
|
+
"properties": {
|
90
|
+
"enter": {
|
91
|
+
"x": {"scale": "x", "field": "data.x"},
|
92
|
+
"width": {"scale": "x", "band": true, "offset": -1},
|
93
|
+
"y": {"scale": "y", "field": "data.y"},
|
94
|
+
"y2": {"scale": "y", "value": 0}
|
95
|
+
},
|
96
|
+
"update": {
|
97
|
+
"fill": {"value": "#1F77B4"}
|
98
|
+
},
|
99
|
+
}
|
100
|
+
}
|
101
|
+
]
|
102
|
+
};
|
103
|
+
|
104
|
+
vg.parse.spec(spec, function(chart) {
|
105
|
+
var view = chart({ el:"##{ dom_id }" }).update();
|
106
|
+
});
|
107
|
+
|
108
|
+
})();
|
109
|
+
</script>
|
110
|
+
</div>
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
def render_nvd3(ca)
|
115
|
+
%(
|
116
|
+
<div class="rde-chart rde-histogram-categorical">
|
28
117
|
<h3 class="rde-chart-title">Histogram</h3>
|
29
118
|
<div id="#{ dom_id }", style="height: 200px;">
|
30
119
|
<svg></svg>
|
@@ -52,6 +141,12 @@ class RailsDataExplorer
|
|
52
141
|
.tickFormat(#{ ca[:y_axis_tick_format] })
|
53
142
|
;
|
54
143
|
|
144
|
+
chart.tooltipContent(
|
145
|
+
function(key, x, y, e, graph) {
|
146
|
+
return '<p>' + x + '</p>' + '<p>' + y + '</p>'
|
147
|
+
}
|
148
|
+
);
|
149
|
+
|
55
150
|
d3.select('##{ dom_id } svg')
|
56
151
|
.datum(data)
|
57
152
|
.transition().duration(100)
|
@@ -68,6 +163,12 @@ class RailsDataExplorer
|
|
68
163
|
)
|
69
164
|
end
|
70
165
|
|
166
|
+
# Render HistogramCategorical only if there is a fairly small number of
|
167
|
+
# distinct values.
|
168
|
+
def render?
|
169
|
+
!@data_set.data_series.first.has_many_uniq_vals?
|
170
|
+
end
|
171
|
+
|
71
172
|
end
|
72
173
|
end
|
73
174
|
end
|