class RailsDataExplorer::Chart::ParallelCoordinates

Responsibilities:

* Render a parallel coordinates chart for multivariate analysis of
  a mix of quantitative, temporal, and categorical data series.

Collaborators:

* DataSet

TODO: add :color chart_role (test first if it makes sense, e.g., for 'pay')

Public Class Methods

new(_data_set, options = {}) click to toggle source
# File lib/rails_data_explorer/chart/parallel_coordinates.rb, line 15
def initialize(_data_set, options = {})
  @data_set = _data_set
  @options = {}.merge(options)
end

Public Instance Methods

compute_chart_attrs() click to toggle source
# File lib/rails_data_explorer/chart/parallel_coordinates.rb, line 60
def compute_chart_attrs
  dimension_data_series = @data_set.data_series.find_all { |ds|
    (ds.chart_roles[Chart::ParallelCoordinates] & [:dimension, :any]).any?
  }
  return false  if dimension_data_series.empty?

  dimension_names = dimension_data_series.map(&:name)
  number_of_values = dimension_data_series.first.values.length
  dimension_values = number_of_values.times.map do |idx|
    dimension_data_series.inject({}) { |m,ds|
      m[ds.name] = if ds.data_type.is_a?(RailsDataExplorer::DataType::Quantitative::Temporal)
        ds.values[idx].to_i * 1000
      else
        ds.values[idx]
      end
      m
    }
  end
  dimension_types = dimension_data_series.inject({}) { |m,ds|
    m[ds.name] = case ds.data_type
    when RailsDataExplorer::DataType::Categorical
      'string'
    when RailsDataExplorer::DataType::Quantitative::Temporal
      'date'
    when RailsDataExplorer::DataType::Quantitative::Integer,
         RailsDataExplorer::DataType::Quantitative::Decimal
      'number'
    else
      raise "Unhandled data_type: #{ ds.data_type.inspect }"
    end
    m
  }
  {
    dimensions: dimension_names,
    values: dimension_values,
    types: dimension_types,
    alpha: 1 / ([Math.log([number_of_values, 2].max), 10].min) # from 1.0 to 0.1
  }
end
render() click to toggle source
# File lib/rails_data_explorer/chart/parallel_coordinates.rb, line 20
def render
  return ''  unless render?
  ca = compute_chart_attrs
  return ''  unless ca

  %(
    <div class="rde-chart rde-parallel-coordinates">
      <h3 class="rde-chart-title">Parallel coordinates</h3>
      <div id="#{ dom_id }" class="rde-chart-parallel-coordinates parcoords" style="height: 400px; width: 100%"></div>
      <script type="text/javascript">
        (function() {
          var parcoords = d3.parcoords()("##{ dom_id }")
                            .dimensions(#{ ca[:dimensions ].to_json })
                            .types(#{ ca[:types].to_json })
                            .alpha(#{ ca[:alpha] })
                            ;

          parcoords.data(#{ ca[:values].to_json })
                   .render()
                   .createAxes() // has to come before other methods that rely on axes (e.g., brushable)
                   // .shadows() // they don't redraw after reordering, so I'm turning them off for now.
                   .reorderable()
                   .brushable()
                   ;

        })();
      </script>
    </div>
  )
end
render?() click to toggle source

Render ParallelCoordinates only when there is at least one data series with DataType Quantitative. If it's all Categorical, then ParallelSet is much better suited.

# File lib/rails_data_explorer/chart/parallel_coordinates.rb, line 54
def render?
  @data_set.data_series.any? { |ds|
    ds.data_type.is_a?(RailsDataExplorer::DataType::Quantitative)
  }
end