D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
Lichtphyz
Full window
Github gist
Game of Thrones Matrix
Built with
blockbuilder.org
<!DOCTYPE html> <html class="ocks-org do-not-copy"> <meta charset="utf-8"> <title>Game of Thrones Co-occurrence</title> <style> @import url(https://bost.ocks.org/mike/style.css?aea6f0a); .background { fill: #eee; } line { stroke: #fff; } text.active { fill: red; } </style> <script src="//d3js.org/d3.v2.min.js" charset="utf-8"></script> <header> <aside>August 18, 2017</aside> <a href="../" rel="author">Daniel Licht, d3.js template by Mike Bostock</a> </header> <h1><i>Game of Thrones</i> Co-occurrence</h1> <aside style="margin-top:80px;"> <p>Order: <select id="order"> <option value="name">by Name</option> <option value="count">by Frequency</option> <option value="group">by Cluster</option> </select> <p>This matrix diagram visualizes character co-occurrences in George R.R. Martin’s Series "A Song of Ice and Fire", books 1-5, commonly called "Game of Thrones"</a></i>. <p>Each colored cell represents two characters that appeared within n sentences of eachother; darker cells indicate characters that co-occurred more frequently. <p>Use the drop-down menu to reorder the matrix and explore the data. <p>Built with <a href="https://d3js.org/">d3.js</a>. </aside> <script> var margin = {top: 80, right: 0, bottom: 10, left: 80}, width = 720, height = 720; var x = d3.scale.ordinal().rangeBands([0, width]), z = d3.scale.linear().domain([0,1000]).clamp(true), c = d3.scale.category10().domain(d3.range(10)); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .style("margin-left", -margin.left + "px") .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); d3.json("GoT_interactions_matrix_draft.json", function(miserables) { var matrix = [], nodes = miserables.nodes, n = nodes.length; // Compute index per node. nodes.forEach(function(node, i) { node.index = i; node.count = 0; matrix[i] = d3.range(n).map(function(j) { return {x: j, y: i, z: 0}; }); }); // Convert links to matrix; count character occurrences. miserables.links.forEach(function(link) { matrix[link.source][link.target].z += link.value; matrix[link.target][link.source].z += link.value; matrix[link.source][link.source].z += link.value; matrix[link.target][link.target].z += link.value; nodes[link.source].count += link.value; nodes[link.target].count += link.value; }); // Precompute the orders. var orders = { name: d3.range(n).sort(function(a, b) { return d3.ascending(nodes[a].name, nodes[b].name); }), count: d3.range(n).sort(function(a, b) { return nodes[b].count - nodes[a].count; }), group: d3.range(n).sort(function(a, b) { return nodes[b].group - nodes[a].group; }) }; // The default sort order. x.domain(orders.name); svg.append("rect") .attr("class", "background") .attr("width", width) .attr("height", height); var row = svg.selectAll(".row") .data(matrix) .enter().append("g") .attr("class", "row") .attr("transform", function(d, i) { return "translate(0," + x(i) + ")"; }) .each(row); row.append("line") .attr("x2", width); row.append("text") .attr("x", -6) .attr("y", x.rangeBand() / 2) .attr("dy", ".32em") .attr("text-anchor", "end") .text(function(d, i) { return nodes[i].name; }); var column = svg.selectAll(".column") .data(matrix) .enter().append("g") .attr("class", "column") .attr("transform", function(d, i) { return "translate(" + x(i) + ")rotate(-90)"; }); column.append("line") .attr("x1", -width); column.append("text") .attr("x", 6) .attr("y", x.rangeBand() / 2) .attr("dy", ".32em") .attr("text-anchor", "start") .text(function(d, i) { return nodes[i].name; }); function row(row) { var cell = d3.select(this).selectAll(".cell") .data(row.filter(function(d) { return d.z; })) .enter().append("rect") .attr("class", "cell") .attr("x", function(d) { return x(d.x); }) .attr("width", x.rangeBand()) .attr("height", x.rangeBand()) .style("fill-opacity", function(d) { return z(d.z); }) .style("fill", function(d) { return nodes[d.x].group == nodes[d.y].group ? c(nodes[d.x].group) : null; }) .on("mouseover", mouseover) .on("mouseout", mouseout); } function mouseover(p) { d3.selectAll(".row text").classed("active", function(d, i) { return i == p.y; }); d3.selectAll(".column text").classed("active", function(d, i) { return i == p.x; }); } function mouseout() { d3.selectAll("text").classed("active", false); } d3.select("#order").on("change", function() { clearTimeout(timeout); order(this.value); }); function order(value) { x.domain(orders[value]); var t = svg.transition().duration(2500); t.selectAll(".row") .delay(function(d, i) { return x(i) * 4; }) .attr("transform", function(d, i) { return "translate(0," + x(i) + ")"; }) .selectAll(".cell") .delay(function(d) { return x(d.x) * 4; }) .attr("x", function(d) { return x(d.x); }); t.selectAll(".column") .delay(function(d, i) { return x(i) * 4; }) .attr("transform", function(d, i) { return "translate(" + x(i) + ")rotate(-90)"; }); } var timeout = setTimeout(function() { order("group"); d3.select("#order").property("selectedIndex", 2).node().focus(); }, 5000); }); </script> <p class="attribution">Source: <a href="https://www-cs-staff.stanford.edu/~uno/sgb.html">The Stanford GraphBase</a>. <p>A network can be represented by an <i><a href="https://en.wikipedia.org/wiki/Adjacency_matrix">adjacency matrix</a></i>, where each cell <i>ij</i> represents an edge from vertex <i>i</i> to vertex <i>j</i>. Here, vertices represent characters in a book, while edges represent co-occurrence in a chapter. <p>Given this two-dimensional representation of a graph, a natural visualization is to show the matrix! However, the effectiveness of a matrix diagram is heavily dependent on the order of rows and columns: if related nodes are placed closed to each other, it is easier to identify clusters and bridges. <p>This example lets you try different orderings via the drop-down menu. This type of diagram can be extended with manual reordering of rows and columns, and expanding or collapsing of clusters, to allow deeper exploration. <p>While path-following is harder in a matrix view than in a <a href="https://mbostock.github.com/d3/ex/force.html">node-link diagram</a>, matrices have other advantages. As networks get large and highly connected, node-link diagrams often devolve into giant hairballs of line crossings. Line crossings are impossible with matrix views. Matrix cells can also be encoded to show additional data; here color depicts clusters computed by a community-detection algorithm. <p>Want more? See this analysis of <a href="../shuffle/compare.html">shuffling algorithms</a> using matrix diagrams. <footer> <aside>January 12, 2012</aside> <a href="../" rel="author">Daniel Licht, d3.js template by Mike Bostock</a> </footer>
https://d3js.org/d3.v2.min.js