Interactive Data Visualization for the Web

Dick Kreisberg

Systems Bioinformatics Workshop

Sept 11, 2012

vis.systemsbiology.net/sbw_2012/

Outline

  • Introduction
  • HTML5
  • D3.js + CSS3 + SVG

What can Interactive Vis do for me?

  • Convey meaning
  • Manage the 3 hurdles of data...
    Scope, Scale, Complexity
  • Provide supporting data and context

HTML

<!doctype html>
<html>
 <head>
  <title>Hello World</title>
  <link rel="stylesheet" type="text/css" href="css/b.css"/>
  <script type="text/javascript"src='js/a.js'></script>
 </head>
 <body>
  <div id="svg_container">    
   <svg width="450" height="300"
           xmlns="http://www.w3.org/2000/svg"></svg>
   <button>Hi!</button>
  </div>
 </body>
</html>

CSS3 Selectors

<div id="BRCA">
 <ul class="mutations">
  <li>1</li>
  <li>2</li>
 </ul>
<div>
with jQuery:
$('#BRCA') == <div>....</div>
$('.mutations') == <ul>...</ul>
$('.mutations li') == [<li>1</li>,<li>2</li>]
$('.mutations li:first') == <li>1</li>
or d3:
d3.select('#BRCA') == <div>....</div>
d3.select('.mutations') == <ul>...</ul>
d3.selectAll('.mutations li') == [<li>1</li>,<li>2</li>]
d3.select('.mutations li:first') == <li>1</li>

SVG

<svg id="updating-plot" xmlns="http://www.w3.org/2000/svg"
	width="450" height="200">
 <g class="canvas" transform="translate(25,15)">
   <g class="axes"></g>
   <g class="labels"></g>
   <g class="data">
     <circle cx="135" cy="150" fill="#F23" stroke="none" r="6"/> </g>
 <rect fill="none" stroke="#0000FF" stroke-width="2"
  x="60" y="30" width="100" height="60" transform="rotate(45)"/>
  <path fill="none" stroke="#444444" stroke-width="2"
    d="M40 70 L240 100"/>
  <text x="100" y="30" class="label">Hello World</text>
  </g>
</svg>
Hello World

ECMAScript5

And the HTML Document API!

Javascript is growing (ES6 is coming) into a full featured tool. It enables a growing number of interactions between the user, the browser, and the data.


D3.js

d3js.org

Javascript library that joins data to the document (DOM). Created by the author of Protovis, Mike Bostock. Several other regular contributors on the project.

D3 is not rigidly coupled to SVG. SVG lends itself to functional operations. D3 does provide high level generators for creating SVG objects quickly.

Let's Make Something

jsfiddle.net/rbkreisberg/cdpcX/

Scatterplot with: axes, ticks, labels, cardinal interpolation, event-driven behavior, periodic behavior.

Follow Along!

jsfiddle.net/rbkreisberg/cdpcX/1/
var w = 450,
    h = 300;

//attach an svg object to the DOM.  Size it appropriately    
var svg = d3.select('#plot')
            .append('svg:svg')
              .attr('width', w)
              .attr('height', h);
    
//draw an outer border.
    svg.append('rect')
        .attr('x', '0')
        .attr('y', '0')
        .attr('width', w)
        .attr('height', h)
        .attr('stroke-width', '2')
        .attr('stroke', 'black')
        .attr('fill', 'none');        

         //attach a title to the graph using a svg:text object    
    svg.append('text')
        .attr('class', 'title')
        .attr('x', w/2 - 40) //position in the center (sorta)
        .attr('y', "14")
        .text('Scatter Plot!');
}

Data -> Rendered Circles

Change of Coordinates

jsfiddle.net/rbkreisberg/cdpcX/2/
F(data) = Viewport.X
G(data) = Viewport.Y
//x_scale maps the array index to the viewport
var x_scale = d3.scale.linear()
                  .domain([0,data_array.length-1])
                  .range([0,viewport_width]);

//invert the y_scale such that higher values is closer to the top
var y_scale = d3.scale.linear()
                  .domain([0,1000])
                  .range([viewport_height,0]);
Create Axes
//d3 function to create an axis from x_scale with the domain flipped
var x_axis = d3.svg.axis()
                .scale(x_scale.copy().domain([20,0]))//use the x_scale
                .tickSize(-screen_height)
                .ticks(5)
                .orient('bottom');
    
d3.select('.unclipped-area')
  .append('g')
    .attr('class','x_axis')
    .attr('transform','translate(0,' + screen_height + ')')
    .call(x_axis);  //call the axis generator
Place Data in Viewport, Functionally
var data_array = [
	{x:32, y:224}, //functionally, instead
        {x:47, y:113},
        {x:128, y:63},
        {x:178, y:160},
        ...
        ];
function drawCircles() {
d3.select('.scatter_plot')
  .selectAll('.data_point')
  .data(data_array)
  .enter()
   .append('circle')  //add the circles
    .attr('class', 'data_point')
    .attr('cx', function(point) { return x_scale(point.x); })
    .attr('cy', function(point) { return y_scale(point.y); })
    .attr('r', 4 ) //radius of 4
    .style('fill', 'blue' );//set style fill to blue
}

Data -> Color, Lines

jsfiddle.net/rbkreisberg/cdpcX/3/

Color by Value

z_color_scale = d3.scale.linear()
                    .domain([0,100])
                    .range(['blue','red']);

function drawCircles() {
...
       .attr('r',4)
       .style('fill',function(point) { 
            return z_color_scale(point.z);})
}
function setupRendering() {
    // create the path based on the index and y values    
    create_line = d3.svg.line()
          .x(function(point,i) {return x_scale(i);})
          .y(function(point) {return y_scale(point.y);})
          .interpolate('cardinal')
          .tension(0.7);
}

function drawLine() {
    d3.select('.line_plot')
        .selectAll('.data_line')
        .data([data_array])
      .enter()
      .append('path')	//path = line
        .attr('class','data_line')
        .attr('d',create_line); // 'd' determines the path
}

Animate with Transition()

jsfiddle.net/rbkreisberg/cdpcX/4/
function drawCircles() {
    d3.select('.scatter_plot')
...
.on('mouseover',function()  {  //change size and color on mouseover
	d3.select(this)
	.transition()
	  .duration(500)
	  .attr('r', 15)
	  .style('fill', 'black');
	})
.on('mouseout',function() {  //restore on mouseout
  d3.select(this)
    .transition()
       .duration(500)
       .attr( 'r', 4)                
       .style( 'fill', function(point) { 
             return z_color_scale(point.z); })
});

function appendDataPoint() {
  data_array.push(createDataPoint());// push a new point onto the back
    d3.select('.scatter_plot')
        .selectAll('.data_point')
        .remove();  //remove the circles

      drawCircles(); //redraw circles       

  d3.select('.data_plot')//move the parent panel of the circles to the left
        .attr("transform", null) //reset translation to zero
      .transition()  //begin the transition definition
        .duration(1000)  //for 1000 msec
        .ease("linear")  //at a constant rate        
        .attr("transform", "translate(" + x_scale(-1) + ")") //go left!
        .each("end", appendDataPoint);  //rinse and repeat
  
  data_array.shift(); // pop the oldest data point off of the array
}

Clip Away the Excess

jsfiddle.net/rbkreisberg/cdpcX/5/
//clipPath prevents the data from bleeding over into other areas    
svg.append("defs")
   .append("clipPath")
	  .attr("id", "clip")
   .append("rect")
	  .attr("width", w*0.8)
	  .attr("height", h*0.8);

//the plot area is inset from the border
svg.append('g')
     .attr('class','unclipped-area')  
     .attr('transform' , 'translate(' + ir_x_offset + ',' + ir_y_offset + ')')     
   .append('g')        
     .attr('class','clipped-area')     
     .attr("clip-path", "url(#clip)"); //attach clip

Resources

D3.js

Mike Bostock - bost.ocks.org/mike/
d3js.org
github.com/mbostock/d3
D3 Google Groups
Stack Overflow

HTML, SVG, Javascript, R

w3schools.com
learnsvg.com
svgbasics.com
codeacademy.com
polychart.com/js

Projects @ ISB

Regulome Explorer
explorer.cancerregulome.org

Transcriptional Regulation & Epigenetic Landscape
trel.systemsbiology.net

Pubcrawl
pubcrawl.systemsbiology.net


Search 'codefor@systemsbiology.org' at code.google.com



dick.kreisberg@systemsbiology.org

Questions?

Reveal.JS

lab.hakim.se/reveal-js/

JSFiddle

jsfiddle.net

NodeJS

nodejs.org

Circos

circos.ca