Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active January 12, 2020 03:40
  • Star 8 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save mbostock/1308257 to your computer and use it in GitHub Desktop.
The Euro Debt Crisis
license: gpl-3.0

Bill Marsh created a nice visual guide to the Euro debt crisis in today’s New York Times using a custom graph layout with directed edges. An alternative approach to visualizing asymmetric relationships among a group of entities is a chord diagram. Chord diagrams don’t show indirect relationships as well, but they better convey the magnitudes of the debt for each country.

This visualization uses two chord diagrams. The diagram on the left emphasizes how much each country lends (creditors); you can see Japan and France are the biggest lenders, but France is also the most exposed by its risky debts to Italy and Greece. The diagram on the right emphasizes how much each country owes (debtors); the United States has by far the largest debt, nearly three times the size of the next-largest debtor, Britain!

Built with D3.js.

creditor debtor amount risk
Britain France 22.4 3
Britain Greece 0.55 0
Britain Italy 26 0
Britain Portugal 19.4 0
Britain United States 345 1
France Germany 53.8 1
France Greece 53.9 0
France Ireland 17.3 0
France Italy 366 0
France Japan 7.73 1
France Portugal 18.3 0
France Spain 118 2
France United States 322 1
Germany Britain 321 1
Germany Greece 19.3 0
Germany Ireland 48.9 0
Germany Portugal 32.5 0
Germany Spain 57.6 2
Germany United States 324 1
Ireland Britain 12 1
Ireland Greece 0.34 0
Ireland Spain 6.38 2
Italy Germany 111 1
Italy Greece 3.22 0
Italy Ireland 2.83 0
Italy Portugal 0.87 0
Japan Britain 28.2 1
Japan Germany 88.5 1
Japan Greece 1.37 0
Japan Ireland 18.9 0
Japan Italy 38.8 0
Japan Portugal 2.18 0
Japan Spain 25.9 2
Japan United States 796 1
Portugal Greece 10.1 0
Portugal Ireland 3.77 0
Portugal United States 0.52 1
Spain Britain 326 1
Spain Greece 0.78 0
Spain Italy 9.79 0
Spain Portugal 62 0
Spain United States 163 1
United States Greece 3.1 0
United States Ireland 11.1 0
United States Italy 3.16 0
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.group text {
font: 11px sans-serif;
pointer-events: none;
}
.group path {
stroke: #000;
}
path.chord {
stroke-width: .75;
fill-opacity: .75;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var width = 480,
height = 500,
outerRadius = Math.min(width, height) / 2 - 4,
innerRadius = outerRadius - 20;
var format = d3.format(",.3r");
// Square matrices, asynchronously loaded; credits is the transpose of debits.
var debits = [],
credits = [];
// The chord layout, for computing the angles of chords and groups.
var layout = d3.layout.chord()
.sortGroups(d3.descending)
.sortSubgroups(d3.descending)
.sortChords(d3.descending)
.padding(.04);
// The color scale, for different categories of “worrisome” risk.
var fill = d3.scale.ordinal()
.domain([0, 1, 2])
.range(["#DB704D", "#D2D0C6", "#ECD08D", "#F8EDD3"]);
// The arc generator, for the groups.
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
// The chord generator (quadratic Bézier), for the chords.
var chord = d3.svg.chord()
.radius(innerRadius);
// Add an SVG element for each diagram, and translate the origin to the center.
var svg = d3.select("body").selectAll("div")
.data([debits, credits])
.enter().append("div")
.style("display", "inline-block")
.style("width", width + "px")
.style("height", height + "px")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Load our data file…
d3.csv("debt.csv", type, function(error, data) {
if (error) throw error;
var countryByName = d3.map(),
countryIndex = -1,
countryByIndex = [];
// Compute a unique index for each country.
data.forEach(function(d) {
if (countryByName.has(d.creditor)) d.creditor = countryByName.get(d.creditor);
else countryByName.set(d.creditor, d.creditor = {name: d.creditor, index: ++countryIndex});
if (countryByName.has(d.debtor)) d.debtor = countryByName.get(d.debtor);
else countryByName.set(d.debtor, d.debtor = {name: d.debtor, index: ++countryIndex});
d.debtor.risk = d.risk;
});
// Initialize a square matrix of debits and credits.
for (var i = 0; i <= countryIndex; i++) {
debits[i] = [];
credits[i] = [];
for (var j = 0; j <= countryIndex; j++) {
debits[i][j] = 0;
credits[i][j] = 0;
}
}
// Populate the matrices, and stash a map from index to country.
data.forEach(function(d) {
debits[d.creditor.index][d.debtor.index] = d;
credits[d.debtor.index][d.creditor.index] = d;
countryByIndex[d.creditor.index] = d.creditor;
countryByIndex[d.debtor.index] = d.debtor;
});
// For each diagram…
svg.each(function(matrix, j) {
var svg = d3.select(this);
// Compute the chord layout.
layout.matrix(matrix);
// Add chords.
svg.selectAll(".chord")
.data(layout.chords)
.enter().append("path")
.attr("class", "chord")
.style("fill", function(d) { return fill(d.source.value.risk); })
.style("stroke", function(d) { return d3.rgb(fill(d.source.value.risk)).darker(); })
.attr("d", chord)
.append("title")
.text(function(d) { return d.source.value.debtor.name + " owes " + d.source.value.creditor.name + " $" + format(d.source.value) + "B."; });
// Add groups.
var g = svg.selectAll(".group")
.data(layout.groups)
.enter().append("g")
.attr("class", "group");
// Add the group arc.
g.append("path")
.style("fill", function(d) { return fill(countryByIndex[d.index].risk); })
.attr("id", function(d, i) { return "group" + d.index + "-" + j; })
.attr("d", arc)
.append("title")
.text(function(d) { return countryByIndex[d.index].name + " " + (j ? "owes" : "is owed") + " $" + format(d.value) + "B."; });
// Add the group label (but only for large groups, where it will fit).
// An alternative labeling mechanism would be nice for the small groups.
g.append("text")
.attr("x", 6)
.attr("dy", 15)
.filter(function(d) { return d.value > 110; })
.append("textPath")
.attr("xlink:href", function(d) { return "#group" + d.index + "-" + j; })
.text(function(d) { return countryByIndex[d.index].name; });
});
});
function type(d) {
d.amount = +d.amount;
d.risk = +d.risk;
d.valueOf = value; // for chord layout
return d;
}
function value() {
return this.amount;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment