|
<!DOCTYPE html> |
|
<html class="ocks-org do-not-copy"> |
|
<meta charset="utf-8"> |
|
<title>Dramatic Co-occurrence</title> |
|
<style> |
|
@import url(../style.css?aea6f0a); |
|
d3_plot { |
|
font-size: 80%; |
|
} |
|
|
|
body.svg { |
|
margin-left: 0px; |
|
} |
|
|
|
.background { |
|
fill: #eee; |
|
} |
|
|
|
line { |
|
stroke: #fff; |
|
} |
|
|
|
text.active { |
|
fill: red; |
|
} |
|
</style> |
|
<script type="text/javascript" src="http://d3js.org/d3.v2.min.js?2.8.1"></script> |
|
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script> |
|
|
|
|
|
<header> |
|
</header> |
|
|
|
<h1>Character Co-occurrence in Shakespearean Drama</h1> |
|
|
|
<aside style="margin-top:20px;"> |
|
<p>Play: |
|
<select id="selected_json"> |
|
<option value='"http://tdm-api-dev.elasticbeanstalk.com/api/json/king_henry_the_fifth.json"'>Henry VIII</option> |
|
<option value='"http://tdm-api-dev.elasticbeanstalk.com/api/json/romeo_and_juliet.json"'>Romeo and Juliet</option> |
|
<option value='"http://tdm-api-dev.elasticbeanstalk.com/api/json/hamlet.json"'>Hamlet</option> |
|
</select> |
|
|
|
<i> </i> |
|
|
|
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 application visualizes the degree to which characters in Shakespeare's plays appear together. |
|
|
|
<p>Each colored cell represents two characters that appeared in the same scene, and darker cells indicate characters that co-occurred more frequently. |
|
|
|
<p>Use the drop-down menus to select a different play, reorder the matrix, and explore the data. |
|
|
|
<p>Built with data from ProQuest's Chadwyck Healey <a href="http://www.proquest.com/products-services/literature_online.html">Literature Online Collections</a>. |
|
</aside> |
|
|
|
<d3_plot></d3_plot> |
|
|
|
<script> |
|
|
|
function select_json(new_json) { |
|
|
|
var margin = { |
|
top: 120, |
|
right: 0, |
|
bottom: 10, |
|
left: 160 |
|
}, |
|
width = 800, |
|
height = 800; |
|
|
|
var x = d3.scale.ordinal().rangeBands([0, width]), |
|
z = d3.scale.linear().domain([0, 4]).clamp(true), |
|
c = d3.scale.category10().domain(d3.range(10)); |
|
|
|
var svg = d3.select("d3_plot").append("svg") |
|
.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + margin.bottom) |
|
.style("margin-left", "0px") |
|
.append("g") |
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); |
|
|
|
// Based on the user-selected input text above, make the appropriate api call and retrieve the json |
|
d3.json(new_json, function(miserables) { |
|
|
|
console.log(new_json) |
|
|
|
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); |
|
}); |
|
|
|
|
|
} |
|
|
|
// set initial json selection |
|
select_json("http://tdm-api-dev.elasticbeanstalk.com/api/json/hamlet.json"); |
|
|
|
// handle on click event |
|
d3.select('#selected_json').on('change', function() { |
|
|
|
// erase old image |
|
d3.select("svg").remove(); |
|
|
|
var new_json = eval(d3.select(this).property('value')); |
|
select_json(new_json); |
|
}); |
|
</script> |