Skip to content

Instantly share code, notes, and snippets.

@jrladd
Last active March 1, 2017 16:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrladd/9a3689b5d911418152d8aeb5ad7a005e to your computer and use it in GitHub Desktop.
Save jrladd/9a3689b5d911418152d8aeb5ad7a005e to your computer and use it in GitHub Desktop.
Inaugural Address topic model network

A network graph of the 10-topic LDA results for all 58 inaugural address. Whose inaugural addresses were semantically similar? Let's find out!

Features of this visualization:

  • drag canvas to pan
  • scroll to zoom
  • orange nodes are topics, blue are inaugural addresses
  • mouseover to see node labels
  • click on any node to see its ego network
  • click on an orange node to see the words in that topic
  • edge thickness is proportional to the percentage chance that a speech has words from a topic (from 0-100%)
  • the darker blue a node is, the later in time that speech was given
  • type to search for the president of your choice (make sure to click the search button)

Interested in how this data was collected? See the source code

{
"directed":false,
"graph":{},
"links":[
{
"source":0,
"target":67,
"weight":0.9590886150544218
},
{
"source":1,
"target":20,
"weight":0.050000000733082244
},
{
"source":1,
"target":22,
"weight":0.7694854398736801
},
{
"source":1,
"target":44,
"weight":0.6154333965606206
},
{
"source":1,
"target":54,
"weight":0.01428633453563116
},
{
"source":1,
"target":41,
"weight":0.0767532347608719
},
{
"source":1,
"target":65,
"weight":0.959087799525372
},
{
"source":1,
"target":25,
"weight":0.9639951848089239
},
{
"source":1,
"target":49,
"weight":0.7275433522038661
},
{
"source":1,
"target":18,
"weight":0.9804315278264504
},
{
"source":2,
"target":27,
"weight":0.39823008511020114
},
{
"source":2,
"target":5,
"weight":0.5863811416428256
},
{
"source":3,
"target":20,
"weight":0.05000000185190809
},
{
"source":3,
"target":50,
"weight":0.33097081882832996
},
{
"source":3,
"target":53,
"weight":0.9742836105334226
},
{
"source":3,
"target":26,
"weight":0.9749967671563833
},
{
"source":3,
"target":54,
"weight":0.014285902206991304
},
{
"source":3,
"target":36,
"weight":0.5261376907413773
},
{
"source":3,
"target":17,
"weight":0.9780473097643487
},
{
"source":3,
"target":30,
"weight":0.9549961712143652
},
{
"source":4,
"target":9,
"weight":0.9886058274312548
},
{
"source":5,
"target":20,
"weight":0.05000002940830765
},
{
"source":5,
"target":48,
"weight":0.9727245882042893
},
{
"source":5,
"target":7,
"weight":0.0233531604964371
},
{
"source":5,
"target":54,
"weight":0.014286849464311168
},
{
"source":5,
"target":23,
"weight":0.9549955371648371
},
{
"source":5,
"target":36,
"weight":0.08579903667620796
},
{
"source":5,
"target":58,
"weight":0.9141011690096893
},
{
"source":5,
"target":39,
"weight":0.6063814978638884
},
{
"source":5,
"target":47,
"weight":0.9918172713798397
},
{
"source":5,
"target":32,
"weight":0.9735270152422393
},
{
"source":5,
"target":49,
"weight":0.10111113977586238
},
{
"source":5,
"target":64,
"weight":0.9876693833982539
},
{
"source":6,
"target":24,
"weight":0.9873225076141071
},
{
"source":7,
"target":27,
"weight":0.6419323159989406
},
{
"source":7,
"target":67,
"weight":0.3204249523636425
},
{
"source":8,
"target":16,
"weight":0.9666617521481505
},
{
"source":9,
"target":20,
"weight":0.050000000705143516
},
{
"source":9,
"target":11,
"weight":0.9666601520040993
},
{
"source":9,
"target":54,
"weight":0.014288722887944277
},
{
"source":9,
"target":10,
"weight":0.9836353444940148
},
{
"source":9,
"target":41,
"weight":0.6592148774561867
},
{
"source":12,
"target":20,
"weight":0.05000000082431679
},
{
"source":12,
"target":66,
"weight":0.9774968873203003
},
{
"source":12,
"target":45,
"weight":0.9249924658565852
},
{
"source":12,
"target":22,
"weight":0.05476735387656795
},
{
"source":12,
"target":61,
"weight":0.9823501125463583
},
{
"source":12,
"target":39,
"weight":0.015581939186800203
},
{
"source":12,
"target":36,
"weight":0.3748521957032017
},
{
"source":12,
"target":42,
"weight":0.9804306943367375
},
{
"source":12,
"target":43,
"weight":0.9718711147446494
},
{
"source":12,
"target":54,
"weight":0.8714199469484332
},
{
"source":13,
"target":27,
"weight":0.9608669363829753
},
{
"source":14,
"target":27,
"weight":0.9830166743478126
},
{
"source":15,
"target":16,
"weight":0.05104387047404819
},
{
"source":15,
"target":24,
"weight":0.9407909991901486
},
{
"source":16,
"target":38,
"weight":0.9842086312589491
},
{
"source":16,
"target":52,
"weight":0.9526278528519924
},
{
"source":16,
"target":37,
"weight":0.962496232911238
},
{
"source":16,
"target":20,
"weight":0.5499604555685323
},
{
"source":16,
"target":39,
"weight":0.3694980514673991
},
{
"source":16,
"target":41,
"weight":0.13959510261083513
},
{
"source":16,
"target":56,
"weight":0.8789882082713888
},
{
"source":16,
"target":57,
"weight":0.9590872021117511
},
{
"source":16,
"target":31,
"weight":0.9883102986265877
},
{
"source":16,
"target":54,
"weight":0.014286092574535059
},
{
"source":19,
"target":67,
"weight":0.9901089299997118
},
{
"source":20,
"target":67,
"weight":0.050018907059669195
},
{
"source":20,
"target":33,
"weight":0.050000158386523254
},
{
"source":20,
"target":24,
"weight":0.05000610820515515
},
{
"source":20,
"target":27,
"weight":0.05001433725736169
},
{
"source":21,
"target":27,
"weight":0.9624962656343531
},
{
"source":22,
"target":33,
"weight":0.16665159469723412
},
{
"source":24,
"target":59,
"weight":0.949994558548883
},
{
"source":24,
"target":46,
"weight":0.9852441042208642
},
{
"source":24,
"target":54,
"weight":0.01428724112303176
},
{
"source":24,
"target":58,
"weight":0.06367240989369555
},
{
"source":24,
"target":50,
"weight":0.6536413508447406
},
{
"source":27,
"target":54,
"weight":0.014286267405427834
},
{
"source":27,
"target":40,
"weight":0.9571390794623486
},
{
"source":27,
"target":41,
"weight":0.1202102729466554
},
{
"source":27,
"target":56,
"weight":0.09676457200955446
},
{
"source":27,
"target":63,
"weight":0.986151403459331
},
{
"source":27,
"target":34,
"weight":0.9763121295272258
},
{
"source":27,
"target":49,
"weight":0.16200964648478502
},
{
"source":28,
"target":67,
"weight":0.9357033892785734
},
{
"source":29,
"target":33,
"weight":0.984744084266699
},
{
"source":33,
"target":60,
"weight":0.9742825485112381
},
{
"source":33,
"target":35,
"weight":0.9836329721441223
},
{
"source":33,
"target":44,
"weight":0.35493015821453866
},
{
"source":33,
"target":62,
"weight":0.9437451513462329
},
{
"source":33,
"target":54,
"weight":0.014286731132981215
},
{
"source":51,
"target":67,
"weight":0.9742814512820942
},
{
"source":54,
"target":67,
"weight":0.014285911720713019
},
{
"source":55,
"target":67,
"weight":0.9571330543617362
}
],
"multigraph":false,
"nodes":[
{
"bipartite":0,
"id":"7_madison_1813"
},
{
"bipartite":1,
"id":0,
"topic_words":"0.011*\"acquisition\" + 0.010*\"aided\" + 0.010*\"improvements\" + 0.009*\"agency\" + 0.009*\"treatment\" + 0.009*\"rendered\" + 0.009*\"activities\" + 0.008*\"corrected\" + 0.008*\"deliberate\" + 0.008*\"upon\""
},
{
"bipartite":0,
"id":"49_reagan_1981"
},
{
"bipartite":1,
"id":3,
"topic_words":"0.014*\"heads\" + 0.013*\"renewal\" + 0.012*\"watching\" + 0.010*\"everywhere\" + 0.010*\"loyal\" + 0.010*\"say\" + 0.010*\"senator\" + 0.010*\"precisely\" + 0.010*\"season\" + 0.009*\"summon\""
},
{
"bipartite":0,
"id":"19_lincoln_1861"
},
{
"bipartite":1,
"id":5,
"topic_words":"0.020*\"coast\" + 0.018*\"courts\" + 0.016*\"reforms\" + 0.014*\"jobs\" + 0.012*\"retreat\" + 0.010*\"platform\" + 0.010*\"allegiance\" + 0.010*\"potential\" + 0.010*\"definite\" + 0.009*\"eastern\""
},
{
"bipartite":0,
"id":"36_hoover_1929"
},
{
"bipartite":0,
"id":"55_bush_george_w_2005"
},
{
"bipartite":0,
"id":"38_roosevelt_franklin_1937"
},
{
"bipartite":1,
"id":9,
"topic_words":"0.022*\"dispute\" + 0.015*\"plainly\" + 0.015*\"races\" + 0.014*\"expressly\" + 0.012*\"similar\" + 0.011*\"acceptance\" + 0.011*\"masters\" + 0.011*\"conclusive\" + 0.011*\"meant\" + 0.010*\"presidency\""
},
{
"bipartite":0,
"id":"23_hayes_1877"
},
{
"bipartite":0,
"id":"11_jackson_1829"
},
{
"bipartite":1,
"id":2,
"topic_words":"0.023*\"islands\" + 0.013*\"survive\" + 0.013*\"stands\" + 0.012*\"fearful\" + 0.011*\"rising\" + 0.010*\"peril\" + 0.010*\"jobs\" + 0.010*\"gain\" + 0.009*\"has\" + 0.008*\"seize\""
},
{
"bipartite":0,
"id":"25_cleveland_1885"
},
{
"bipartite":0,
"id":"27_cleveland_1893"
},
{
"bipartite":0,
"id":"15_polk_1845"
},
{
"bipartite":1,
"id":1,
"topic_words":"0.017*\"agitation\" + 0.013*\"supposed\" + 0.011*\"despair\" + 0.009*\"exert\" + 0.009*\"story\" + 0.009*\"convinced\" + 0.009*\"jealous\" + 0.009*\"neighbor\" + 0.009*\"unimpaired\" + 0.008*\"decided\""
},
{
"bipartite":0,
"id":"51_bush_george_h_w_1989"
},
{
"bipartite":0,
"id":"5_jefferson_1805"
},
{
"bipartite":0,
"id":"9_monroe_1821"
},
{
"bipartite":0,
"id":"2_washington_1793"
},
{
"bipartite":0,
"id":"45_johnson_1965"
},
{
"bipartite":0,
"id":"34_harding_1921"
},
{
"bipartite":0,
"id":"47_nixon_1973"
},
{
"bipartite":1,
"id":7,
"topic_words":"0.012*\"compact\" + 0.010*\"vigorous\" + 0.010*\"governmental\" + 0.010*\"lights\" + 0.009*\"processes\" + 0.009*\"activities\" + 0.009*\"realization\" + 0.009*\"repose\" + 0.009*\"filled\" + 0.008*\"races\""
},
{
"bipartite":0,
"id":"6_madison_1809"
},
{
"bipartite":0,
"id":"52_clinton_1993"
},
{
"bipartite":1,
"id":8,
"topic_words":"0.016*\"story\" + 0.014*\"extravagance\" + 0.011*\"steady\" + 0.011*\"alliance\" + 0.010*\"activity\" + 0.009*\"usefulness\" + 0.008*\"thrift\" + 0.008*\"bounty\" + 0.008*\"defended\" + 0.008*\"fairness\""
},
{
"bipartite":0,
"id":"30_roosevelt_theodore_1905"
},
{
"bipartite":0,
"id":"50_reagan_1985"
},
{
"bipartite":0,
"id":"21_grant_1869"
},
{
"bipartite":0,
"id":"13_van_buren_1837"
},
{
"bipartite":0,
"id":"53_clinton_1997"
},
{
"bipartite":1,
"id":6,
"topic_words":"0.020*\"nuclear\" + 0.014*\"senator\" + 0.014*\"aided\" + 0.014*\"areas\" + 0.011*\"bible\" + 0.010*\"barriers\" + 0.010*\"rates\" + 0.009*\"space\" + 0.009*\"hopeful\" + 0.008*\"compassion\""
},
{
"bipartite":0,
"id":"54_bush_george_w_2001"
},
{
"bipartite":0,
"id":"41_truman_1949"
},
{
"bipartite":0,
"id":"56_obama_2009"
},
{
"bipartite":0,
"id":"12_jackson_1833"
},
{
"bipartite":0,
"id":"18_buchanan_1857"
},
{
"bipartite":0,
"id":"35_coolidge_1925"
},
{
"bipartite":0,
"id":"44_kennedy_1961"
},
{
"bipartite":0,
"id":"14_harrison_1841"
},
{
"bipartite":0,
"id":"29_mckinley_1901"
},
{
"bipartite":0,
"id":"43_eisenhower_1957"
},
{
"bipartite":0,
"id":"32_wilson_1913"
},
{
"bipartite":0,
"id":"20_lincoln_1865"
},
{
"bipartite":0,
"id":"24_garfield_1881"
},
{
"bipartite":0,
"id":"31_taft_1909"
},
{
"bipartite":0,
"id":"58_trump_2017"
},
{
"bipartite":0,
"id":"28_mckinley_1897"
},
{
"bipartite":0,
"id":"10_adams_john_quincy_1825"
},
{
"bipartite":0,
"id":"3_adams_john_1797"
},
{
"bipartite":0,
"id":"16_taylor_1849"
},
{
"bipartite":0,
"id":"42_eisenhower_1953"
},
{
"bipartite":0,
"id":"40_roosevelt_franklin_1945"
},
{
"bipartite":0,
"id":"33_wilson_1917"
},
{
"bipartite":0,
"id":"37_roosevelt_franklin_1933"
},
{
"bipartite":0,
"id":"4_jefferson_1801"
},
{
"bipartite":0,
"id":"1_washington_1789"
},
{
"bipartite":0,
"id":"39_roosevelt_franklin_1941"
},
{
"bipartite":0,
"id":"46_nixon_1969"
},
{
"bipartite":0,
"id":"17_pierce_1853"
},
{
"bipartite":0,
"id":"48_carter_1977"
},
{
"bipartite":0,
"id":"8_monroe_1817"
},
{
"bipartite":0,
"id":"26_harrison_1889"
},
{
"bipartite":0,
"id":"22_grant_1873"
},
{
"bipartite":0,
"id":"57_obama_2013"
},
{
"bipartite":1,
"id":4,
"topic_words":"0.025*\"british\" + 0.016*\"coast\" + 0.016*\"respecting\" + 0.011*\"dominions\" + 0.011*\"facility\" + 0.010*\"accord\" + 0.010*\"mississippi\" + 0.010*\"constituents\" + 0.009*\"season\" + 0.009*\"attitude\""
}
]
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: .6;
shape-rendering: geometricPrecision;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
}
#tools div {
display: inline;
}
form, select {
float: right;
display: inline;
}
</style>
<body>
<div id='tools'></div>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var graph; //Global variable for the graph
svg.append('rect')
.attr('width', '100%')
.attr('height', '100%')
.attr('fill', '#FFFFFF');
// Call zoom for svg container.
svg.call(d3.zoom().on('zoom', zoomed)).on("dblclick.zoom", null);
var container = svg.append('g');
//Create scales for color and edge weight
var color = d3.scaleLinear()
.domain([1776,2017])
.range([0.1,1]);
var weight = d3.scaleLinear()
.domain([0,1])
.range([1,5]);
// Create form for search (see function below).
var search = d3.select("div#tools").append('form').attr('onsubmit', 'return false;');
var box = search.append('input')
.attr('type', 'text')
.attr('id', 'searchTerm')
.attr('placeholder', 'Type to search...');
var button = search.append('input')
.attr('type', 'button')
.attr('value', 'Search')
.on('click', function () { searchNodes(); });
//Toggle for ego networks on click (function below).
var toggle = 0;
//Create groups for nodes and links
var link = container.append("g")
.attr("class", "links")
.selectAll(".link"),
node = container.append("g")
.attr("class", "nodes")
.selectAll(".node");
//Get data from json file, assign that data to graph variable.
d3.json("inaugural.json", function(error, json) {
if (error) throw error;
graph = json;
update();
});
// Zooming function translates the size of the svg container.
function zoomed() {
container.attr("transform", "translate(" + d3.event.transform.x + ", " + d3.event.transform.y + ") scale(" + d3.event.transform.k + ")");
}
// Search for nodes by making all unmatched nodes temporarily transparent.
function searchNodes() {
var term = document.getElementById('searchTerm').value;
var selected = container.selectAll('.node').filter(function (d, i) {
return d.id.toString().search(term.toLowerCase()) == -1;
});
selected.style('opacity', '0');
var link = container.selectAll('.link');
link.style('stroke-opacity', '0');
d3.selectAll('.node').transition()
.duration(5000)
.style('opacity', '1');
d3.selectAll('.link').transition().duration(5000).style('stroke-opacity', '0.6');
}
//Draw the graph!
function update() {
//Parameters for force layout simulation
var simulation = d3.forceSimulation(graph.nodes)
.force("link", d3.forceLink(graph.links))//.id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength([-300]))//.distanceMax([500]))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("x", d3.forceX())
.force("y", d3.forceY())
.stop();
//The graph will be drawn behind the scenes and then displayed in static form.
//This code tells the program how many times to iterate through the layout simulation.
for (var i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); i < n; ++i) {
simulation.tick();
}
// Data join with links and corresponding nodes.
//If we wanted to reload the graph with an adjusted node set, we could do so.
link = link.data(graph.links, function(d) {return d.source.id + ', ' + d.target.id;});
link.exit().remove();
var linkEnter = link.enter().append('line')
.attr('class', 'link');
link = linkEnter.merge(link)
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; })
.attr("stroke-width", function(d) { return weight(d.weight); });
// When adding and removing graph.nodes, reassert attributes and behaviors.
node = node.data(graph.nodes, function(d) {return d.id;});
node.exit().remove();
var nodeEnter = node.enter().append('circle')
.attr('r', 10)
.attr("fill", function(d) { if (d.bipartite == 1) {return '#FFA500';} else { var person_data = d.id.split('_'); return d3.interpolateBlues(color(parseInt(person_data[person_data.length-1])));} })//return color(d.bipartite); }) // Color by bipartite attribute.
.attr('class', 'node')
.attr('id', function(d) { return "n" + d.id.toString(); })
.attr('clickToggle', 0)
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
// On click, toggle ego networks for the selected node. (See function below.)
.on('click', function(d) { toggleClick(d); });
node = nodeEnter.merge(node);
node.append("title")
.text(function(d) { return d.id; });
}
// A function to handle click toggling based on neighboring nodes.
function toggleClick(d) {
// Make object of all neighboring nodes.
connectedNodes = {};
connectedNodes[d.id] = true;
graph.links.forEach(function(l) {
if (l.source.id == d.id) { connectedNodes[l.target.id] = true; }
else if (l.target.id == d.id) { connectedNodes[l.source.id] = true; };
});
if (toggle == 0) {
// Ternary operator restyles links and nodes if they are adjacent.
d3.selectAll('.link').style('stroke-opacity', function (l) {
return l.target == d || l.source == d ? 1 : 0.2;
});
d3.selectAll('.node').style('opacity', function (n) {
if (n.id in connectedNodes) { return 1; }
else { return 0.2; };
});
// Show information when node is clicked
d3.select('div#tools').append('span').text(d.topic_words);
toggle = 1;
}
else {
// Restore nodes and links to normal opacity.
d3.selectAll('.link').style('stroke-opacity', 0.6);
d3.selectAll('.node').style('opacity', 1);
d3.selectAll('span').remove();
toggle = 0;
}
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment