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!
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
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
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
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
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
JSON
CSV
TSV
XML
Text
Loading Data
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
d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height])
.nodes(json.nodes)
.links(json.links)
.start();
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; });
});
The Wealth & Health of Nation
P.S. The presentation is built with stack.js which uses ... d3 !