# -*- coding: utf-8 -*- class RailsDataExplorer class DataType # This is an abstract class. Use sub_classes # # Responsibilities: # * Provide available charts and statistics for quantitative data type. # * Provide methods for quantitative data type. # # Collaborators: # * DataSet # class Quantitative < DataType def all_available_chart_types [ # { # chart_class: Chart::BoxPlot, # chart_roles: [:y], # dimensions_count_min: 1, # dimensions_count_max: 1 # }, { chart_class: Chart::HistogramQuantitative, chart_roles: [:x], dimensions_count_min: 1, dimensions_count_max: 1 }, { chart_class: Chart::BoxPlotGroup, chart_roles: [:x], dimensions_count_min: 2, dimensions_count_max: 2, }, { chart_class: Chart::Scatterplot, chart_roles: [:x, :y, :size], dimensions_count_min: 2 }, { chart_class: Chart::DescriptiveStatisticsTable, chart_roles: [:any], dimensions_count_min: 1, dimensions_count_max: 1 }, { chart_class: Chart::ParallelCoordinates, chart_roles: [:dimension], dimensions_count_min: 3, }, ].freeze end def descriptive_statistics(values) non_nil_values = values.find_all { |e| !(e.nil? || Float::NAN == e) } stats = ::DescriptiveStatistics::Stats.new(non_nil_values) ruby_formatters = { integer: Proc.new { |v| v.nil? ? 'Null' : number_with_delimiter(v.round) }, decimal: Proc.new { |v| case when v.nil? 'Null' when v.is_a?(Float) && v.nan? 'NaN' else number_with_precision( v, precision: 3, significant: true, strip_insignificant_zeros: true, delimiter: ',' ) end }, pass_through: Proc.new { |v| (v.nil? || Float::NAN == v) ? 'NaN' : v }, } [ { label: 'Min', value: stats.min, ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: '1%ile', value: stats.value_from_percentile(1), ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: '5%ile', value: stats.value_from_percentile(5), ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: '10%ile', value: stats.value_from_percentile(10), ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: '25%ile', value: stats.value_from_percentile(25), ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: 'Median', value: stats.median, ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: '75%ile', value: stats.value_from_percentile(75), ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: '90%ile', value: stats.value_from_percentile(90), ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: '95%ile', value: stats.value_from_percentile(95), ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: '99%ile', value: stats.value_from_percentile(99), ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: 'Max', value: stats.max, ruby_formatter: ruby_formatters[:decimal], table_row: 1 }, { label: 'Range', value: stats.range, ruby_formatter: ruby_formatters[:decimal], table_row: 2 }, { label: 'Mean', value: stats.mean, ruby_formatter: ruby_formatters[:decimal], table_row: 2 }, { label: 'Mode', value: stats.mode, ruby_formatter: ruby_formatters[:decimal], table_row: 2 }, { label: 'Count', value: values.length, ruby_formatter: ruby_formatters[:integer], table_row: 2 }, { label: 'Sum', value: non_nil_values.inject(0) { |m,e| m += e }, ruby_formatter: ruby_formatters[:decimal], table_row: 2 }, { label: 'Variance', value: stats.variance, ruby_formatter: ruby_formatters[:decimal], table_row: 2 }, { label: 'Std. dev.', value: stats.standard_deviation, ruby_formatter: ruby_formatters[:decimal], table_row: 2 }, { label: 'Rel. std. dev.', value: stats.relative_standard_deviation, ruby_formatter: ruby_formatters[:decimal], table_row: 2 }, { label: 'Skewness', value: stats.skewness, ruby_formatter: ruby_formatters[:decimal], table_row: 2 }, { label: 'Kurtosis', value: stats.kurtosis, ruby_formatter: ruby_formatters[:decimal], table_row: 2 }, { label: '', value: '', ruby_formatter: ruby_formatters[:pass_through], table_row: 2 }, ] end # Returns an object that describes a statistics table. def descriptive_statistics_table(values) desc_stats = descriptive_statistics(values) table = Utils::RdeTable.new([]) [1,2].each do |table_row| table.rows << Utils::RdeTableRow.new( :tr, desc_stats.find_all { |e| table_row == e[:table_row] }.map { |stat| Utils::RdeTableCell.new(:th, stat[:label], ruby_formatter: Proc.new { |e| e }, css_class: 'rde-cell-label') }, css_class: 'rde-column_header' ) table.rows << Utils::RdeTableRow.new( :tr, desc_stats.find_all { |e| table_row == e[:table_row] }.map { |stat| Utils::RdeTableCell.new(:td, stat[:value], ruby_formatter: stat[:ruby_formatter], css_class: 'rde-cell-value') }, css_class: 'rde-data_row' ) end table end def axis_tick_format(values) raise "Implement me in sub_class" end def axis_scale(data_series, modification, d3_or_vega) # Log scales can't handle 0 values if data_series.min_val(modification) > 0.0 && data_series.has_large_dynamic_range?(modification) { d3: 'd3.scale.log', vega: 'log' }[d3_or_vega] else { d3: 'd3.scale.linear', vega: 'linear' }[d3_or_vega] end end end end end