Redesigning the Economists Lottery of Life table.
Countries are organized along the x-axis according to their rank and colored based on continent. Hover over the legend to highlight all entries for a continent, or hover over a point or a label to highlight that one entry.
xxxxxxxxxx
<html>
<head>
<title>The Lottery of Life</title>
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 10px;
color: #333;
}
text {
fill: #333;
}
.axis .domain {
fill: none;
}
.title {
text-anchor: middle;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var fadeTo = 0.3;
var margin = {top: 10, right: 5, bottom: 120, left: 50};
var width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select('body').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'),
legend = svg.append('g');
svg.append('g').attr('class', 'y axis')
.attr('transform', 'translate(-5,0)')
.append('text')
.attr('class', 'title')
.attr('transform', 'translate(' + (-margin.left) + ',' + (height/2) + ')rotate(-90)')
.attr('dy', '1.4em')
.text('Birth Rate (per 1,000 people)');
svg.append('g').attr('class', 'x axis')
.attr('transform', 'translate(0,' + (height + 15) + ')')
.append('text')
.attr('class', 'title')
.attr('transform', 'translate(' + (width/2) + ',' + margin.bottom + ')')
.attr('dy', '-2.4em')
.text('Rank');
var x = d3.scale.linear().domain([0, 10]).range([0, width]),
y = d3.scale.linear().range([height, 0]),
yAxis = d3.svg.axis().scale(y).orient('left'),
color = d3.scale.ordinal().range(['#8DD3C7', '#FDB462', '#BEBADA', '#FB8072', '#80B1D3', '#B3DE69']);
var birthRate = d3.format('.2f'),
population = d3.format('.2s');
d3.json('data.json', update);
function update(data) {
var yExtent = [0, Number.MIN_VALUE],
continents = [];
data = data.filter(function (d) { return !!d.births; });
data.forEach(function (d) {
if (continents.indexOf(d.continent) < 0) {
continents.push(d.continent);
}
if (d.births) {
var births = d3.map();
d.births.forEach(function (b) {
births.set(b.year, b.value);
yExtent[1] = Math.max(yExtent[1], b.value);
});
d.births = births;
}
});
continents = continents.sort(d3.ascending);
color.domain(continents);
x.domain(d3.extent(data, function (d) { return d.rank; }));
y.domain(yExtent);
var plot = svg.selectAll('.point')
.data(data, function (d) { return d['country code']; });
plot.transition().duration(750)
.attr('cx', function (d) { return x(d.rank); })
.attr('cy', function (d) { return y(d.births.get(2010)); });
plot.enter().append('circle').attr('class', 'point')
.attr('cx', function (d) { return x(d.rank); })
.attr('cy', function (d) { return y(d.births.get(2010)); })
.attr('r', 4)
.style('fill', function (d) { return color(d.continent); })
.style('opacity', 0)
.on('mouseover', hover)
.on('mouseout', unhover)
.transition().duration(500)
.delay(function (d, i) { return d.rank * 150; })
.style('opacity', 1);
plot.exit().remove();
var labelData = d3.map();
data.forEach(function (d) {
if (!labelData.has(d.rank)) {
labelData.set(d.rank, []);
}
labelData.get(d.rank).push(d);
});
var rank = svg.selectAll('.x.axis').selectAll('.label')
.data(labelData.values().sort(function (a, b) { return d3.ascending(a[0].rank, b[0].rank); }), function (d) { return d.country; });
rank.enter().append('g').attr('class', 'label')
.style('opacity', 0)
.transition().duration(500)
.delay(function (d, i) { return d[0].rank * 150; })
.style('opacity', 1);
rank.attr('transform', function (d) { return 'translate(' + x(d[0].rank) + ',0)rotate(-45)'; })
.call(function (selection) {
selection.each(function (d, i) {
var label = d3.select(this).selectAll('text').data(d);
label.enter().append('text')
.attr('class', 'label')
.style('text-anchor', 'end')
.on('mouseover', hover)
.on('mouseout', unhover);
label.text(function (d) { return d.country; })
.attr('transform', function (d, i) {
var offset = 0, n = i;
while (n-- > 0) {
offset -= label[n][0].getBBox().width + 10;
}
return 'translate(' + offset + ',0)';
});
});
});
rank.exit().remove();
svg.selectAll('.y.axis').call(yAxis);
var l = legend.selectAll('.legend').data(continents);
var lEnter = l.enter().append('g').attr('class', 'legend');
lEnter.append('rect')
.attr('x', -10)
.attr('y', -10)
.attr('width', 12)
.attr('height', 12);
lEnter.append('text')
.attr('transform', 'translate(8,0)');
l.selectAll('rect')
.style('fill', function (d) { return color(d); });
l.selectAll('text').text(String);
l.attr('transform', function (d, i) { return 'translate(10,' + (i * 14) + ')'; })
.on('mouseover', legendHover)
.on('mouseout', unhover);
}
function hover(d) {
svg.selectAll('.x.axis text.label')
.transition().duration(500)
.style('opacity', function (q) {
return d.country === q.country ? 1 : fadeTo;
});
svg.selectAll('.point')
.transition().duration(500)
.style('opacity', function (q) {
return d.country === q.country ? 1: fadeTo;
});
}
function legendHover(d) {
svg.selectAll('.point')
.transition().duration(500)
.style('opacity', function (q) {
return d === q.continent ? 1 : fadeTo;
});
svg.selectAll('.x.axis text.label')
.transition().duration(500)
.style('opacity', function (q) {
return d === q.continent ? 1 : fadeTo;
});
}
function unhover(d) {
svg.selectAll('.point, .x.axis text.label')
.transition().duration(500)
.style('opacity', 1);
}
</script>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js