d3.js
Data-Driven Documents
Mario Volke
JavaScript Usergroup
Munich
7 March 2012
d3.js: Data-Driven Documents
- JavaScript Library
- Open source
- developed by Michael Bostock and others at Stanford University
- former work on Protovis Library
d3.js: Data-Driven Documents
- not a traditional visualization framework
- focus on efficient manipulation of documents based on data
- bind arbitrary data to the DOM
- apply data-driven transformations to the document
- functional/declarative style
- support for interaction and animation
- you have to do the graphical representation yourself!
- but has helpers for SVG generation
Selections
- operate on arbitrary sets of nodes called selections
- W3C Selectors API or Sizzle fallback
d3.selectAll("p")
.style("color", "white");
Dynamic Properties
- bind data to a selection
- apply functions to styles, attributes and other properties
- a lot of built-in reusable functions available
- bound data available as first argument
- you can omit the data operator the second time!
d3.selectAll("p")
.data([4, 8, 15, 16, 23, 42])
.style("font-size", function(d, i) { return d + "px"; });
Enter and Exit
- enter: selects all data elements without a DOM representation
- exit: selects all DOM elements without data
- common pattern
- perform only necessary modifications
var p = d3.select("body").selectAll("p") // Update…
.data([4, 8, 15, 16, 23, 42])
.text(String);
p.enter().append("p") // Enter…
.text(String);
p.exit().remove(); // Exit…
Subselections
- generate a subselection by calling selectAll on an existing selection
- maintains the hierarchical structure
d3.selectAll("ul")
.data(questions) // an array of questions
.selectAll("li")
// a nested array of responses
.data(function(d) { return d.responses; })
// the text of the response
.text(function(d) { return d.text; });
Transitions
- with d3.js they are easy!
- various tweening functions
- interpolation of basic types, numbers and numbers in strings (e.g. "15px")
// fade background from white to black in 1s
d3.select("body").transition()
.duration(1000)
.style("background-color", "black");
Data Format (CSV)
year,country,survival,children,population
1960,Zimbabwe,0.8458,7.16,3752373
1960,Zambia,0.7904,7.02,3044733
1960,Vietnam,0.90311951,7.09,35172657
1960,Venezuela,0.916,6.62,7562108
1960,Vanuatu,0.8399,7.2,63702
1960,Uruguay,0.9381,2.88,2538095
1960,United States,0.9699,3.59,186326215
Do you speak JavaScript?! ☺
var data_file = "data/ted.csv",
m = [10, 30, 40, 40],
w = jQuery(document.body).width()-m[1]-m[3],
h = jQuery(document.body).height()-m[0]-m[2],
x = d3.scale.linear().range([0, w]),
y = d3.scale.linear().range([h, 0]),
r = d3.scale.linear().range([5, 50]);
// An SVG element with a bottom-right origin.
var svg = d3.select("#slide-ted-demo").append("svg")
.attr("width", w+m[1]+m[3])
.attr("height", h+m[0]+m[2])
.append("g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
var title = svg.append("text")
.attr("class", "title")
.attr("dy", "1em")
.attr("dx", ".5em");
d3.csv(data_file, function(data) {
var year_interval = d3.extent(data,
function(d) { return +d.year; });
x.domain(
[d3.min(data, function(d) { return +d.survival; }), 1]);
y.domain(
[0, d3.max(data, function(d) { return +d.children; })]);
r.domain(
d3.extent(data, function(d) { return +d.population; }));
var x_axis = d3.svg.axis()
.scale(x)
.tickFormat(function(d) {
return Math.floor(d*100) + "%"; });
var y_axis = d3.svg.axis()
.scale(y);
svg.append("g")
.attr("class", "y axis")
.call(y_axis.orient("left"));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(x_axis.orient("bottom"));
data = d3.nest()
.key(function(d) { return d.year; })
.map(data);
setSequence(function(year) {
var circle = svg.selectAll("circle")
.data(data[year],
function(d) { return d.country });
circle.enter().append("circle")
.attr("cx", function(d) { return x(d.survival); })
.attr("cy", function(d) { return y(d.children); })
.attr("r", function(d) { return r(d.population); });
circle.transition()
.duration(500)
.ease("linear")
.attr("cx", function(d) { return x(d.survival); })
.attr("cy", function(d) { return y(d.children); })
.attr("r", function(d) { return r(d.population); });
title.text(year);
}, year_interval, 500);
});
function setSequence(callback, interval, delay) {
var i = interval[0];
global_timer = setInterval(function() {
if(i > interval[1]) {
clearInterval(global_timer);
}
callback(i);
i++;
}, delay);
}