Interactive data visualization: D3

Mahbubul Majumder, PhD
Oct 30, 2014

What is D3 ?

  • Data Driven Document

  • JavaScript library provided by Mike Bostock

    • functions to generate elegant web graphics
    • smooth transition, animation and allows interactivity with graphics
  • Knowledge needed besides math/stat

    • HTML, CSS, JavaScript
    • Document Object Model (DOM) such as XML tree
    • Scalable Vector Graphics (SVG)
  • It is intended for displaying facts already discovered by other tools like ggplot2

Basic building blocks

  • SVG comes with a basic set of shape elements:

    • rectangle
    • circle
    • ellipse
    • straight Line
    • polyline
    • polygon
  • D3 provides

    • automation
    • smooth transitions
    • interactivity

Basic SVG shapes

  • Drawing a rectangle
<svg width="500" height="100">
  <rect x="200" y="0" width="150" height="100" fill="steelblue" />
</svg>  

  • Drawing a circle
<svg width="500" height="100">
  <circle cx="270" cy="50" r="50" fill="purple" />
</svg>  

  • Drawing line
<svg width="500" height="90">
  <line x1="50" y1="5" x2="400" y2="90" stroke="maroon" stroke-width="5"/> </svg>

  • Drawing a polygon
<svg width="500" height="80">
  <polygon fill="orange" stroke="blue" stroke-width="3" points="150,75 200,10 250,50"/> </svg>

How D3 works

  • Uses JavaScripts to change the attributes of the shapes
    • data are mapped to each of the attributes
    • many codes are automated, so you don't have to worry about details
    • for transitions, the attributes are changed with some delay
    • CSS helps to implement this in more controlled way
  • D3 functionalities are loaded sourcing the D3 link. Basic HTML codes with D3 follow
<!DOCTYPE html>
 <html>
   <head>
     <script type="text/javascript" 
     src="http://d3js.org/d3.v3.min.js"> </script>
   </head>
   <body>
     <script> 
       ... Your D3 codes go here ...
     </script>
   </body>
 </html>

D3 methods and operators

  • D3 methods; such as select(), selectAll()

    • need to use dot . followed by D3 such as D3.select()
    • example D3.select("body") or D3.selectAll("circle")
  • D3 operators; such as append(), style(), data(), enter()

    • need to use . followed by D3 methods such as D3.select().append()
    • example D3.select("body").append("svg")
    • chain operation can be performed using successive dots .
d3.select("svg").append("circle")
  .attr("cx",100).attr("cy",100)
  .attr("r",50)
  .style("fill", "steelblue")

JavaScript console

  • Modern browsers provide JavaScript console

    • allows testing the script on the fly
    • values of the variables can be obtained and manipulated
    • facilitates debugging
    • for Chrome, right click and then select inspect element javascript-console
  • You can test your codes in jsfiddle as well.

Drawing a circle using D3

<html>

 <head>
  <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
 </head>

 <body>
 <h1>My circle</h1>
 <svg id="s1" width="200" height="200"></svg>

 <script>
  d3.select("#s1").append("circle")
    .attr("cx",100).attr("cy",100)
    .attr("r",50)
    .style("fill", "steelblue")
 </script>

 </body>
</html>

My circle

Mapping data to attributes

  • For plots we usually map data to attributes of the elements

    • common way of injecting data is csv file or JSON
  • For example, JSON data for the centers of three circles myDat = [50,100,150,200]

<svg id="s2" width="900" height="200">
<script>
 var myDat = [50,100,150,200];
 d3.select("#s2").selectAll("circle")
   .data(myDat).enter().append("circle")
   .attr("cx",function(d){return(d);})
   .attr("cy",100).attr("r",20)
   .style("fill", "blue");
</script>

Mapping data to attributes

<html>
 <head>
   <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
 </head>
 <body>
 <svg id="s3" width="500" height="200"></svg>
 <script>
  var myDat = [50,100,150,200];
  d3.select("#s3").selectAll("circle")
    .data(myDat).enter()
    .append("circle")
    .attr("cx",function(d){return(d);})
    .attr("cy",100).attr("r",20)
    .style("fill", "orange");
 </script>
 </body>
</html>

Output

Mapping data to attributes

<html>
 <head>
    <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
 </head>
 <body>
 <svg id="s4" width="500" height="250"></svg>
 <script>
  var myDf=[{"x":60,  "y":50,  "z":"#F8766D"},
            {"x":90,  "y":40,  "z":"#7CAE00"},
            {"x":120, "y":75,  "z":"#00BFC4"},
            {"x":150, "y":100, "z":"#C77CFF"},
            {"x":180, "y":110, "z":"#477CFF"}];
  d3.select("#s4").selectAll("circle")
    .data(myDf).enter()
    .append("circle")
    .attr("cx",function(d){return d.x;})
    .attr("cy",function(d){return d.y;})
    .attr("r",10)
    .style("fill",function(d){return d.z;});
 </script>
 </body>
</html>

Output

Similar display, but with rectangles

Interaction and transition

  • User interaction occurs when mouse is moved over an SVG object

    • on mouseover, mouseout, mouseclick
    • on each of these events you can write your JavaScript function
  • Transition occurs when some change is done smoothly rather than instantaneously

    • D3 takes care of smooth transition automatically
    • transition can be triggered by user interactivity or automatically
    • for example, a circle radius can be changed to larger number. D3 will make this transition smoothly and we don't have to worry about details.

D3 transition example

<html>
<head>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg width=900 height=350>
<circle id="c2" cx="100" cy="100" r="30" fill= "steelblue"></circle>
</svg>

<script>
animate = function() {
 d3.select(this).transition().duration(1500)
   .attr("r", 80).each("end", function () {
  d3.select(this).transition().duration(1500)
    .attr("r", 1).each("end", animate)});
}    
d3.select("#c2").transition().duration(1000)
    .attr("r", 1).each("end", animate);
</script>
</body>
</html>

Output

Drawing points based on data

<html>
<head>
  <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>  
</head>
<body>
<svg id= "mySvg" width=500 height=600></svg>
<script>  
var myDat = [10,20,30,40,50,60,70,80,90,100,110,120, 130,140,150,160,170,180,190,200,210,220,230,240,250, 260,270,280,290,300,310,320,330,340,350,360,370,380];

d3.select("#mySvg")
  .selectAll("circle")
  .data(myDat).enter()
  .append("circle")
  .attr("cy", function(d){return d;})
  .attr("cx", 20)
  .attr("r",10)
  .style("fill","steelblue");
</script>
</body>
</html>  

Output

Transitions with different delay

<script>
move = function() {
 d3.select(this).transition()
   .duration(1500)
   .attr("cx", 10)
   .each("end", function () {
     d3.select(this).transition()
       .duration(1500)
       .attr("cx", 200)
       .each("end", move)
     });
} 

d3.select("#mySvg")
  .selectAll("circle").transition()
  .delay(function(d,i){return i*100;})
  .duration(1000)
  .attr("cx", 200)
  .each("end", move);
</script>

To view output
Refresh the page

For details view the talk given by Scott Murray

Interacting with plot

<style>
  rect:hover{
    opacity: 0.5;}
</style>
<svg id ="s8" width=500 height=450></svg>
<script>
  d3.select("#s8").selectAll("text")
    .data(myDf).enter().append("text")
    .attr("font-size", "20px")
    .attr("x",function(d){return d.x;})
    .attr("y",function(d){return d.y-2;})
    .text(function(d){return d.y;});

  d3.select("#s8").selectAll("rect")
    .data(myDf).enter()
    .append("rect")
    .attr("x",function(d){return d.x;})
    .attr("y",function(d){return d.y;})
    .attr("height",function(d){return 150-d.y;})
    .attr("width",25)
    .style("fill",function(d){return d.z;});
</script>

Output

Interacting with plot

  <p id="p8">Selected bar:</p>
<script>
showHeight = function(i){
  var x=d3.select("#s8").selectAll("text");
  d3.select(x[0][i]).style("visibility", "visible");
  d3.select("#p8").text("Selected bar: " + i);}

eraseHeight = function(i){
  var x=d3.select("#s8").selectAll("text");
  d3.select(x[0][i]).style("visibility", "hidden");}

d3.select("#s8").selectAll("rect")
  .data(myDf).enter()
  .append("rect").attr("width",25)
  .attr("x",function(d){return d.x;})
  .attr("y",function(d){return d.y;})
  .attr("height",function(d){return 150-d.y;})
  .style("fill",function(d){return d.z;})
  .on("mouseover", function(d,i) {showHeight(i);})
  .on("mouseout", function(d,i) {eraseHeight(i);});
</script>    

Mouse over the bars

Selected bar:

What next?

  • We have full control of what we need to do

    • scale and axis
    • is it worth to spend that much time only to generate a histogram?
  • D3 is not intended for exploratory study or quick plotting.

    • use when you discovered something and give it a lively look
    • make animated or interactive info-graphics
    • explore NY Times info-graphics for some motivation
  • So, what are the alternatives?

    • vega could be another choice
    • for R users, the packages rCharts and rMaps show promise
    • R package ggvis is on CRAN and still being developped. Sit tight.
    • for application development R package shiny is handy

Reading assignment and references