rails-data-explorer 0.1.0 → 0.2.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -0
- data/README.md +18 -0
- data/doc/how_to/release.md +2 -5
- data/lib/rails-data-explorer-no-rails.rb +1 -0
- data/lib/rails-data-explorer.rb +5 -0
- data/lib/rails-data-explorer/chart.rb +11 -11
- data/lib/rails-data-explorer/chart/box_plot_group.rb +10 -3
- data/lib/rails-data-explorer/chart/contingency_table.rb +58 -45
- data/lib/rails-data-explorer/chart/descriptive_statistics_table.rb +2 -2
- data/lib/rails-data-explorer/chart/histogram_categorical.rb +10 -4
- data/lib/rails-data-explorer/chart/histogram_quantitative.rb +12 -5
- data/lib/rails-data-explorer/chart/histogram_temporal.rb +6 -0
- data/lib/rails-data-explorer/chart/parallel_coordinates.rb +4 -4
- data/lib/rails-data-explorer/chart/parallel_set.rb +3 -3
- data/lib/rails-data-explorer/chart/stacked_bar_chart_categorical_percent.rb +3 -3
- data/lib/rails-data-explorer/constants.rb +5 -0
- data/lib/rails-data-explorer/data_series.rb +20 -14
- data/lib/rails-data-explorer/data_set.rb +4 -0
- data/lib/rails-data-explorer/data_type/categorical.rb +84 -72
- data/lib/rails-data-explorer/data_type/quantitative.rb +46 -48
- data/lib/rails-data-explorer/data_type/quantitative/temporal.rb +23 -17
- data/lib/rails-data-explorer/exploration.rb +20 -4
- data/lib/rails-data-explorer/statistics/rng_category.rb +1 -1
- data/lib/rails-data-explorer/utils/data_binner.rb +20 -10
- data/lib/rails-data-explorer/utils/data_quantizer.rb +6 -6
- data/lib/rails-data-explorer/utils/rde_table.rb +62 -0
- data/lib/rails_data_explorer.rb +35 -20
- data/rails-data-explorer.gemspec +1 -1
- data/spec/rails-data-explorer/exploration_spec.rb +4 -4
- data/spec/rails-data-explorer/utils/data_binner_spec.rb +3 -3
- metadata +30 -50
@@ -14,6 +14,9 @@ class RailsDataExplorer
|
|
14
14
|
m[key] += 1
|
15
15
|
m
|
16
16
|
}
|
17
|
+
histogram_values_ds = DataSeries.new('_', h.values)
|
18
|
+
y_scale_type = histogram_values_ds.axis_scale(:vega)
|
19
|
+
bar_y2_val = 'log' == y_scale_type ? histogram_values_ds.min_val / 10.0 : 0
|
17
20
|
width = 800
|
18
21
|
{
|
19
22
|
values: h.map { |k,v| { x: k, y: v } },
|
@@ -25,6 +28,9 @@ class RailsDataExplorer
|
|
25
28
|
bar_width: 2,
|
26
29
|
y_axis_label: 'Frequency',
|
27
30
|
y_axis_tick_format: "d3.format('r')",
|
31
|
+
y_scale_type: y_scale_type,
|
32
|
+
y_scale_domain: [bar_y2_val, histogram_values_ds.max_val],
|
33
|
+
bar_y2_val: bar_y2_val,
|
28
34
|
}
|
29
35
|
end
|
30
36
|
|
@@ -81,10 +81,10 @@ class RailsDataExplorer
|
|
81
81
|
m
|
82
82
|
}
|
83
83
|
{
|
84
|
-
:
|
85
|
-
:
|
86
|
-
:
|
87
|
-
:
|
84
|
+
dimensions: dimension_names,
|
85
|
+
values: dimension_values,
|
86
|
+
types: dimension_types,
|
87
|
+
alpha: 1 / ([Math.log([number_of_values, 2].max), 10].min) # from 1.0 to 0.1
|
88
88
|
}
|
89
89
|
end
|
90
90
|
|
@@ -11,7 +11,7 @@ class RailsDataExplorer
|
|
11
11
|
|
12
12
|
def compute_chart_attrs
|
13
13
|
dimension_data_series = @data_set.data_series.find_all { |ds|
|
14
|
-
(ds.chart_roles[Chart::
|
14
|
+
(ds.chart_roles[Chart::ParallelSet] & [:dimension, :any]).any?
|
15
15
|
}
|
16
16
|
return false if dimension_data_series.empty?
|
17
17
|
|
@@ -28,8 +28,8 @@ class RailsDataExplorer
|
|
28
28
|
}
|
29
29
|
end
|
30
30
|
{
|
31
|
-
:
|
32
|
-
:
|
31
|
+
dimensions: dimension_names,
|
32
|
+
values: dimension_values
|
33
33
|
}
|
34
34
|
end
|
35
35
|
|
@@ -9,10 +9,10 @@ class RailsDataExplorer
|
|
9
9
|
|
10
10
|
def compute_chart_attrs
|
11
11
|
x_candidates = @data_set.data_series.find_all { |ds|
|
12
|
-
(ds.chart_roles[Chart::
|
12
|
+
(ds.chart_roles[Chart::StackedBarChartCategoricalPercent] & [:x, :any]).any?
|
13
13
|
}.sort { |a,b| b.uniq_vals.length <=> a.uniq_vals.length }
|
14
14
|
y_candidates = @data_set.data_series.find_all { |ds|
|
15
|
-
(ds.chart_roles[Chart::
|
15
|
+
(ds.chart_roles[Chart::StackedBarChartCategoricalPercent] & [:y, :any]).any?
|
16
16
|
}
|
17
17
|
|
18
18
|
x_ds = x_candidates.first
|
@@ -20,7 +20,7 @@ class RailsDataExplorer
|
|
20
20
|
return false if x_ds.nil? || y_ds.nil?
|
21
21
|
|
22
22
|
# initialize data_matrix
|
23
|
-
data_matrix = { :
|
23
|
+
data_matrix = { _sum: { _sum: 0 } }
|
24
24
|
x_ds.uniq_vals.each { |x_val|
|
25
25
|
data_matrix[x_val] = {}
|
26
26
|
data_matrix[x_val][:_sum] = 0
|
@@ -5,20 +5,20 @@ class RailsDataExplorer
|
|
5
5
|
# http://en.wikipedia.org/wiki/Significant_figures
|
6
6
|
|
7
7
|
attr_reader :data_type, :name, :values, :chart_roles
|
8
|
-
delegate :available_chart_types, :
|
9
|
-
delegate :available_chart_roles, :
|
8
|
+
delegate :available_chart_types, to: :data_type, prefix: false
|
9
|
+
delegate :available_chart_roles, to: :data_type, prefix: false
|
10
10
|
|
11
11
|
# Any data series with a dynamic range greater than this is considered
|
12
12
|
# having a large dynamic range
|
13
13
|
# We consider dynamic range the ratio between the largest and the smallest value.
|
14
|
-
def self.
|
15
|
-
|
14
|
+
def self.large_dynamic_range_threshold
|
15
|
+
10000.0
|
16
16
|
end
|
17
17
|
|
18
18
|
# Any data series with more than this uniq vals is considered having many
|
19
19
|
# uniq values.
|
20
|
-
def self.
|
21
|
-
|
20
|
+
def self.many_uniq_vals_threshold
|
21
|
+
30
|
22
22
|
end
|
23
23
|
|
24
24
|
# options: :chart_roles, :data_type (all optional)
|
@@ -40,6 +40,10 @@ class RailsDataExplorer
|
|
40
40
|
@data_type.descriptive_statistics_table(values)
|
41
41
|
end
|
42
42
|
|
43
|
+
def number_of_values
|
44
|
+
values.length
|
45
|
+
end
|
46
|
+
|
43
47
|
def values_summary
|
44
48
|
if values.length < 3 || values.inspect.length < 80
|
45
49
|
values.inspect
|
@@ -66,37 +70,39 @@ class RailsDataExplorer
|
|
66
70
|
data_type.axis_tick_format(values)
|
67
71
|
end
|
68
72
|
|
69
|
-
|
70
|
-
|
73
|
+
# @param[Symbol] d3_or_vega :d3 or :vega
|
74
|
+
def axis_scale(d3_or_vega)
|
75
|
+
data_type.axis_scale(self, d3_or_vega)
|
71
76
|
end
|
72
77
|
|
73
78
|
def uniq_vals
|
74
|
-
@uniq_vals
|
79
|
+
@uniq_vals ||= values.uniq
|
75
80
|
end
|
76
81
|
|
77
82
|
def uniq_vals_count
|
78
|
-
@uniq_vals_count
|
83
|
+
@uniq_vals_count ||= uniq_vals.length
|
79
84
|
end
|
80
85
|
|
81
86
|
def min_val
|
82
|
-
@min_val
|
87
|
+
@min_val ||= values.compact.min
|
83
88
|
end
|
84
89
|
|
85
90
|
def max_val
|
86
|
-
@max_val
|
91
|
+
@max_val ||= values.compact.max
|
87
92
|
end
|
88
93
|
|
89
94
|
# Used to decide whether we can render certain chart types
|
90
95
|
def has_many_uniq_vals?
|
91
|
-
uniq_vals_count > self.class.
|
96
|
+
uniq_vals_count > self.class.many_uniq_vals_threshold
|
92
97
|
end
|
93
98
|
|
94
99
|
def dynamic_range
|
100
|
+
# TODO: avoid division by zero
|
95
101
|
max_val / [min_val, max_val].min.to_f
|
96
102
|
end
|
97
103
|
|
98
104
|
def has_large_dynamic_range?
|
99
|
-
dynamic_range > self.class.
|
105
|
+
dynamic_range > self.class.large_dynamic_range_threshold
|
100
106
|
end
|
101
107
|
|
102
108
|
def label_sorter(label_val_key, value_sorter)
|
@@ -2,20 +2,22 @@ class RailsDataExplorer
|
|
2
2
|
class DataType
|
3
3
|
class Categorical < DataType
|
4
4
|
|
5
|
+
# TODO: when there are too many categories, only separate the N most
|
6
|
+
# significant ones and group all other values under "Other"
|
5
7
|
def all_available_chart_types
|
6
8
|
[
|
7
9
|
{
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
12
|
-
},
|
13
|
-
{
|
14
|
-
:chart_class => Chart::PieChart,
|
15
|
-
:chart_roles => [:any],
|
16
|
-
:dimensions_count_min => 1,
|
17
|
-
:dimensions_count_max => 1,
|
10
|
+
chart_class: Chart::HistogramCategorical,
|
11
|
+
chart_roles: [:x],
|
12
|
+
dimensions_count_min: 1,
|
13
|
+
dimensions_count_max: 1,
|
18
14
|
},
|
15
|
+
# {
|
16
|
+
# chart_class: Chart::PieChart,
|
17
|
+
# chart_roles: [:any],
|
18
|
+
# dimensions_count_min: 1,
|
19
|
+
# dimensions_count_max: 1,
|
20
|
+
# },
|
19
21
|
{
|
20
22
|
chart_class: Chart::BoxPlotGroup,
|
21
23
|
chart_roles: [:y],
|
@@ -38,6 +40,12 @@ class RailsDataExplorer
|
|
38
40
|
dimensions_count_min: 2,
|
39
41
|
dimensions_count_max: 2,
|
40
42
|
},
|
43
|
+
{
|
44
|
+
chart_class: Chart::StackedHistogramTemporal,
|
45
|
+
chart_roles: [:y],
|
46
|
+
dimensions_count_min: 2,
|
47
|
+
dimensions_count_max: 2,
|
48
|
+
},
|
41
49
|
{
|
42
50
|
chart_class: Chart::ContingencyTable,
|
43
51
|
chart_roles: [:any],
|
@@ -60,25 +68,31 @@ class RailsDataExplorer
|
|
60
68
|
|
61
69
|
def descriptive_statistics(values)
|
62
70
|
frequencies = values.inject(Hash.new(0)) { |m,e| m[e] += 1; m }
|
71
|
+
labels_ds = DataSeries.new('_', values.uniq)
|
63
72
|
total_count = values.length
|
64
73
|
ruby_formatters = {
|
65
|
-
:
|
66
|
-
:
|
74
|
+
integer: Proc.new { |v| number_with_delimiter(v.round) },
|
75
|
+
percent: Proc.new { |v| number_to_percentage(v, precision: 3, significant: true, strip_insignificant_zeros: true, delimiter: ',') },
|
67
76
|
}
|
68
77
|
r = frequencies.inject([]) { |m, (k,v)|
|
69
|
-
m << { :
|
70
|
-
m << { :
|
78
|
+
m << { label: "#{ k.to_s }_count", value: v, ruby_formatter: ruby_formatters[:integer] }
|
79
|
+
m << { label: "#{ k.to_s }_percent", value: (v / total_count.to_f) * 100, ruby_formatter: ruby_formatters[:percent] }
|
71
80
|
m
|
72
|
-
}.sort
|
73
|
-
|
74
|
-
|
81
|
+
}.sort(
|
82
|
+
&labels_ds.label_sorter(
|
83
|
+
:label,
|
84
|
+
lambda { |a,b| b[:value] <=> a[:value] }
|
85
|
+
)
|
86
|
+
)
|
87
|
+
r.insert(0, { label: '[Total]_count', value: total_count, ruby_formatter: ruby_formatters[:integer] })
|
88
|
+
r.insert(0, { label: '[Total]_percent', value: 100, ruby_formatter: ruby_formatters[:percent] })
|
75
89
|
r
|
76
90
|
end
|
77
91
|
|
78
|
-
# Returns an
|
92
|
+
# Returns an object that describes a statistics table.
|
79
93
|
def descriptive_statistics_table(values)
|
80
94
|
desc_stats = descriptive_statistics(values)
|
81
|
-
if desc_stats.length < DataSeries.
|
95
|
+
if desc_stats.length < DataSeries.many_uniq_vals_threshold
|
82
96
|
descriptive_statistics_table_horizontal(desc_stats)
|
83
97
|
else
|
84
98
|
descriptive_statistics_table_vertical(desc_stats)
|
@@ -87,68 +101,64 @@ class RailsDataExplorer
|
|
87
101
|
|
88
102
|
def descriptive_statistics_table_horizontal(desc_stats)
|
89
103
|
labels = desc_stats.map { |e| e[:label].gsub(/_count|_percent/, '') }.uniq
|
90
|
-
table =
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
:
|
97
|
-
OpenStruct.new(:value => label, :ruby_formatter => Proc.new { |e| e }, :tag => :th, :css_class => 'rde-cell-label')
|
98
|
-
}
|
104
|
+
table = Utils::RdeTable.new([])
|
105
|
+
table.rows << Utils::RdeTableRow.new(
|
106
|
+
:tr,
|
107
|
+
labels.map { |label|
|
108
|
+
Utils::RdeTableCell.new(:th, label, ruby_formatter: Proc.new { |e| e }, css_class: 'rde-cell-label')
|
109
|
+
},
|
110
|
+
css_class: 'rde-column_header'
|
99
111
|
)
|
100
|
-
table.rows <<
|
101
|
-
:
|
102
|
-
|
103
|
-
:cells => labels.map { |label|
|
112
|
+
table.rows << Utils::RdeTableRow.new(
|
113
|
+
:tr,
|
114
|
+
labels.map { |label|
|
104
115
|
stat = desc_stats.detect { |e| "#{ label }_count" == e[:label] }
|
105
|
-
|
106
|
-
}
|
116
|
+
Utils::RdeTableCell.new(:td, stat[:value], ruby_formatter: stat[:ruby_formatter], css_class: 'rde-cell-value')
|
117
|
+
},
|
118
|
+
css_class: 'rde-data_row'
|
107
119
|
)
|
108
|
-
table.rows <<
|
109
|
-
:
|
110
|
-
|
111
|
-
:cells => labels.map { |label|
|
120
|
+
table.rows << Utils::RdeTableRow.new(
|
121
|
+
:tr,
|
122
|
+
labels.map { |label|
|
112
123
|
stat = desc_stats.detect { |e| "#{ label }_percent" == e[:label] }
|
113
|
-
|
114
|
-
}
|
124
|
+
Utils::RdeTableCell.new(:td, stat[:value], ruby_formatter: stat[:ruby_formatter], css_class: 'rde-cell-value')
|
125
|
+
},
|
126
|
+
css_class: 'rde-data_row'
|
115
127
|
)
|
116
128
|
table
|
117
129
|
end
|
118
130
|
|
119
131
|
def descriptive_statistics_table_vertical(desc_stats)
|
120
132
|
labels = desc_stats.map { |e| e[:label].gsub(/_count|_percent/, '') }.uniq
|
121
|
-
table =
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
:
|
128
|
-
OpenStruct.new(:value => label, :tag => :th, :css_class => 'rde-cell-label')
|
129
|
-
}
|
133
|
+
table = Utils::RdeTable.new([])
|
134
|
+
table.rows << Utils::RdeTableRow.new(
|
135
|
+
:tr,
|
136
|
+
%w[Value Count Percent].map { |label|
|
137
|
+
Utils::RdeTableCell.new(:th, label, css_class: 'rde-cell-label')
|
138
|
+
},
|
139
|
+
css_class: 'rde-column_header',
|
130
140
|
)
|
131
141
|
labels.each { |label|
|
132
142
|
count_stat = desc_stats.detect { |e| "#{ label }_count" == e[:label] }
|
133
143
|
percent_stat = desc_stats.detect { |e| "#{ label }_percent" == e[:label] }
|
134
|
-
table.rows <<
|
135
|
-
:
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
:
|
142
|
-
:
|
143
|
-
:css_class => 'rde-cell-value'
|
144
|
+
table.rows << Utils::RdeTableRow.new(
|
145
|
+
:tr,
|
146
|
+
[
|
147
|
+
Utils::RdeTableCell.new(:td, label, css_class: 'rde-cell-value'),
|
148
|
+
Utils::RdeTableCell.new(
|
149
|
+
:td,
|
150
|
+
count_stat[:value],
|
151
|
+
ruby_formatter: count_stat[:ruby_formatter],
|
152
|
+
css_class: 'rde-cell-value'
|
144
153
|
),
|
145
|
-
|
146
|
-
:
|
147
|
-
|
148
|
-
:
|
149
|
-
:
|
154
|
+
Utils::RdeTableCell.new(
|
155
|
+
:td,
|
156
|
+
percent_stat[:value],
|
157
|
+
ruby_formatter: percent_stat[:ruby_formatter],
|
158
|
+
css_class: 'rde-cell-value'
|
150
159
|
),
|
151
|
-
]
|
160
|
+
],
|
161
|
+
css_class: 'rde-data_row',
|
152
162
|
)
|
153
163
|
}
|
154
164
|
table
|
@@ -163,18 +173,20 @@ class RailsDataExplorer
|
|
163
173
|
# @param[Proc] value_sorter the sorting proc to use if not sorted numerically
|
164
174
|
# @return[Proc] a Proc that will be used by #sort
|
165
175
|
def label_sorter(label_val_key, data_series, value_sorter)
|
166
|
-
if data_series.uniq_vals.any? { |e| e.to_s =~
|
176
|
+
if data_series.uniq_vals.any? { |e| e.to_s =~ /^[\+\-]?\d+/ }
|
167
177
|
# Sort numerical categories by key ASC
|
168
178
|
lambda { |a,b|
|
169
|
-
|
179
|
+
number_and_full_string_extractor = lambda { |val|
|
170
180
|
str = label_val_key ? val[label_val_key] : val
|
171
|
-
number = str.gsub(/^[^\d]*/, '')
|
181
|
+
number = str.gsub(/^[^\d\+\-]*/, '') # remove non-digit leading chars
|
182
|
+
.gsub(',', '') # remove delimiter commas, they throw off to_f parsing
|
183
|
+
.to_f
|
172
184
|
number += 1 if str =~ /^>/ # increase highest threshold by one for proper sorting
|
173
|
-
number
|
185
|
+
[number, str]
|
174
186
|
}
|
175
|
-
|
176
|
-
|
177
|
-
|
187
|
+
a_number_and_full_string = number_and_full_string_extractor.call(a)
|
188
|
+
b_number_and_full_string = number_and_full_string_extractor.call(b)
|
189
|
+
a_number_and_full_string <=> b_number_and_full_string
|
178
190
|
}
|
179
191
|
else
|
180
192
|
# Use provided value sorter
|
@@ -47,10 +47,10 @@ class RailsDataExplorer
|
|
47
47
|
non_nil_values = values.find_all { |e| !(e.nil? || Float::NAN == e) }
|
48
48
|
stats = ::DescriptiveStatistics::Stats.new(non_nil_values)
|
49
49
|
ruby_formatters = {
|
50
|
-
:
|
50
|
+
integer: Proc.new { |v|
|
51
51
|
v.nil? ? 'Null' : number_with_delimiter(v.round)
|
52
52
|
},
|
53
|
-
:
|
53
|
+
decimal: Proc.new { |v|
|
54
54
|
case
|
55
55
|
when v.nil?
|
56
56
|
'Null'
|
@@ -59,62 +59,60 @@ class RailsDataExplorer
|
|
59
59
|
else
|
60
60
|
number_with_precision(
|
61
61
|
v,
|
62
|
-
:
|
63
|
-
:
|
64
|
-
:
|
65
|
-
:
|
62
|
+
precision: 3,
|
63
|
+
significant: true,
|
64
|
+
strip_insignificant_zeros: true,
|
65
|
+
delimiter: ','
|
66
66
|
)
|
67
67
|
end
|
68
68
|
},
|
69
|
-
:
|
69
|
+
pass_through: Proc.new { |v| (v.nil? || Float::NAN == v) ? 'NaN' : v },
|
70
70
|
}
|
71
71
|
[
|
72
|
-
{ :
|
73
|
-
{ :
|
74
|
-
{ :
|
75
|
-
{ :
|
76
|
-
{ :
|
77
|
-
{ :
|
78
|
-
{ :
|
79
|
-
{ :
|
80
|
-
{ :
|
81
|
-
{ :
|
82
|
-
{ :
|
72
|
+
{ label: 'Min', value: stats.min, ruby_formatter: ruby_formatters[:decimal], table_row: 1 },
|
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 },
|
75
|
+
{ label: '10%ile', value: stats.value_from_percentile(10), ruby_formatter: ruby_formatters[:decimal], table_row: 1 },
|
76
|
+
{ label: '25%ile', value: stats.value_from_percentile(25), ruby_formatter: ruby_formatters[:decimal], table_row: 1 },
|
77
|
+
{ label: 'Median', value: stats.median, ruby_formatter: ruby_formatters[:decimal], table_row: 1 },
|
78
|
+
{ label: '75%ile', value: stats.value_from_percentile(75), ruby_formatter: ruby_formatters[:decimal], table_row: 1 },
|
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 },
|
81
|
+
{ label: '99%ile', value: stats.value_from_percentile(99), ruby_formatter: ruby_formatters[:decimal], table_row: 1 },
|
82
|
+
{ label: 'Max', value: stats.max, ruby_formatter: ruby_formatters[:decimal], table_row: 1 },
|
83
83
|
|
84
|
-
{ :
|
85
|
-
{ :
|
86
|
-
{ :
|
87
|
-
{ :
|
88
|
-
{ :
|
89
|
-
{ :
|
90
|
-
{ :
|
91
|
-
{ :
|
92
|
-
{ :
|
93
|
-
{ :
|
94
|
-
{ :
|
84
|
+
{ label: 'Range', value: stats.range, ruby_formatter: ruby_formatters[:decimal], table_row: 2 },
|
85
|
+
{ label: 'Mean', value: stats.mean, ruby_formatter: ruby_formatters[:decimal], table_row: 2 },
|
86
|
+
{ label: 'Mode', value: stats.mode, ruby_formatter: ruby_formatters[:decimal], table_row: 2 },
|
87
|
+
{ label: 'Count', value: values.length, ruby_formatter: ruby_formatters[:integer], table_row: 2 },
|
88
|
+
{ label: 'Sum', value: non_nil_values.inject(0) { |m,e| m += e }, ruby_formatter: ruby_formatters[:decimal], table_row: 2 },
|
89
|
+
{ label: 'Variance', value: stats.variance, ruby_formatter: ruby_formatters[:decimal], table_row: 2 },
|
90
|
+
{ label: 'Std. dev.', value: stats.standard_deviation, ruby_formatter: ruby_formatters[:decimal], table_row: 2 },
|
91
|
+
{ label: 'Rel. std. dev.', value: stats.relative_standard_deviation, ruby_formatter: ruby_formatters[:decimal], table_row: 2 },
|
92
|
+
{ label: 'Skewness', value: stats.skewness, ruby_formatter: ruby_formatters[:decimal], table_row: 2 },
|
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 },
|
95
95
|
]
|
96
96
|
end
|
97
97
|
|
98
|
-
# Returns an
|
98
|
+
# Returns an object that describes a statistics table.
|
99
99
|
def descriptive_statistics_table(values)
|
100
100
|
desc_stats = descriptive_statistics(values)
|
101
|
-
table =
|
102
|
-
:rows => []
|
103
|
-
)
|
101
|
+
table = Utils::RdeTable.new([])
|
104
102
|
[1,2].each do |table_row|
|
105
|
-
table.rows <<
|
106
|
-
:
|
107
|
-
:
|
108
|
-
|
109
|
-
|
110
|
-
|
103
|
+
table.rows << Utils::RdeTableRow.new(
|
104
|
+
:tr,
|
105
|
+
desc_stats.find_all { |e| table_row == e[:table_row] }.map { |stat|
|
106
|
+
Utils::RdeTableCell.new(:th, stat[:label], ruby_formatter: Proc.new { |e| e }, css_class: 'rde-cell-label')
|
107
|
+
},
|
108
|
+
css_class: 'rde-column_header'
|
111
109
|
)
|
112
|
-
table.rows <<
|
113
|
-
:
|
114
|
-
:
|
115
|
-
|
116
|
-
|
117
|
-
|
110
|
+
table.rows << Utils::RdeTableRow.new(
|
111
|
+
:tr,
|
112
|
+
desc_stats.find_all { |e| table_row == e[:table_row] }.map { |stat|
|
113
|
+
Utils::RdeTableCell.new(:td, stat[:value], ruby_formatter: stat[:ruby_formatter], css_class: 'rde-cell-value')
|
114
|
+
},
|
115
|
+
css_class: 'rde-data_row'
|
118
116
|
)
|
119
117
|
end
|
120
118
|
table
|
@@ -124,12 +122,12 @@ class RailsDataExplorer
|
|
124
122
|
raise "Implement me in sub_class"
|
125
123
|
end
|
126
124
|
|
127
|
-
def axis_scale(data_series)
|
125
|
+
def axis_scale(data_series, d3_or_vega)
|
128
126
|
# Log scales can't handle 0 values
|
129
127
|
if data_series.min_val > 0.0 && data_series.has_large_dynamic_range?
|
130
|
-
'd3.scale.log'
|
128
|
+
{ d3: 'd3.scale.log', vega: 'log' }[d3_or_vega]
|
131
129
|
else
|
132
|
-
'd3.scale.linear'
|
130
|
+
{ d3: 'd3.scale.linear', vega: 'linear' }[d3_or_vega]
|
133
131
|
end
|
134
132
|
end
|
135
133
|
|