Skip to content

Instantly share code, notes, and snippets.

@AFulgens
Last active August 29, 2015 14:27
Show Gist options
  • Save AFulgens/0b83a2e41391a040464b to your computer and use it in GitHub Desktop.
Save AFulgens/0b83a2e41391a040464b to your computer and use it in GitHub Desktop.
Data Visualization - Coursera - Programming Assignment 2

Programming Assignment 2, for the Coursera course Data Visualization.

It uses a radial layout with hierarchical edge bundling in D3, showing character co-appearances in the Les Miserables novel. With the radio buttons the data is ordered in three different ways: the original ordering from the dataset, alphabetical based on the characters' names, and based on their weight (i.e. how many co-appearances they have, which translates to the degree of the nodes).

The code was shamelessly copied from Mike Bostock's Hierarchical Edge Bundling demo, and modified wherever needed.

Sadly, interactive re-layouting did not work in the couple hours I spent with this assignment, thus a static page-reload is done for re-ordering.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
font: 300 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
fill: #bbb;
}
.node:hover {
fill: #000;
}
.link {
stroke: steelblue;
stroke-opacity: .4;
fill: none;
pointer-events: none;
}
.node:hover,
.node--source,
.node--target {
font-weight: 700;
}
.node--source {
fill: #2ca02c;
}
.node--target {
fill: #d62728;
}
.link--source,
.link--target {
stroke-opacity: 1;
stroke-width: 2px;
}
.link--source {
stroke: #d62728;
}
.link--target {
stroke: #2ca02c;
}
form {
position: absolute;
top: 1em;
left: 1em;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<form>
<input type="radio" name="reference" class="selectUrl" onclick="window.top.location='http://bl.ocks.org/afulgens/0b83a2e41391a040464b'">Original<br />
<input type="radio" name="reference" class="selectUrl" onclick="window.top.location='http://bl.ocks.org/afulgens/0b83a2e41391a040464b?sort=alphabetical'">Alphabetical<br />
<input type="radio" name="reference" class="selectUrl" onclick="window.top.location='http://bl.ocks.org/afulgens/0b83a2e41391a040464b?sort=weight'">Weight<br />
</form>
<script>
var vars = [], hash;
var hashes = window.top.location.href.slice(window.top.location.href.indexOf('?') + 1).split('&');
for(var i = 0; i < hashes.length; i++)
{
hash = hashes[i].split('=');
vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
var diameter = 960,
radius = diameter / 2,
innerRadius = radius - 120;
var cluster;
if (vars["sort"] == "alphabetical") {
cluster = d3.layout.cluster()
.size([360, innerRadius])
.sort(function(a,b) { return d3.ascending(a.key, b.key); })
.value(function(d) { return d.size; });
} else if (vars["sort"] == "weight") {
cluster = d3.layout.cluster()
.size([360, innerRadius])
.sort(function(a,b) { return d3.ascending(a.weight, b.weight); })
.value(function(d) { return d.size; });
} else {
cluster = d3.layout.cluster()
.size([360, innerRadius])
.sort(null)
.value(function(d) { return d.size; });
}
var bundle = d3.layout.bundle();
var line = d3.svg.line.radial()
.interpolate("bundle")
.tension(.5)
.radius(function(d) { return d.y; })
.angle(function(d) { return d.x / 180 * Math.PI; });
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
var link = svg.append("g").selectAll(".link"),
node = svg.append("g").selectAll(".node");
d3.json("les-miserables.json", function(error, chars) {
if (error) throw error;
var nodes = cluster.nodes(characters(chars)),
links = coapp(nodes, chars);
link = link
.data(bundle(links))
.enter().append("path")
.each(function(d) { d.source = d[0], d.target = d[d.length - 1]; })
.attr("class", "link")
.attr("d", line);
node = node
.data(nodes.filter(function(n) { return !n.children; }))
.enter().append("text")
.attr("class", "node")
.attr("dy", ".31em")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + (d.y + 8) + ",0)" + (d.x < 180 ? "" : "rotate(180)"); })
.style("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.text(function(d) { return d.key; })
.on("mouseover", mouseovered)
.on("mouseout", mouseouted);
});
function mouseovered(d) {
node
.each(function(n) { n.target = n.source = false; });
link
.classed("link--target", function(l) { if (l.target === d) return l.source.source = true; })
.classed("link--source", function(l) { if (l.source === d) return l.target.target = true; })
.filter(function(l) { return l.target === d || l.source === d; })
.each(function() { this.parentNode.appendChild(this); });
node
.classed("node--target", function(n) { return n.target; })
.classed("node--source", function(n) { return n.source; });
}
function mouseouted(d) {
link
.classed("link--target", false)
.classed("link--source", false);
node
.classed("node--target", false)
.classed("node--source", false);
}
d3.select(self.frameElement).style("height", diameter + "px");
// Lazily construct the character nodes.
function characters(c) {
var map = {};
function find(id, data, label, weight) {
var node = map[id], i;
if (!node) {
node = map[id] = data || {id: id, children: []};
if (id.length) {
node.parent = find(id.substring(0, i = id.lastIndexOf(".")));
node.parent.children.push(node);
node.key = label;
node.weight = weight;
}
}
return node;
}
function getWeight(id, d) {
weight = 0;
d["edges"].forEach(function(e) {
if(e.source == id || e.target == id) {
weight += e.value / 2;
}
});
return weight;
}
c["nodes"].forEach(function(d) {
find(d.id, d, d.label, getWeight(d.id, c));
});
return map[""];
}
// Return a list of coappearances for the given array of nodes.
function coapp(nodes, data) {
var map = {},
coapps = [];
// Compute a map from id to node.
nodes.forEach(function(d) {
if (d.id) {
map[d.id] = d;
}
});
// For each coapp, construct a link from the source to target node.
data["edges"].forEach(function(d) {
for (i = 0; i < d.value; ++i) {
coapps.push({source: map[d.source], target: map[d.target]});
}
});
return coapps;
}
</script>
{
"creator" : "Creator Mark Newman on Fri Jul 21 12:44:53 2006",
"updated_by" : "Daniel Kovacs for Data Visualization Coursera Course @ 2015-08-08",
"data_description" : "Coappearance network in the novel Les Miserables, see https://networkdata.ics.uci.edu/data.php?id=109",
"nodes" : [
{"id" : "0","label" : "Myriel"},
{"id" : "1","label" : "Napoleon"},
{"id" : "2","label" : "MlleBaptistine"},
{"id" : "3","label" : "MmeMagloire"},
{"id" : "4","label" : "CountessDeLo"},
{"id" : "5","label" : "Geborand"},
{"id" : "6","label" : "Champtercier"},
{"id" : "7","label" : "Cravatte"},
{"id" : "8","label" : "Count"},
{"id" : "9","label" : "OldMan"},
{"id" : "10","label" : "Labarre"},
{"id" : "11","label" : "Valjean"},
{"id" : "12","label" : "Marguerite"},
{"id" : "13","label" : "MmeDeR"},
{"id" : "14","label" : "Isabeau"},
{"id" : "15","label" : "Gervais"},
{"id" : "16","label" : "Tholomyes"},
{"id" : "17","label" : "Listolier"},
{"id" : "18","label" : "Fameuil"},
{"id" : "19","label" : "Blacheville"},
{"id" : "20","label" : "Favourite"},
{"id" : "21","label" : "Dahlia"},
{"id" : "22","label" : "Zephine"},
{"id" : "23","label" : "Fantine"},
{"id" : "24","label" : "MmeThenardier"},
{"id" : "25","label" : "Thenardier"},
{"id" : "26","label" : "Cosette"},
{"id" : "27","label" : "Javert"},
{"id" : "28","label" : "Fauchelevent"},
{"id" : "29","label" : "Bamatabois"},
{"id" : "30","label" : "Perpetue"},
{"id" : "31","label" : "Simplice"},
{"id" : "32","label" : "Scaufflaire"},
{"id" : "33","label" : "Woman1"},
{"id" : "34","label" : "Judge"},
{"id" : "35","label" : "Champmathieu"},
{"id" : "36","label" : "Brevet"},
{"id" : "37","label" : "Chenildieu"},
{"id" : "38","label" : "Cochepaille"},
{"id" : "39","label" : "Pontmercy"},
{"id" : "40","label" : "Boulatruelle"},
{"id" : "41","label" : "Eponine"},
{"id" : "42","label" : "Anzelma"},
{"id" : "43","label" : "Woman2"},
{"id" : "44","label" : "MotherInnocent"},
{"id" : "45","label" : "Gribier"},
{"id" : "46","label" : "Jondrette"},
{"id" : "47","label" : "MmeBurgon"},
{"id" : "48","label" : "Gavroche"},
{"id" : "49","label" : "Gillenormand"},
{"id" : "50","label" : "Magnon"},
{"id" : "51","label" : "MlleGillenormand"},
{"id" : "52","label" : "MmePontmercy"},
{"id" : "53","label" : "MlleVaubois"},
{"id" : "54","label" : "LtGillenormand"},
{"id" : "55","label" : "Marius"},
{"id" : "56","label" : "BaronessT"},
{"id" : "57","label" : "Mabeuf"},
{"id" : "58","label" : "Enjolras"},
{"id" : "59","label" : "Combeferre"},
{"id" : "60","label" : "Prouvaire"},
{"id" : "61","label" : "Feuilly"},
{"id" : "62","label" : "Courfeyrac"},
{"id" : "63","label" : "Bahorel"},
{"id" : "64","label" : "Bossuet"},
{"id" : "65","label" : "Joly"},
{"id" : "66","label" : "Grantaire"},
{"id" : "67","label" : "MotherPlutarch"},
{"id" : "68","label" : "Gueulemer"},
{"id" : "69","label" : "Babet"},
{"id" : "70","label" : "Claquesous"},
{"id" : "71","label" : "Montparnasse"},
{"id" : "72","label" : "Toussaint"},
{"id" : "73","label" : "Child1"},
{"id" : "74","label" : "Child2"},
{"id" : "75","label" : "Brujon"},
{"id" : "76","label" : "MmeHucheloup"}
],
"edges" : [
{"source" : "1","target" : "0","value" : "1"},
{"source" : "2","target" : "0","value" : "8"},
{"source" : "3","target" : "0","value" : "10"},
{"source" : "3","target" : "2","value" : "6"},
{"source" : "4","target" : "0","value" : "1"},
{"source" : "5","target" : "0","value" : "1"},
{"source" : "6","target" : "0","value" : "1"},
{"source" : "7","target" : "0","value" : "1"},
{"source" : "8","target" : "0","value" : "2"},
{"source" : "9","target" : "0","value" : "1"},
{"source" : "11","target" : "10","value" : "1"},
{"source" : "11","target" : "3","value" : "3"},
{"source" : "11","target" : "2","value" : "3"},
{"source" : "11","target" : "0","value" : "5"},
{"source" : "12","target" : "11","value" : "1"},
{"source" : "13","target" : "11","value" : "1"},
{"source" : "14","target" : "11","value" : "1"},
{"source" : "15","target" : "11","value" : "1"},
{"source" : "17","target" : "16","value" : "4"},
{"source" : "18","target" : "16","value" : "4"},
{"source" : "18","target" : "17","value" : "4"},
{"source" : "19","target" : "16","value" : "4"},
{"source" : "19","target" : "17","value" : "4"},
{"source" : "19","target" : "18","value" : "4"},
{"source" : "20","target" : "16","value" : "3"},
{"source" : "20","target" : "17","value" : "3"},
{"source" : "20","target" : "18","value" : "3"},
{"source" : "20","target" : "19","value" : "4"},
{"source" : "21","target" : "16","value" : "3"},
{"source" : "21","target" : "17","value" : "3"},
{"source" : "21","target" : "18","value" : "3"},
{"source" : "21","target" : "19","value" : "3"},
{"source" : "21","target" : "20","value" : "5"},
{"source" : "22","target" : "16","value" : "3"},
{"source" : "22","target" : "17","value" : "3"},
{"source" : "22","target" : "18","value" : "3"},
{"source" : "22","target" : "19","value" : "3"},
{"source" : "22","target" : "20","value" : "4"},
{"source" : "22","target" : "21","value" : "4"},
{"source" : "23","target" : "16","value" : "3"},
{"source" : "23","target" : "17","value" : "3"},
{"source" : "23","target" : "18","value" : "3"},
{"source" : "23","target" : "19","value" : "3"},
{"source" : "23","target" : "20","value" : "4"},
{"source" : "23","target" : "21","value" : "4"},
{"source" : "23","target" : "22","value" : "4"},
{"source" : "23","target" : "12","value" : "2"},
{"source" : "23","target" : "11","value" : "9"},
{"source" : "24","target" : "23","value" : "2"},
{"source" : "24","target" : "11","value" : "7"},
{"source" : "25","target" : "24","value" : "13"},
{"source" : "25","target" : "23","value" : "1"},
{"source" : "25","target" : "11","value" : "12"},
{"source" : "26","target" : "24","value" : "4"},
{"source" : "26","target" : "11","value" : "31"},
{"source" : "26","target" : "16","value" : "1"},
{"source" : "26","target" : "25","value" : "1"},
{"source" : "27","target" : "11","value" : "17"},
{"source" : "27","target" : "23","value" : "5"},
{"source" : "27","target" : "25","value" : "5"},
{"source" : "27","target" : "24","value" : "1"},
{"source" : "27","target" : "26","value" : "1"},
{"source" : "28","target" : "11","value" : "8"},
{"source" : "28","target" : "27","value" : "1"},
{"source" : "29","target" : "23","value" : "1"},
{"source" : "29","target" : "27","value" : "1"},
{"source" : "29","target" : "11","value" : "2"},
{"source" : "30","target" : "23","value" : "1"},
{"source" : "31","target" : "30","value" : "2"},
{"source" : "31","target" : "11","value" : "3"},
{"source" : "31","target" : "23","value" : "2"},
{"source" : "31","target" : "27","value" : "1"},
{"source" : "32","target" : "11","value" : "1"},
{"source" : "33","target" : "11","value" : "2"},
{"source" : "33","target" : "27","value" : "1"},
{"source" : "34","target" : "11","value" : "3"},
{"source" : "34","target" : "29","value" : "2"},
{"source" : "35","target" : "11","value" : "3"},
{"source" : "35","target" : "34","value" : "3"},
{"source" : "35","target" : "29","value" : "2"},
{"source" : "36","target" : "34","value" : "2"},
{"source" : "36","target" : "35","value" : "2"},
{"source" : "36","target" : "11","value" : "2"},
{"source" : "36","target" : "29","value" : "1"},
{"source" : "37","target" : "34","value" : "2"},
{"source" : "37","target" : "35","value" : "2"},
{"source" : "37","target" : "36","value" : "2"},
{"source" : "37","target" : "11","value" : "2"},
{"source" : "37","target" : "29","value" : "1"},
{"source" : "38","target" : "34","value" : "2"},
{"source" : "38","target" : "35","value" : "2"},
{"source" : "38","target" : "36","value" : "2"},
{"source" : "38","target" : "37","value" : "2"},
{"source" : "38","target" : "11","value" : "2"},
{"source" : "38","target" : "29","value" : "1"},
{"source" : "39","target" : "25","value" : "1"},
{"source" : "40","target" : "25","value" : "1"},
{"source" : "41","target" : "24","value" : "2"},
{"source" : "41","target" : "25","value" : "3"},
{"source" : "42","target" : "41","value" : "2"},
{"source" : "42","target" : "25","value" : "2"},
{"source" : "42","target" : "24","value" : "1"},
{"source" : "43","target" : "11","value" : "3"},
{"source" : "43","target" : "26","value" : "1"},
{"source" : "43","target" : "27","value" : "1"},
{"source" : "44","target" : "28","value" : "3"},
{"source" : "44","target" : "11","value" : "1"},
{"source" : "45","target" : "28","value" : "2"},
{"source" : "47","target" : "46","value" : "1"},
{"source" : "48","target" : "47","value" : "2"},
{"source" : "48","target" : "25","value" : "1"},
{"source" : "48","target" : "27","value" : "1"},
{"source" : "48","target" : "11","value" : "1"},
{"source" : "49","target" : "26","value" : "3"},
{"source" : "49","target" : "11","value" : "2"},
{"source" : "50","target" : "49","value" : "1"},
{"source" : "50","target" : "24","value" : "1"},
{"source" : "51","target" : "49","value" : "9"},
{"source" : "51","target" : "26","value" : "2"},
{"source" : "51","target" : "11","value" : "2"},
{"source" : "52","target" : "51","value" : "1"},
{"source" : "52","target" : "39","value" : "1"},
{"source" : "53","target" : "51","value" : "1"},
{"source" : "54","target" : "51","value" : "2"},
{"source" : "54","target" : "49","value" : "1"},
{"source" : "54","target" : "26","value" : "1"},
{"source" : "55","target" : "51","value" : "6"},
{"source" : "55","target" : "49","value" : "12"},
{"source" : "55","target" : "39","value" : "1"},
{"source" : "55","target" : "54","value" : "1"},
{"source" : "55","target" : "26","value" : "21"},
{"source" : "55","target" : "11","value" : "19"},
{"source" : "55","target" : "16","value" : "1"},
{"source" : "55","target" : "25","value" : "2"},
{"source" : "55","target" : "41","value" : "5"},
{"source" : "55","target" : "48","value" : "4"},
{"source" : "56","target" : "49","value" : "1"},
{"source" : "56","target" : "55","value" : "1"},
{"source" : "57","target" : "55","value" : "1"},
{"source" : "57","target" : "41","value" : "1"},
{"source" : "57","target" : "48","value" : "1"},
{"source" : "58","target" : "55","value" : "7"},
{"source" : "58","target" : "48","value" : "7"},
{"source" : "58","target" : "27","value" : "6"},
{"source" : "58","target" : "57","value" : "1"},
{"source" : "58","target" : "11","value" : "4"},
{"source" : "59","target" : "58","value" : "15"},
{"source" : "59","target" : "55","value" : "5"},
{"source" : "59","target" : "48","value" : "6"},
{"source" : "59","target" : "57","value" : "2"},
{"source" : "60","target" : "48","value" : "1"},
{"source" : "60","target" : "58","value" : "4"},
{"source" : "60","target" : "59","value" : "2"},
{"source" : "61","target" : "48","value" : "2"},
{"source" : "61","target" : "58","value" : "6"},
{"source" : "61","target" : "60","value" : "2"},
{"source" : "61","target" : "59","value" : "5"},
{"source" : "61","target" : "57","value" : "1"},
{"source" : "61","target" : "55","value" : "1"},
{"source" : "62","target" : "55","value" : "9"},
{"source" : "62","target" : "58","value" : "17"},
{"source" : "62","target" : "59","value" : "13"},
{"source" : "62","target" : "48","value" : "7"},
{"source" : "62","target" : "57","value" : "2"},
{"source" : "62","target" : "41","value" : "1"},
{"source" : "62","target" : "61","value" : "6"},
{"source" : "62","target" : "60","value" : "3"},
{"source" : "63","target" : "59","value" : "5"},
{"source" : "63","target" : "48","value" : "5"},
{"source" : "63","target" : "62","value" : "6"},
{"source" : "63","target" : "57","value" : "2"},
{"source" : "63","target" : "58","value" : "4"},
{"source" : "63","target" : "61","value" : "3"},
{"source" : "63","target" : "60","value" : "2"},
{"source" : "63","target" : "55","value" : "1"},
{"source" : "64","target" : "55","value" : "5"},
{"source" : "64","target" : "62","value" : "12"},
{"source" : "64","target" : "48","value" : "5"},
{"source" : "64","target" : "63","value" : "4"},
{"source" : "64","target" : "58","value" : "10"},
{"source" : "64","target" : "61","value" : "6"},
{"source" : "64","target" : "60","value" : "2"},
{"source" : "64","target" : "59","value" : "9"},
{"source" : "64","target" : "57","value" : "1"},
{"source" : "64","target" : "11","value" : "1"},
{"source" : "65","target" : "63","value" : "5"},
{"source" : "65","target" : "64","value" : "7"},
{"source" : "65","target" : "48","value" : "3"},
{"source" : "65","target" : "62","value" : "5"},
{"source" : "65","target" : "58","value" : "5"},
{"source" : "65","target" : "61","value" : "5"},
{"source" : "65","target" : "60","value" : "2"},
{"source" : "65","target" : "59","value" : "5"},
{"source" : "65","target" : "57","value" : "1"},
{"source" : "65","target" : "55","value" : "2"},
{"source" : "66","target" : "64","value" : "3"},
{"source" : "66","target" : "58","value" : "3"},
{"source" : "66","target" : "59","value" : "1"},
{"source" : "66","target" : "62","value" : "2"},
{"source" : "66","target" : "65","value" : "2"},
{"source" : "66","target" : "48","value" : "1"},
{"source" : "66","target" : "63","value" : "1"},
{"source" : "66","target" : "61","value" : "1"},
{"source" : "66","target" : "60","value" : "1"},
{"source" : "67","target" : "57","value" : "3"},
{"source" : "68","target" : "25","value" : "5"},
{"source" : "68","target" : "11","value" : "1"},
{"source" : "68","target" : "24","value" : "1"},
{"source" : "68","target" : "27","value" : "1"},
{"source" : "68","target" : "48","value" : "1"},
{"source" : "68","target" : "41","value" : "1"},
{"source" : "69","target" : "25","value" : "6"},
{"source" : "69","target" : "68","value" : "6"},
{"source" : "69","target" : "11","value" : "1"},
{"source" : "69","target" : "24","value" : "1"},
{"source" : "69","target" : "27","value" : "2"},
{"source" : "69","target" : "48","value" : "1"},
{"source" : "69","target" : "41","value" : "1"},
{"source" : "70","target" : "25","value" : "4"},
{"source" : "70","target" : "69","value" : "4"},
{"source" : "70","target" : "68","value" : "4"},
{"source" : "70","target" : "11","value" : "1"},
{"source" : "70","target" : "24","value" : "1"},
{"source" : "70","target" : "27","value" : "1"},
{"source" : "70","target" : "41","value" : "1"},
{"source" : "70","target" : "58","value" : "1"},
{"source" : "71","target" : "27","value" : "1"},
{"source" : "71","target" : "69","value" : "2"},
{"source" : "71","target" : "68","value" : "2"},
{"source" : "71","target" : "70","value" : "2"},
{"source" : "71","target" : "11","value" : "1"},
{"source" : "71","target" : "48","value" : "1"},
{"source" : "71","target" : "41","value" : "1"},
{"source" : "71","target" : "25","value" : "1"},
{"source" : "72","target" : "26","value" : "2"},
{"source" : "72","target" : "27","value" : "1"},
{"source" : "72","target" : "11","value" : "1"},
{"source" : "73","target" : "48","value" : "2"},
{"source" : "74","target" : "48","value" : "2"},
{"source" : "74","target" : "73","value" : "3"},
{"source" : "75","target" : "69","value" : "3"},
{"source" : "75","target" : "68","value" : "3"},
{"source" : "75","target" : "25","value" : "3"},
{"source" : "75","target" : "48","value" : "1"},
{"source" : "75","target" : "41","value" : "1"},
{"source" : "75","target" : "70","value" : "1"},
{"source" : "75","target" : "71","value" : "1"},
{"source" : "76","target" : "64","value" : "1"},
{"source" : "76","target" : "65","value" : "1"},
{"source" : "76","target" : "66","value" : "1"},
{"source" : "76","target" : "63","value" : "1"},
{"source" : "76","target" : "62","value" : "1"},
{"source" : "76","target" : "48","value" : "1"},
{"source" : "76","target" : "58","value" : "1"}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment