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.
Files changed (52) hide show
  1. data/CHANGELOG.md +5 -1
  2. data/README.md +11 -0
  3. data/Rakefile +62 -0
  4. data/doc/how_to/release.md +23 -0
  5. data/doc/how_to/trouble_when_packaging_assets.md +8 -0
  6. data/lib/rails-data-explorer-no-rails.rb +42 -0
  7. data/lib/rails-data-explorer.rb +5 -9
  8. data/lib/rails-data-explorer/chart/box_plot.rb +5 -1
  9. data/lib/rails-data-explorer/chart/box_plot_group.rb +22 -5
  10. data/lib/rails-data-explorer/chart/contingency_table.rb +45 -10
  11. data/lib/rails-data-explorer/chart/histogram_categorical.rb +104 -3
  12. data/lib/rails-data-explorer/chart/histogram_quantitative.rb +99 -2
  13. data/lib/rails-data-explorer/chart/histogram_temporal.rb +10 -55
  14. data/lib/rails-data-explorer/chart/parallel_coordinates.rb +4 -0
  15. data/lib/rails-data-explorer/chart/parallel_set.rb +4 -0
  16. data/lib/rails-data-explorer/chart/pie_chart.rb +89 -8
  17. data/lib/rails-data-explorer/chart/scatterplot.rb +110 -8
  18. data/lib/rails-data-explorer/chart/stacked_bar_chart_categorical_percent.rb +133 -14
  19. data/lib/rails-data-explorer/data_series.rb +37 -2
  20. data/lib/rails-data-explorer/data_type/categorical.rb +72 -2
  21. data/lib/rails-data-explorer/data_type/quantitative.rb +41 -12
  22. data/lib/rails-data-explorer/data_type/quantitative/temporal.rb +3 -2
  23. data/lib/rails-data-explorer/exploration.rb +5 -1
  24. data/lib/rails-data-explorer/utils/data_binner.rb +31 -0
  25. data/lib/rails-data-explorer/utils/data_quantizer.rb +66 -0
  26. data/lib/rails_data_explorer.rb +133 -0
  27. data/rails-data-explorer.gemspec +4 -4
  28. data/spec/helper.rb +7 -0
  29. data/spec/helper_no_rails.rb +10 -0
  30. data/spec/rails-data-explorer/data_series_spec.rb +45 -0
  31. data/spec/rails-data-explorer/data_type/categorical_spec.rb +34 -0
  32. data/spec/rails-data-explorer/exploration_spec.rb +55 -0
  33. data/spec/rails-data-explorer/utils/data_binner_spec.rb +29 -0
  34. data/spec/rails-data-explorer/utils/data_quantizer_spec.rb +71 -0
  35. data/vendor/assets/javascripts/packaged/rails-data-explorer.min.js +1 -0
  36. data/vendor/assets/javascripts/rails-data-explorer.js +6 -5
  37. data/vendor/assets/javascripts/{d3.boxplot.js → sources/d3.boxplot.js} +10 -3
  38. data/vendor/assets/javascripts/{d3.parcoords.js → sources/d3.parcoords.js} +1 -1
  39. data/vendor/assets/javascripts/{d3.parsets.js → sources/d3.parsets.js} +3 -3
  40. data/vendor/assets/javascripts/{d3.v3.js → sources/d3.v3.js} +0 -0
  41. data/vendor/assets/javascripts/{nv.d3.js → sources/nv.d3.js} +0 -0
  42. data/vendor/assets/javascripts/sources/vega.js +7040 -0
  43. data/vendor/assets/stylesheets/packaged/rails-data-explorer.min.css +9 -0
  44. data/vendor/assets/stylesheets/rails-data-explorer.css +7 -7
  45. data/vendor/assets/stylesheets/{bootstrap-theme.css → sources/bootstrap-theme.css} +0 -0
  46. data/vendor/assets/stylesheets/{bootstrap.css → sources/bootstrap.css} +0 -0
  47. data/vendor/assets/stylesheets/{d3.boxplot.css → sources/d3.boxplot.css} +0 -0
  48. data/vendor/assets/stylesheets/{d3.parcoords.css → sources/d3.parcoords.css} +0 -0
  49. data/vendor/assets/stylesheets/{d3.parsets.css → sources/d3.parsets.css} +0 -0
  50. data/vendor/assets/stylesheets/{nv.d3.css → sources/nv.d3.css} +0 -0
  51. data/vendor/assets/stylesheets/{rde-default-style.css → sources/rde-default-style.css} +0 -0
  52. metadata +65 -28
@@ -1,3 +1,7 @@
1
- ## 0.0.1
1
+ ## 0.1.0
2
+
3
+ * Is now being used in a production app, however no documentation yet.
4
+
5
+ ### 0.0.1
2
6
 
3
7
  * Initial release
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'
@@ -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: 1334,
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: 1334,
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
- <svg class="box" style="height: #{ ca[:base_width] }px;"></svg>
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 { |a,b|
32
- @observed_vals[b][:_sum] <=> @observed_vals[a][:_sum]
33
- }
34
- y_sorted_keys = y_ds.uniq_vals.sort { |a,b|
35
- @observed_vals[:_sum][b] <=> @observed_vals[:_sum][a]
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
- @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 )
180
- @conclusion << if @p_value <= @significance_level
181
- "#{ x_ds.name } and #{ y_ds.name } are dependent variables (p_value: #{ number_with_precision(@p_value) })"
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
- "#{ x_ds.name } and #{ y_ds.name } are independent variables (p_value: #{ number_with_precision(@p_value) })"
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| { x: k, y: v } }.sort { |a,b| b[:y] <=> a[:y] },
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