Beautiful Data(Driven) Visualizations with Javascript
BetterSoftware 2012

Luca Mearelli
@lmea #d3js #bsw2012

When you find a box like the above in the presentation: click it!

D3 Show Reel

Javascript
+
Web technologies

Easy to tinker with, powerful, accessible (standards!)

From Data to DOM and Back

Not a visual language but a tool to link data to elements and manipulate them.

D3 is not an SVG layer, not a compatibility layer and especially not an abstraction layer.


It's a set of tools on top of web technologies to bind data to DOM elements.

Chris Viau on D3 vs JQuery

HTML5
CSS
Javascript
SVG

Web technologies

	
<svg width="400" height="300">
  <circle class="little" 
             cx="200" cy="75" r="30"
             style="stroke:green; fill:#c00000;">
  </circle>
</svg>

SVG

<!DOCTYPE html>
<meta charset="utf-8">
<style>/* CSS */</style>
<body>
<script src="d3.v2.js"></script>
<script>/* JavaScript */</script>

A d3 starter file

Use the browser developer tools


 ⌥⌘I


Serve from http


python -m SimpleHTTTPServer -p 3000

Beginners' tips

Selections

Three Little Circles

var circle = svg.selectAll("circle");

Selections

circle.style("fill", "steelblue");
circle.attr("cy", 90);
circle.attr("r", 30);

Selections: setting attributes

circle.attr("cx", function() {
  return Math.random() * w;
});
  

Selections: using functions

Data binding

circle.data([32, 57, 112]);

Data

circle.attr("cx", function(d) {
  return d;
});
circle.attr("r", function(d) {
  return Math.sqrt(d);
});

Data-bound functions

circle.attr("cx", function(d, i) {
  return i * 100 + 30;
});

Data-bound functions

var circle = svg.selectAll("circle")
    .data([32, 57, 112, 293]);

Adding data

var enter = circle.enter().append("circle");
enter.attr("cy", 90);
enter.attr("cx", 160);
enter.attr("r", function(d) {
  return Math.sqrt(d);
});

Adding data

var enter = circle.enter().append("circle");
enter.attr("cy", 90);
enter.attr("cx", function(d) {
  return d;
});
enter.attr("r", function(d) {
  return Math.sqrt(d);
});

Selections: starting from empty


svg.selectAll("circle")
    .data([32, 57, 112, 293])
  .enter().append("circle")
    .attr("cy", 90)
    .attr("cx", String)
    .attr("r", Math.sqrt);

Selections: chaining


var circle = svg.selectAll("circle")
    .data([32, 57]);

Removing data

circle.exit().remove();

Removing data

Thiking with joins

Selections: joins

var circle = svg.selectAll("circle")
    	.data([32, 57, 293], String)
    .enter().append("circle")
    	.attr("cy", 90)
    	.attr("cx", String)
    	.attr("r", Math.sqrt)
    .exit().remove();

Selections: all together

Input domain ↦ Output range

Scales

dataScale = [1000, 2000, 3000, 4000, 5000];

l = d3.scale.linear()
      .domain([1000,5000])
      .range([20, 380]);

circle.attr("cx", l);

Linear Scale

lc = d3.scale.linear()
      .domain([1000,5000])
      .range(["blue", "red"]);

circle.attr("fill", lc);

Linear Scale: Interpolation

s = d3.scale.sqrt()
      .domain([1000,5000])
      .round([10, 50]);

circle.attr("r", s);

Square Root Scale

lg = d3.scale.log()
       .domain([1000,5000])
       .range([20, 380]);

circle.attr("cx", l);

Logarithmic Scale

o = d3.scale.ordinal()
      .domain(dataScale)
      .rangePoints([h-20, 20]);
circle.attr("cx", o);

oc = d3.scale.category10(),
circle.attr("fill", oc);

Ordinal Scales

var ya = d3.svg.axis()
               .scale(o)
               .orient("left");

svg.append(ya);

Axes

Transitions

circle.transition()
      .duration(1000)
      .style("fill", "red")
      .attr("r", 100)
      .attr("cx", 150)
      .attr("cy", 150);

Animations

circle
  .transition().duration(1000)
    .attr("cx", w-60)
  .transition().duration(1000).delay(1000)
    .attr("cy", h-60)
  .transition().duration(1000).delay(2000)
    .attr("cx", 60);

Animation delay

var moveCircle = function(what) {
  return function() {
    d3.select(this).transition()
      .attr("cx", xc(what)).attr("cy", yc(what))
      .each("end", moveCircle(what+1));
    };
}
circle.transition().each("end", moveCircle(1));

Animation chaining

Loading Data

JSON

CSV

TSV

XML

Text

Loading Data

Open Data Comune di Firenze
museo;anno;presenze
"musei civici fiorentini";2006;580872
"galleria degli uffizi";2006;1663438
"galleria dell accademia";2006;1236938
"museo di san marco";2006;194627
"museo di palazzo davanzati";2006;65477
...

Loading CSV

d3.csv("ingressi_musei.csv", function(ingressi) {
  ingressi.forEach(function(d) {
    d.presenze = +d.presenze;
  });
  var uffizi = ingressi.filter(function(d){ 
	return d.museo == "galleria degli uffizi"
  });
  // then build the visualization
});

Loading CSV

var nest = d3.nest()
  .key(function(d) { return d.anno; })
  .entries(ingressi);
  
var totals = d3.nest()
  .key(function(d) { return d.anno; })
  .rollup(function(d) {
    return {
      presenze:d3.sum(d,function(g){return g.presenze;})
    };
  })
  .entries(ingressi);

Data manipulation

var rects = svg.selectAll('rect')
               .data(data, function(d){ 
                 return d.museo; 
               });

Data key

[
  {
    "museo": "musei civici fiorentini",
    "anno": 2006,
    "presenze": 580872
  },
  {
    "museo": "galleria degli uffizi",
    "anno": 2006,
    "presenze": 1663438
  },
  {
    "museo": "galleria dell accademia",
    "anno": 2006,
    "presenze": 1236938
  },
	...
]

JSON

d3.json("ingressi_musei.json", function(ingressi) { })
	

JSON

Layouts

d3.layout.force()
  .charge(-120)
  .linkDistance(30)
  .size([width, height])
  .nodes(json.nodes)
  .links(json.links)
  .start();	

Simple Force Layout

force.on("tick", function(e) {
  var k = .1 * e.alpha;
  nodes.forEach(function(o, i) {
    o.y += (foci[o.id].y - o.y) * k;
    o.x += (foci[o.id].x - o.x) * k;
  });
  svg.selectAll("circle.node")
      .attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });
});

Force Layout with Multiple Foci

d3.layout.partition();

Partition Layout

Partition Layout - zoom

Source: Mike Bostock, Tom Carden, Gapminder

The Wealth & Health of Nation

Wrapup

Is it for me?

d3js.org


Tutorials

Stack Overflow

Google Group

Further directions

P.S. The presentation is built with stack.js which uses ... d3!

Stack is made by
and is available on GitHub
under the BSD license