rails-data-explorer 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
@@ -17,6 +17,7 @@ class RailsDataExplorer
|
|
17
17
|
|
18
18
|
x_ds = x_candidates.first
|
19
19
|
y_ds = (y_candidates - [x_ds]).first
|
20
|
+
return false if x_ds.nil? || y_ds.nil?
|
20
21
|
|
21
22
|
# initialize data_matrix
|
22
23
|
data_matrix = { :_sum => { :_sum => 0 } }
|
@@ -38,25 +39,30 @@ class RailsDataExplorer
|
|
38
39
|
data_matrix[:_sum][:_sum] += 1
|
39
40
|
}
|
40
41
|
|
41
|
-
x_sorted_keys = x_ds.uniq_vals.sort
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
x_sorted_keys = x_ds.uniq_vals.sort(
|
43
|
+
&x_ds.label_sorter(
|
44
|
+
nil,
|
45
|
+
lambda { |a,b| data_matrix[b][:_sum] <=> data_matrix[a][:_sum] }
|
46
|
+
)
|
47
|
+
)
|
48
|
+
y_sorted_keys = y_ds.uniq_vals.sort(
|
49
|
+
&y_ds.label_sorter(
|
50
|
+
nil,
|
51
|
+
lambda { |a,b| data_matrix[:_sum][b] <=> data_matrix[:_sum][a] }
|
52
|
+
)
|
53
|
+
)
|
47
54
|
|
48
55
|
values = case @data_set.dimensions_count
|
49
56
|
when 2
|
50
57
|
y_sorted_keys.map { |y_val|
|
51
|
-
{
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
y: (data_matrix[x_val][y_val] / data_matrix[x_val][:_sum].to_f) }
|
58
|
+
x_sorted_keys.map { |x_val|
|
59
|
+
{
|
60
|
+
x: x_val,
|
61
|
+
y: (data_matrix[x_val][y_val] / data_matrix[x_val][:_sum].to_f) * 100,
|
62
|
+
c: y_val
|
57
63
|
}
|
58
64
|
}
|
59
|
-
}
|
65
|
+
}.flatten
|
60
66
|
else
|
61
67
|
raise(ArgumentError.new("Exactly two data series required for contingency table."))
|
62
68
|
end
|
@@ -72,8 +78,115 @@ class RailsDataExplorer
|
|
72
78
|
def render
|
73
79
|
return '' unless render?
|
74
80
|
ca = compute_chart_attrs
|
81
|
+
return '' unless ca
|
82
|
+
render_vega(ca)
|
83
|
+
end
|
84
|
+
|
85
|
+
def render_vega(ca)
|
75
86
|
%(
|
76
|
-
<div class="rde-chart rde-bar-chart">
|
87
|
+
<div class="rde-chart rde-stacked-bar-chart-categorical-percent">
|
88
|
+
<h3 class="rde-chart-title">Stacked Bar Chart</h3>
|
89
|
+
<div id="#{ dom_id }"></div>
|
90
|
+
<script type="text/javascript">
|
91
|
+
(function() {
|
92
|
+
var spec = {
|
93
|
+
"width": 800,
|
94
|
+
"height": 200,
|
95
|
+
"padding": {"top": 10, "left": 50, "bottom": 50, "right": 100},
|
96
|
+
"data": [
|
97
|
+
{
|
98
|
+
"name": "table",
|
99
|
+
"values": #{ ca[:values].to_json }
|
100
|
+
},
|
101
|
+
{
|
102
|
+
"name": "stats",
|
103
|
+
"source": "table",
|
104
|
+
"transform": [
|
105
|
+
{"type": "facet", "keys": ["data.x"]},
|
106
|
+
{"type": "stats", "value": "data.y"}
|
107
|
+
]
|
108
|
+
}
|
109
|
+
],
|
110
|
+
"scales": [
|
111
|
+
{
|
112
|
+
"name": "x",
|
113
|
+
"type": "ordinal",
|
114
|
+
"range": "width",
|
115
|
+
"domain": {"data": "table", "field": "data.x"}
|
116
|
+
},
|
117
|
+
{
|
118
|
+
"name": "y",
|
119
|
+
"type": "linear",
|
120
|
+
"range": "height",
|
121
|
+
"nice": true,
|
122
|
+
"domain": {"data": "stats", "field": "sum"}
|
123
|
+
},
|
124
|
+
{
|
125
|
+
"name": "color",
|
126
|
+
"type": "ordinal",
|
127
|
+
"range": "category10"
|
128
|
+
}
|
129
|
+
],
|
130
|
+
"axes": [
|
131
|
+
{
|
132
|
+
"type": "x",
|
133
|
+
"scale": "x",
|
134
|
+
"title": "#{ ca[:x_axis_label] }",
|
135
|
+
"format": #{ ca[:x_axis_tick_format] },
|
136
|
+
},
|
137
|
+
{
|
138
|
+
"type": "y",
|
139
|
+
"scale": "y",
|
140
|
+
"title": "#{ ca[:y_axis_label] }",
|
141
|
+
"format": #{ ca[:y_axis_tick_format] },
|
142
|
+
}
|
143
|
+
],
|
144
|
+
"marks": [
|
145
|
+
{
|
146
|
+
"type": "group",
|
147
|
+
"from": {
|
148
|
+
"data": "table",
|
149
|
+
"transform": [
|
150
|
+
{"type": "facet", "keys": ["data.c"]},
|
151
|
+
{"type": "stack", "point": "data.x", "height": "data.y"}
|
152
|
+
]
|
153
|
+
},
|
154
|
+
"marks": [
|
155
|
+
{
|
156
|
+
"type": "rect",
|
157
|
+
"properties": {
|
158
|
+
"enter": {
|
159
|
+
"x": {"scale": "x", "field": "data.x"},
|
160
|
+
"width": {"scale": "x", "band": true, "offset": -1},
|
161
|
+
"y": {"scale": "y", "field": "y"},
|
162
|
+
"y2": {"scale": "y", "field": "y2"},
|
163
|
+
"fill": {"scale": "color", "field": "data.c"}
|
164
|
+
},
|
165
|
+
}
|
166
|
+
}
|
167
|
+
]
|
168
|
+
}
|
169
|
+
],
|
170
|
+
"legends": [
|
171
|
+
{
|
172
|
+
"fill": "color",
|
173
|
+
}
|
174
|
+
],
|
175
|
+
};
|
176
|
+
|
177
|
+
vg.parse.spec(spec, function(chart) {
|
178
|
+
var view = chart({ el:"##{ dom_id }" }).update();
|
179
|
+
});
|
180
|
+
|
181
|
+
})();
|
182
|
+
</script>
|
183
|
+
</div>
|
184
|
+
)
|
185
|
+
end
|
186
|
+
|
187
|
+
def render_nvd3(ca)
|
188
|
+
%(
|
189
|
+
<div class="rde-chart rde-stacked-bar-chart-categorical-percent">
|
77
190
|
<h3 class="rde-chart-title">Stacked Bar Chart</h3>
|
78
191
|
<div id="#{ dom_id }", style="height: 200px;">
|
79
192
|
<svg></svg>
|
@@ -98,6 +211,12 @@ class RailsDataExplorer
|
|
98
211
|
|
99
212
|
chart.multibar.stacked(true);
|
100
213
|
chart.showControls(false);
|
214
|
+
chart.tooltipContent(
|
215
|
+
function(key, x, y, e, graph) {
|
216
|
+
return '<p>' + key + '</p>' + '<p>' + y + ' of ' + x + '</p>'
|
217
|
+
}
|
218
|
+
);
|
219
|
+
|
101
220
|
|
102
221
|
d3.select('##{ dom_id } svg')
|
103
222
|
.datum(data)
|
@@ -8,6 +8,19 @@ class RailsDataExplorer
|
|
8
8
|
delegate :available_chart_types, :to => :data_type, :prefix => false
|
9
9
|
delegate :available_chart_roles, :to => :data_type, :prefix => false
|
10
10
|
|
11
|
+
# Any data series with a dynamic range greater than this is considered
|
12
|
+
# having a large dynamic range
|
13
|
+
# We consider dynamic range the ratio between the largest and the smallest value.
|
14
|
+
def self.large_dynamic_range_cutoff
|
15
|
+
1000.0
|
16
|
+
end
|
17
|
+
|
18
|
+
# Any data series with more than this uniq vals is considered having many
|
19
|
+
# uniq values.
|
20
|
+
def self.many_uniq_vals_cutoff
|
21
|
+
20
|
22
|
+
end
|
23
|
+
|
11
24
|
# options: :chart_roles, :data_type (all optional)
|
12
25
|
def initialize(_name, _values, options={})
|
13
26
|
options = { chart_roles: [], data_type: nil }.merge(options)
|
@@ -53,6 +66,10 @@ class RailsDataExplorer
|
|
53
66
|
data_type.axis_tick_format(values)
|
54
67
|
end
|
55
68
|
|
69
|
+
def axis_scale
|
70
|
+
data_type.axis_scale(self)
|
71
|
+
end
|
72
|
+
|
56
73
|
def uniq_vals
|
57
74
|
@uniq_vals = values.uniq
|
58
75
|
end
|
@@ -69,6 +86,23 @@ class RailsDataExplorer
|
|
69
86
|
@max_val = values.compact.max
|
70
87
|
end
|
71
88
|
|
89
|
+
# Used to decide whether we can render certain chart types
|
90
|
+
def has_many_uniq_vals?
|
91
|
+
uniq_vals_count > self.class.many_uniq_vals_cutoff
|
92
|
+
end
|
93
|
+
|
94
|
+
def dynamic_range
|
95
|
+
max_val / [min_val, max_val].min.to_f
|
96
|
+
end
|
97
|
+
|
98
|
+
def has_large_dynamic_range?
|
99
|
+
dynamic_range > self.class.large_dynamic_range_cutoff
|
100
|
+
end
|
101
|
+
|
102
|
+
def label_sorter(label_val_key, value_sorter)
|
103
|
+
data_type.label_sorter(label_val_key, self, value_sorter)
|
104
|
+
end
|
105
|
+
|
72
106
|
private
|
73
107
|
|
74
108
|
# @param[Array<Symbol>] chart_role_overrides, :x, :y, :color
|
@@ -94,14 +128,15 @@ class RailsDataExplorer
|
|
94
128
|
|
95
129
|
def init_data_type(data_type_override)
|
96
130
|
if data_type_override.nil?
|
97
|
-
|
131
|
+
first_value = values.detect { |e| !e.nil? }
|
132
|
+
case first_value
|
98
133
|
when Integer, Bignum, Fixnum
|
99
134
|
DataType::Quantitative::Integer.new
|
100
135
|
when Float
|
101
136
|
DataType::Quantitative::Decimal.new
|
102
137
|
when String
|
103
138
|
DataType::Categorical.new
|
104
|
-
when DateTime, ActiveSupport::TimeWithZone
|
139
|
+
when Time, DateTime, ActiveSupport::TimeWithZone
|
105
140
|
DataType::Quantitative::Temporal.new
|
106
141
|
else
|
107
142
|
raise(ArgumentError.new("Can't infer data type for value: #{ values.first.class.inspect }"))
|
@@ -70,14 +70,22 @@ class RailsDataExplorer
|
|
70
70
|
m << { :label => "#{ k.to_s }_percent", :value => (v / total_count.to_f) * 100, :ruby_formatter => ruby_formatters[:percent] }
|
71
71
|
m
|
72
72
|
}.sort { |a,b| b[:value] <=> a[:value] }
|
73
|
-
r.insert(0, { :label => '
|
74
|
-
r.insert(0, { :label => '
|
73
|
+
r.insert(0, { :label => '[Total]_count', :value => total_count, :ruby_formatter => ruby_formatters[:integer] })
|
74
|
+
r.insert(0, { :label => '[Total]_percent', :value => 100, :ruby_formatter => ruby_formatters[:percent] })
|
75
75
|
r
|
76
76
|
end
|
77
77
|
|
78
78
|
# Returns an OpenStruct that describes a statistics table.
|
79
79
|
def descriptive_statistics_table(values)
|
80
80
|
desc_stats = descriptive_statistics(values)
|
81
|
+
if desc_stats.length < DataSeries.many_uniq_vals_cutoff
|
82
|
+
descriptive_statistics_table_horizontal(desc_stats)
|
83
|
+
else
|
84
|
+
descriptive_statistics_table_vertical(desc_stats)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def descriptive_statistics_table_horizontal(desc_stats)
|
81
89
|
labels = desc_stats.map { |e| e[:label].gsub(/_count|_percent/, '') }.uniq
|
82
90
|
table = OpenStruct.new(
|
83
91
|
:rows => []
|
@@ -108,10 +116,72 @@ class RailsDataExplorer
|
|
108
116
|
table
|
109
117
|
end
|
110
118
|
|
119
|
+
def descriptive_statistics_table_vertical(desc_stats)
|
120
|
+
labels = desc_stats.map { |e| e[:label].gsub(/_count|_percent/, '') }.uniq
|
121
|
+
table = OpenStruct.new(
|
122
|
+
:rows => []
|
123
|
+
)
|
124
|
+
table.rows << OpenStruct.new(
|
125
|
+
:css_class => 'rde-column_header',
|
126
|
+
:tag => :tr,
|
127
|
+
:cells => %w[Value Count Percent].map { |label|
|
128
|
+
OpenStruct.new(:value => label, :tag => :th, :css_class => 'rde-cell-label')
|
129
|
+
}
|
130
|
+
)
|
131
|
+
labels.each { |label|
|
132
|
+
count_stat = desc_stats.detect { |e| "#{ label }_count" == e[:label] }
|
133
|
+
percent_stat = desc_stats.detect { |e| "#{ label }_percent" == e[:label] }
|
134
|
+
table.rows << OpenStruct.new(
|
135
|
+
:css_class => 'rde-data_row',
|
136
|
+
:tag => :tr,
|
137
|
+
:cells => [
|
138
|
+
OpenStruct.new(:value => label, :tag => :td, :css_class => 'rde-cell-value'),
|
139
|
+
OpenStruct.new(
|
140
|
+
:value => count_stat[:value],
|
141
|
+
:ruby_formatter => count_stat[:ruby_formatter],
|
142
|
+
:tag => :td,
|
143
|
+
:css_class => 'rde-cell-value'
|
144
|
+
),
|
145
|
+
OpenStruct.new(
|
146
|
+
:value => percent_stat[:value],
|
147
|
+
:ruby_formatter => percent_stat[:ruby_formatter],
|
148
|
+
:tag => :td,
|
149
|
+
:css_class => 'rde-cell-value'
|
150
|
+
),
|
151
|
+
]
|
152
|
+
)
|
153
|
+
}
|
154
|
+
table
|
155
|
+
end
|
156
|
+
|
111
157
|
def axis_tick_format(values)
|
112
158
|
%(function(d) { return d })
|
113
159
|
end
|
114
160
|
|
161
|
+
# @param[Symbol, nil] label_val_key the hash key to use to get the label value during sort (sent to a,b)
|
162
|
+
# @param[DataSeries] data_series the ds that contains the uniq vals
|
163
|
+
# @param[Proc] value_sorter the sorting proc to use if not sorted numerically
|
164
|
+
# @return[Proc] a Proc that will be used by #sort
|
165
|
+
def label_sorter(label_val_key, data_series, value_sorter)
|
166
|
+
if data_series.uniq_vals.any? { |e| e.to_s =~ /^\d+/ }
|
167
|
+
# Sort numerical categories by key ASC
|
168
|
+
lambda { |a,b|
|
169
|
+
number_extractor = lambda { |val|
|
170
|
+
str = label_val_key ? val[label_val_key] : val
|
171
|
+
number = str.gsub(/^[^\d]*/, '').to_f
|
172
|
+
number += 1 if str =~ /^>/ # increase highest threshold by one for proper sorting
|
173
|
+
number
|
174
|
+
}
|
175
|
+
a_number = number_extractor.call(a)
|
176
|
+
b_number = number_extractor.call(b)
|
177
|
+
a_number <=> b_number
|
178
|
+
}
|
179
|
+
else
|
180
|
+
# Use provided value sorter
|
181
|
+
value_sorter
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
115
185
|
end
|
116
186
|
end
|
117
187
|
end
|
@@ -6,12 +6,12 @@ class RailsDataExplorer
|
|
6
6
|
|
7
7
|
def all_available_chart_types
|
8
8
|
[
|
9
|
-
{
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
},
|
9
|
+
# {
|
10
|
+
# chart_class: Chart::BoxPlot,
|
11
|
+
# chart_roles: [:y],
|
12
|
+
# dimensions_count_min: 1,
|
13
|
+
# dimensions_count_max: 1
|
14
|
+
# },
|
15
15
|
{
|
16
16
|
chart_class: Chart::HistogramQuantitative,
|
17
17
|
chart_roles: [:x],
|
@@ -44,34 +44,54 @@ class RailsDataExplorer
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def descriptive_statistics(values)
|
47
|
-
|
47
|
+
non_nil_values = values.find_all { |e| !(e.nil? || Float::NAN == e) }
|
48
|
+
stats = ::DescriptiveStatistics::Stats.new(non_nil_values)
|
48
49
|
ruby_formatters = {
|
49
|
-
:integer => Proc.new { |v|
|
50
|
-
|
51
|
-
|
50
|
+
:integer => Proc.new { |v|
|
51
|
+
v.nil? ? 'Null' : number_with_delimiter(v.round)
|
52
|
+
},
|
53
|
+
:decimal => Proc.new { |v|
|
54
|
+
case
|
55
|
+
when v.nil?
|
56
|
+
'Null'
|
57
|
+
when v.is_a?(Float) && v.nan?
|
58
|
+
'NaN'
|
59
|
+
else
|
60
|
+
number_with_precision(
|
61
|
+
v,
|
62
|
+
:precision => 3,
|
63
|
+
:significant => true,
|
64
|
+
:strip_insignificant_zeros => true,
|
65
|
+
:delimiter => ','
|
66
|
+
)
|
67
|
+
end
|
68
|
+
},
|
69
|
+
:pass_through => Proc.new { |v| (v.nil? || Float::NAN == v) ? 'NaN' : v },
|
52
70
|
}
|
53
71
|
[
|
54
72
|
{ :label => 'Min', :value => stats.min, :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
55
73
|
{ :label => '1%ile', :value => stats.value_from_percentile(1), :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
74
|
+
{ :label => '5%ile', :value => stats.value_from_percentile(5), :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
56
75
|
{ :label => '10%ile', :value => stats.value_from_percentile(10), :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
57
76
|
{ :label => '25%ile', :value => stats.value_from_percentile(25), :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
58
77
|
{ :label => 'Median', :value => stats.median, :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
59
78
|
{ :label => '75%ile', :value => stats.value_from_percentile(75), :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
60
79
|
{ :label => '90%ile', :value => stats.value_from_percentile(90), :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
80
|
+
{ :label => '95%ile', :value => stats.value_from_percentile(95), :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
61
81
|
{ :label => '99%ile', :value => stats.value_from_percentile(99), :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
62
82
|
{ :label => 'Max', :value => stats.max, :ruby_formatter => ruby_formatters[:decimal], :table_row => 1 },
|
63
|
-
{ :label => '', :value => '', :ruby_formatter => ruby_formatters[:pass_through], :table_row => 1 },
|
64
83
|
|
65
84
|
{ :label => 'Range', :value => stats.range, :ruby_formatter => ruby_formatters[:decimal], :table_row => 2 },
|
66
85
|
{ :label => 'Mean', :value => stats.mean, :ruby_formatter => ruby_formatters[:decimal], :table_row => 2 },
|
67
86
|
{ :label => 'Mode', :value => stats.mode, :ruby_formatter => ruby_formatters[:decimal], :table_row => 2 },
|
68
87
|
{ :label => 'Count', :value => values.length, :ruby_formatter => ruby_formatters[:integer], :table_row => 2 },
|
69
|
-
{ :label => 'Sum', :value =>
|
88
|
+
{ :label => 'Sum', :value => non_nil_values.inject(0) { |m,e| m += e }, :ruby_formatter => ruby_formatters[:decimal], :table_row => 2 },
|
70
89
|
{ :label => 'Variance', :value => stats.variance, :ruby_formatter => ruby_formatters[:decimal], :table_row => 2 },
|
71
90
|
{ :label => 'Std. dev.', :value => stats.standard_deviation, :ruby_formatter => ruby_formatters[:decimal], :table_row => 2 },
|
72
91
|
{ :label => 'Rel. std. dev.', :value => stats.relative_standard_deviation, :ruby_formatter => ruby_formatters[:decimal], :table_row => 2 },
|
73
92
|
{ :label => 'Skewness', :value => stats.skewness, :ruby_formatter => ruby_formatters[:decimal], :table_row => 2 },
|
74
93
|
{ :label => 'Kurtosis', :value => stats.kurtosis, :ruby_formatter => ruby_formatters[:decimal], :table_row => 2 },
|
94
|
+
{ :label => '', :value => '', :ruby_formatter => ruby_formatters[:pass_through], :table_row => 2 },
|
75
95
|
]
|
76
96
|
end
|
77
97
|
|
@@ -104,6 +124,15 @@ class RailsDataExplorer
|
|
104
124
|
raise "Implement me in sub_class"
|
105
125
|
end
|
106
126
|
|
127
|
+
def axis_scale(data_series)
|
128
|
+
# Log scales can't handle 0 values
|
129
|
+
if data_series.min_val > 0.0 && data_series.has_large_dynamic_range?
|
130
|
+
'd3.scale.log'
|
131
|
+
else
|
132
|
+
'd3.scale.linear'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
107
136
|
end
|
108
137
|
end
|
109
138
|
end
|
@@ -26,10 +26,11 @@ class RailsDataExplorer
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def descriptive_statistics(values)
|
29
|
+
non_nil_values = values.find_all { |e| !e.nil? }
|
29
30
|
ruby_formatter = Proc.new { |v| v.nil? ? '' : v.strftime('%a, %b %e, %Y, %l:%M:%S %p %Z') }
|
30
31
|
[
|
31
|
-
{ :label => 'Min', :value =>
|
32
|
-
{ :label => 'Max', :value =>
|
32
|
+
{ :label => 'Min', :value => non_nil_values.min, :ruby_formatter => ruby_formatter },
|
33
|
+
{ :label => 'Max', :value => non_nil_values.max, :ruby_formatter => ruby_formatter },
|
33
34
|
{ :label => 'Count', :value => values.length, :ruby_formatter => Proc.new { |e| number_with_delimiter(e) } },
|
34
35
|
]
|
35
36
|
end
|