Skip to content

Instantly share code, notes, and snippets.

@willzjc
Last active June 17, 2019 03:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save willzjc/cd154295ca1e70da9b8e4596f5795ab8 to your computer and use it in GitHub Desktop.
Save willzjc/cd154295ca1e70da9b8e4596f5795ab8 to your computer and use it in GitHub Desktop.
Matrix - Weighted with Legend
license: mit
termyear type target source weight
2017 Fall Double Major MXN USD 633
2017 Fall Double Major TRY USD 1904
2017 Fall Double Major ILS USD 2772
2017 Fall Double Major CNH USD 549
2017 Fall Double Major CNY USD 637
2017 Fall Double Major SGD USD 1845
2017 Fall Double Major HKD USD 1108
2017 Fall Double Major BRL USD 1362
2017 Fall Double Major TWD USD 1215
2017 Fall Double Major ZAR USD 1909
2017 Fall Double Major PLN USD 2085
2017 Fall Double Major HUF USD 2431
2017 Fall Double Major CZK USD 1304
2017 Fall Double Major RUB USD 122
2017 Fall Double Major EUR USD 1891
2017 Fall Double Major GBP USD 564
2017 Fall Double Major TRL USD 819
2017 Fall Double Major MAD USD 894
2017 Fall Double Major SOS USD 70
2017 Fall Double Major CHF USD 736
2017 Fall Double Major CAD USD 2676
2017 Fall Double Major COP USD 2069
2017 Fall Double Major PEN USD 3132
2017 Fall Double Major ARS USD 1822
2017 Fall Double Major NOK USD 1602
2017 Fall Double Major DKK USD 1442
2017 Fall Double Major NZD USD 1452
2017 Fall Double Major AUD USD 1352
2017 Fall Double Major KRW USD 1978
2017 Fall Double Major CLP USD 1516
2017 Fall Double Major USD ARS 652
2017 Fall Double Major CNY ARS 86
2017 Fall Double Major BRL ARS 317
2017 Fall Double Major RUB ARS 0
2017 Fall Double Major EUR ARS 183
2017 Fall Double Major GBP ARS 220
2017 Fall Double Major CHF ARS 717
2017 Fall Double Major CAD ARS 63
2017 Fall Double Major AUD ARS 377
2017 Fall Double Major USD AUD 2654
2017 Fall Double Major TRY AUD 651
2017 Fall Double Major ILS AUD 1234
2017 Fall Double Major CNY AUD 1802
2017 Fall Double Major SGD AUD 1981
2017 Fall Double Major HKD AUD 2097
2017 Fall Double Major KRW AUD 726
2017 Fall Double Major BRL AUD 426
2017 Fall Double Major TWD AUD 865
2017 Fall Double Major ZAR AUD 1702
2017 Fall Double Major PLN AUD 444
2017 Fall Double Major HUF AUD 616
2017 Fall Double Major CZK AUD 273
2017 Fall Double Major RUB AUD 303
2017 Fall Double Major EUR AUD 2375
2017 Fall Double Major GBP AUD 971
2017 Fall Double Major CHF AUD 1771
2017 Fall Double Major CAD AUD 1964
2017 Fall Double Major ARS AUD 968
2017 Fall Double Major NOK AUD 1070
2017 Fall Double Major DKK AUD 779
2017 Fall Double Major NZD AUD 2204
2017 Fall Double Major USD BRL 1758
2017 Fall Double Major CNY BRL 55
2017 Fall Double Major KRW BRL 296
2017 Fall Double Major RUB BRL 166
2017 Fall Double Major GBP BRL 189
2017 Fall Double Major CHF BRL 314
2017 Fall Double Major CAD BRL 1385
2017 Fall Double Major CLP BRL 300
2017 Fall Double Major EUR BRL 1452
2017 Fall Double Major ARS BRL 263
2017 Fall Double Major MXN CAD 1415
2017 Fall Double Major USD CAD 1589
2017 Fall Double Major TRY CAD 1241
2017 Fall Double Major ILS CAD 631
2017 Fall Double Major CNY CAD 2205
2017 Fall Double Major SGD CAD 1233
2017 Fall Double Major HKD CAD 1893
2017 Fall Double Major KRW CAD 1301
2017 Fall Double Major BRL CAD 865
2017 Fall Double Major TWD CAD 1224
2017 Fall Double Major ZAR CAD 1259
2017 Fall Double Major PLN CAD 972
2017 Fall Double Major HUF CAD 880
2017 Fall Double Major CZK CAD 540
2017 Fall Double Major RUB CAD 1076
2017 Fall Double Major EUR CAD 991
2017 Fall Double Major GBP CAD 1510
2017 Fall Double Major MAD CAD 503
2017 Fall Double Major CHF CAD 2301
2017 Fall Double Major COP CAD 801
2017 Fall Double Major ARS CAD 2090
2017 Fall Double Major NOK CAD 727
2017 Fall Double Major DKK CAD 231
2017 Fall Double Major NZD CAD 383
2017 Fall Double Major AUD CAD 1775
2017 Fall Double Major MXN CHF 716
2017 Fall Double Major USD CHF 421
2017 Fall Double Major TRY CHF 1026
2017 Fall Double Major ILS CHF 187
2017 Fall Double Major CNY CHF 579
2017 Fall Double Major SGD CHF 1094
2017 Fall Double Major HKD CHF 986
2017 Fall Double Major KRW CHF 46
2017 Fall Double Major BRL CHF 647
2017 Fall Double Major ZAR CHF 1913
2017 Fall Double Major PLN CHF 470
2017 Fall Double Major HUF CHF 374
2017 Fall Double Major CZK CHF 637
2017 Fall Double Major RUB CHF 759
2017 Fall Double Major EUR CHF 258
2017 Fall Double Major GBP CHF 1392
2017 Fall Double Major CAD CHF 638
2017 Fall Double Major NOK CHF 869
2017 Fall Double Major DKK CHF 1364
2017 Fall Double Major NZD CHF 864
2017 Fall Double Major AUD CHF 610
2017 Fall Double Major USD CLP 697
2017 Fall Double Major USD CNH 355
2017 Fall Double Major HKD CNH 1002
2017 Fall Double Major USD CNY 1048
2017 Fall Double Major CNH CNY 520
2017 Fall Double Major SGD CNY 1126
2017 Fall Double Major HKD CNY 1283
2017 Fall Double Major KRW CNY 454
2017 Fall Double Major TWD CNY 1280
2017 Fall Double Major RUB CNY 230
2017 Fall Double Major EUR CNY 1009
2017 Fall Double Major GBP CNY 385
2017 Fall Double Major CHF CNY 363
2017 Fall Double Major CAD CNY 930
2017 Fall Double Major NZD CNY 185
2017 Fall Double Major AUD CNY 526
2017 Fall Double Major USD COP 980
2017 Fall Double Major ILS COP 1622
2017 Fall Double Major EUR COP 1194
2017 Fall Double Major PEN COP 1361
2017 Fall Double Major ARS COP 1348
2017 Fall Double Major USD CZK 368
2017 Fall Double Major PLN CZK 942
2017 Fall Double Major HUF CZK 597
2017 Fall Double Major RUB CZK 291
2017 Fall Double Major EUR CZK 734
2017 Fall Double Major GBP CZK 475
2017 Fall Double Major CHF CZK 240
2017 Fall Double Major USD DKK 1198
2017 Fall Double Major CNY DKK 151
2017 Fall Double Major RUB DKK 243
2017 Fall Double Major EUR DKK 997
2017 Fall Double Major GBP DKK 1076
2017 Fall Double Major CHF DKK 220
2017 Fall Double Major NOK DKK 709
2017 Fall Double Major MXN EUR 1183
2017 Fall Double Major USD EUR 2353
2017 Fall Double Major TRY EUR 1738
2017 Fall Double Major ILS EUR 2134
2017 Fall Double Major CNH EUR 667
2017 Fall Double Major CNY EUR 1267
2017 Fall Double Major SGD EUR 1390
2017 Fall Double Major HKD EUR 1652
2017 Fall Double Major KRW EUR 1062
2017 Fall Double Major BRL EUR 1684
2017 Fall Double Major TWD EUR 1372
2017 Fall Double Major ZAR EUR 2066
2017 Fall Double Major PLN EUR 1403
2017 Fall Double Major HUF EUR 2022
2017 Fall Double Major CZK EUR 1007
2017 Fall Double Major RUB EUR 182
2017 Fall Double Major GBP EUR 962
2017 Fall Double Major TRL EUR 207
2017 Fall Double Major MAD EUR 1320
2017 Fall Double Major SOS EUR 942
2017 Fall Double Major CHF EUR 308
2017 Fall Double Major CAD EUR 2525
2017 Fall Double Major COP EUR 1099
2017 Fall Double Major PEN EUR 2005
2017 Fall Double Major ARS EUR 660
2017 Fall Double Major NOK EUR 2023
2017 Fall Double Major DKK EUR 1279
2017 Fall Double Major NZD EUR 2396
2017 Fall Double Major AUD EUR 3243
2017 Fall Double Major CLP EUR 1476
2017 Fall Double Major MXN GBP 1418
2017 Fall Double Major USD GBP 706
2017 Fall Double Major TRY GBP 1395
2017 Fall Double Major ILS GBP 1320
2017 Fall Double Major CNY GBP 1189
2017 Fall Double Major SGD GBP 592
2017 Fall Double Major HKD GBP 672
2017 Fall Double Major KRW GBP 819
2017 Fall Double Major BRL GBP 1523
2017 Fall Double Major TWD GBP 959
2017 Fall Double Major ZAR GBP 1507
2017 Fall Double Major PLN GBP 1369
2017 Fall Double Major HUF GBP 1668
2017 Fall Double Major CZK GBP 1943
2017 Fall Double Major RUB GBP 520
2017 Fall Double Major EUR GBP 752
2017 Fall Double Major MAD GBP 826
2017 Fall Double Major CHF GBP 1221
2017 Fall Double Major CAD GBP 1432
2017 Fall Double Major COP GBP 1077
2017 Fall Double Major CLP GBP 682
2017 Fall Double Major ARS GBP 151
2017 Fall Double Major NOK GBP 1037
2017 Fall Double Major DKK GBP 1767
2017 Fall Double Major NZD GBP 1957
2017 Fall Double Major AUD GBP 1692
2017 Fall Double Major USD HKD 1207
2017 Fall Double Major CNH HKD 793
2017 Fall Double Major CNY HKD 1161
2017 Fall Double Major SGD HKD 1039
2017 Fall Double Major KRW HKD 992
2017 Fall Double Major TWD HKD 1398
2017 Fall Double Major ZAR HKD 299
2017 Fall Double Major EUR HKD 1513
2017 Fall Double Major GBP HKD 1218
2017 Fall Double Major CZK HKD 81
2017 Fall Double Major RUB HKD 410
2017 Fall Double Major CHF HKD 488
2017 Fall Double Major CAD HKD 750
2017 Fall Double Major NZD HKD 454
2017 Fall Double Major AUD HKD 785
2017 Fall Double Major USD HUF 1209
2017 Fall Double Major EUR HUF 714
2017 Fall Double Major GBP HUF 694
2017 Fall Double Major CHF HUF 245
2017 Fall Double Major USD ILS 958
2017 Fall Double Major RUB ILS 431
2017 Fall Double Major EUR ILS 360
2017 Fall Double Major USD KRW 1182
2017 Fall Double Major CNY KRW 537
2017 Fall Double Major HKD KRW 698
2017 Fall Double Major BRL KRW 482
2017 Fall Double Major TWD KRW 311
2017 Fall Double Major RUB KRW 336
2017 Fall Double Major EUR KRW 1109
2017 Fall Double Major GBP KRW 744
2017 Fall Double Major CAD KRW 259
2017 Fall Double Major AUD KRW 240
2017 Fall Double Major USD MAD 297
2017 Fall Double Major ILS MAD 748
2017 Fall Double Major ZAR MAD 1801
2017 Fall Double Major RUB MAD 614
2017 Fall Double Major EUR MAD 1677
2017 Fall Double Major SOS MAD 352
2017 Fall Double Major CAD MAD 1781
2017 Fall Double Major COP MAD 274
2017 Fall Double Major ARS MAD 1415
2017 Fall Double Major NOK MAD 56
2017 Fall Double Major RUB MXN 0
2017 Fall Double Major USD MXN 400
2017 Fall Double Major EUR MXN 771
2017 Fall Double Major GBP MXN 206
2017 Fall Double Major CAD MXN 780
2017 Fall Double Major USD NOK 977
2017 Fall Double Major CNY NOK 91
2017 Fall Double Major HUF NOK 334
2017 Fall Double Major RUB NOK 539
2017 Fall Double Major EUR NOK 1075
2017 Fall Double Major GBP NOK 1041
2017 Fall Double Major PLN NOK 1160
2017 Fall Double Major CHF NOK 932
2017 Fall Double Major CAD NOK 66
2017 Fall Double Major ARS NOK 770
2017 Fall Double Major DKK NOK 481
2017 Fall Double Major AUD NOK 761
2017 Fall Double Major USD NZD 2500
2017 Fall Double Major CNY NZD 461
2017 Fall Double Major SGD NZD 1170
2017 Fall Double Major HKD NZD 2364
2017 Fall Double Major TWD NZD 335
2017 Fall Double Major RUB NZD 70
2017 Fall Double Major EUR NZD 1426
2017 Fall Double Major GBP NZD 929
2017 Fall Double Major CHF NZD 1805
2017 Fall Double Major CAD NZD 2168
2017 Fall Double Major AUD NZD 2414
2017 Fall Double Major USD PEN 1050
2017 Fall Double Major TRY PEN 2927
2017 Fall Double Major ILS PEN 1674
2017 Fall Double Major ZAR PEN 1625
2017 Fall Double Major MAD PEN 2226
2017 Fall Double Major SOS PEN 1643
2017 Fall Double Major CAD PEN 1534
2017 Fall Double Major ARS PEN 732
2017 Fall Double Major NOK PEN 1107
2017 Fall Double Major AUD PEN 1335
2017 Fall Double Major USD PLN 1532
2017 Fall Double Major HUF PLN 366
2017 Fall Double Major CZK PLN 475
2017 Fall Double Major RUB PLN 292
2017 Fall Double Major EUR PLN 1078
2017 Fall Double Major GBP PLN 491
2017 Fall Double Major CHF PLN 259
2017 Fall Double Major CAD PLN 388
<!DOCTYPE html>
<meta charset="utf-8">
<title>Rating Co-occurrence</title>
<link href='https://fonts.googleapis.com/css?family=Lato:400,900,700,300' rel='stylesheet' type='text/css'>
<style>
#container {
min-height: 800px;
margin: 0px;
padding: 0px;
display: -webkit-flex;
display: flex;
-webkit-flex-flow: row;
flex-flow: row;
}
#container > graphic {
margin: 4px;
padding: 2px;
min-width: 300px;
background: #fff;
-webkit-flex: 1 6 66%;
flex: 1 6 66%;
-webkit-order: 1;
order: 1;
}
#container > aside {
margin: 4px;
padding: 5px;
background: #fff;
-webkit-flex: 1 6 34%;
flex: 1 6 34%;
-webkit-order: 2;
order: 2;
}
header, footer {
display: block;
margin: 4px;
/*padding: 5px;*/
min-height: 50px;
/* background: #ffeebb;*/
font-family: 'Lato', sans-serif;
font-size: 32px;
font-weight: 300;
/*letter-spacing: -2px;*/
padding: 0em 1em 0 0;
text-rendering: optimizeLegibility;
text-align: center;
}
/* Too narrow to support three columns */
@media all and (max-width: 640px) {
#container, #page {
-webkit-flex-flow: column;
flex-direction: column;
}
#container > article, #container > nav, #container > aside {
/* Return them to document order */
-webkit-order: 0;
order: 0;
}
#container > nav, #container > aside, header, footer {
min-height: 50px;
max-height: 50px;
}
}
.aside {
color: #525252;
font: 300 10pt "Lato", sans-serif;
}
#explain {
font: 400 14pt "Lato", sans-serif;
height: 110px;
padding-top: 50px;
}
svg {
font: 300 10pt 'Lato', sans-serif;
}
.background {
fill: #eee;
}
line {
stroke: #fff;
}
text.active {
text-shadow: 2px 2px 2px yellow;
}
</style>
<script src="http://d3js.org/d3.v3.js"></script>
<!-- <script type="text/JavaScript" src="d3.js"></script> -->
<script src="jsLouvain.js" type="text/JavaScript"></script>
<header>Matrix Transitions</header>
<div id="container">
<graphic id="matrix"></graphic>
<aside id="explain-aside">
<div id="explain"></div> <!-- explain -->
<div class="aside">
<p>Year & Term: <select id="termyear">
</select>
<p>Order By: <select id="order">
<option value="name">Currency Shortcode (Alphabetically)</option>
<option value="count">Currency Relation with highest trend ratings</option>
<option value="group">Clusters of Co-Occurring CCY Pairs</option>
</select>
<p>This adjacency matrix diagram shows major currencies that co-occur as part of relations.
<p>Each colored cell represents two pair relations were each declared by at least four students. Mouseover a cell and the Google's metric of trend ratings will display in the upper right.
<p>The darker the cell, the higher the number of rating for that combination of disciplines.
<!-- <p>Colors represent clusters<span id="colors"></span></p> -->
<p/>Don't forget to use the drop-down menu above to reorder the matrix and explore the data!
</div> <!-- aside -->
</aside> <!-- explain-aside -->
</div> <!-- container -->
<footer>...</footer><script>
// set the dimensions of the graphic
var margin = {top: 240, right: 0, bottom: 10, left: 200},
width = (window.innerWidth*0.66) - 240,
height = width,
orders ,
matrix = [],
nodeHash = {},
nodes = [],
edges = [],
n; // nodes.length
// define the scales
var x = d3.scale.ordinal().rangeBands([0, width]),
z = d3.scale.linear().domain([0, 3300]).clamp(true),
c = d3.scale.ordinal().range(["#5254a3","#e80c89","#7848ac","#aa9408","#49bc38","#016437","#8a8309","#5062a4","#da01a0"]);
// add the svg container
var svg = d3.select("#matrix").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + (margin.left - 20) + "," + (margin.top - 50) + ")");
// add the explainer text which displays when you mouseover a colored cell
var explainer = d3.select("#explain").append("foreignObject")
.attr("width", screen.width * 0.2)
.attr("height", 200)
.append("xhtml:text")
.attr("id", "linklabel")
.html("<strong>hint:</strong> mouseover a colored cell<br> to view multiple-major parings");
d3.csv("data.csv",function(error,data) {
// populate the term dropdown based on the data
// var termyears = d3.nest()
// .key(function(d) { return d.termyear}).sortKeys(d3.ascending)
// .rollup(function(leaves) { return leaves.length; })
// .entries(data);
// var termyears = ["2005 Spring", "2005 Fall", "2006 Spring", "2006 Fall", "2007 Spring", "2007 Fall", "2008 Spring", "2008 Fall", "2009 Spring", "2009 Fall", "2010 Spring", "2010 Fall", "2011 Spring", "2011 Fall", "2012 Spring", "2012 Fall", "2013 Spring", "2013 Fall", "2014 Spring", "2014 Fall", "2015 Spring"]
var termyears= ['2017 Fall']
var list = d3.select("#termyear");
list.selectAll("option")
.data(termyears)
.enter()
.append("option")
.attr("value", function(d) {return d;})
.text(function(d) {return d; });
// set the default to the latest term /y ear
d3.select("#termyear").property("selectedIndex", (d3.select("#termyear").property("length")-1));
// filter the data by term/year; spring 2015 is the current default
var filtered_data = data.filter(function(d) {return d.termyear === d3.select("#termyear").property('value');});
// The matrix is a grey background
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height);
createNetwork(filtered_data);
// Precompute the orders.
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[a].group - nodes[b].group; })
};
// The default sort order.
x.domain(orders.name);
// Each row is a group of things, such as a white horizontal line, a colored square, and a text label
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); // call a row function to deal with the task of creating a square cell
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.replace(/&amp;/g, '&'); })
.style("fill", function(d,i) { return c(nodes[i].group); })
.on("mouseover", mouseover_row)
.on("mouseout", mouseout_row);
// each column is also a group containing a line and some text, both rotated -90 degrees
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.replace(/&amp;/g, '&'); })
.style("fill", function(d,i) { return c(nodes[i].group); })
.on("mouseover", mouseover_col)
.on("mouseout", mouseout_col);
// create the actual colored square cells and color them according to their group.
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 d.x == d.y ? 1 : z(d.z); })
.style("fill", function(d) { return d.x == d.y ? '#fff' : (nodes[d.x].group == nodes[d.y].group ? c(nodes[d.x].group) : '#03a423');})
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.append("title")
.text(function(d) {return d.x == d.y ? "" : d.z + " of Google Trend Rating";});
}
// when the Order By dropdown changes,
d3.select("#order").on("change", function() {
// clearTimeout(timeout); // This clears the delay set by WindowTimers.setTimeout ().
order(this.value);
});
// when the Term / Year dropdown changes,
d3.select("#termyear").on("change", function() {
var filtered_data = data.filter(function(d) {return d.termyear === d3.select("#termyear").property('value');});
createNetwork(filtered_data);
updateMatrix(matrix);
});
function order(value) {
//re-order the x domain according to the predefined sort orders
x.domain(orders[value]);
// set up the transition to last a total of 3 seconds
var t = svg.transition().duration(3000);
// have each row and column move after a delay that is a function of the index of its location
t.selectAll(".row")
.delay(function(d, i) { return x(i) * 5; })
.attr("transform", function(d, i) { return "translate(0," + x(i) + ")"; })
.selectAll(".cell")
.delay(function(d) { return x(d.x) * 5; })
.attr("x", function(d) { return x(d.x); });
t.selectAll(".column")
.delay(function(d, i) { return x(i) * 5; })
.attr("transform", function(d, i) { return "translate(" + x(i) + ")rotate(-90)"; });
}
// other options for the layout above that don't make the diagonal white:
// .style("fill-opacity", function(d) { return d.x == d.y ? 0 : z(d.z); }) // make the diagonal grey
// .style("fill", function(d) { return d.x == d.y ? '#696969' : (nodes[d.x].group == nodes[d.y].group ? c(nodes[d.x].group) : '#696969');})
// the row & column labels of the matrix and the square cells all have associated mouseover events
function mouseover_row(p) {
var activeCells = d3.selectAll("rect.cell").filter(function(d) {
return d.y == p[0].y && d.x != d.y ;
}).style('stroke-width',3).style('stroke','yellow');
}
function mouseout_row(p) {
d3.selectAll("rect.cell").filter(function(d) {
return d.y == p[0].y && d.x != d.y ;
}).style('stroke-width',0);
}
function mouseover_col(p) {
var activeCells = d3.selectAll("rect.cell").filter(function(d) {
return d.x == p[0].y && d.x != d.y ;
}).style('stroke-width',3).style('stroke','yellow');
}
function mouseout_col(p) {
d3.selectAll("rect.cell").filter(function(d) {
return d.x == p[0].y && d.x != d.y ;
}).style('stroke-width',0);
}
function mouseover(p) {
var rowtext = d3.selectAll(".row text").filter(function(d, i) { return i == p.y; }),
coltext = d3.selectAll(".column text").filter(function(d, i) { return i == p.x; });
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; });
if(rowtext.text() === coltext.text()) {
d3.select("#linklabel").html("");
}
else {
d3.select("#linklabel").html(rowtext.text() + ' | ' + coltext.text() + '<br><br><span style="font-size: 18pt;"> ' + p.z + "</span> Google Trend Rating");
}
}
function mouseout() {
d3.selectAll("text").classed("active", false);
d3.select("#linklabel").html("");
}
// take the data output and turn it into a network.
function createNetwork(edgelist) {
edges = [], nodeHash = {};
var nestedEdges, edgelist;
// account for triple majors,such as cases where a student is triple major in say, econ-math-statistics : the Cal Answers query returns two rows for econ-stat, one for 1st/2nd major, other for 1st/3rd major so combine the two duplicative rows and sum their values.
nestedEdges = d3.nest().key(function(d) {return d.source + '-' + d.target;}).rollup(function(v) { return d3.sum(v, function(d) { return +d.weight; }); }).entries(edgelist);
// manipulate the combined data into a network data structure of edges and nodes
nestedEdges.forEach(function(d) {
d.source = d.key.slice(0,d.key.indexOf('-'));
d.target = d.key.slice(d.key.indexOf('-') + 1, d.key.length);
d.weight = d.values;
});
// clean up the data structure and call it edgelist
edgelist = nestedEdges.map(function(d) {return {'source':d.source, 'target':d.target, 'weight':d.weight};});
// for each edge whose value is greater than 4
edgelist.forEach(function (edge) {
if (edge.weight >= 4) {
// use nodeHash to keep track of things as the nodes and edges are created
if (!nodeHash[edge.source]) {
nodeHash[edge.source] = {id: edge.source, name: edge.source};
if(nodes.filter(function(d) {return d.name === edge.source;}).length == 0) {nodes.push(nodeHash[edge.source]);}
}
if (!nodeHash[edge.target]) {
nodeHash[edge.target] = {id: edge.target, name: edge.target};
if(nodes.filter(function(d) {return d.name === edge.target;}).length == 0) {nodes.push(nodeHash[edge.target]); }
}
edges.push({source: nodeHash[edge.source], target: nodeHash[edge.target], weight: edge.weight});
}
});
var newedges = edges.map(function(d) {
return {
'sourceName' : d.source.name,
'targetName' : d.target.name,
'source' : "",
'target' : "",
'weight' : +d.weight
}
});
// remove nodes that don't belong in the array
// nodes.filter(return function(d) {return d.name === edges;});
newnodes = [];
nodes.forEach(function (node,i){
var b = newedges.filter(function(d) {return d.sourceName === node.name || d.targetName === node.name; });
if(b.length > 0){
newnodes.push(node);
}
});
nodes = newnodes;
nodes.sort(function (a, b) {
if (a.name > b.name) {
return 1;
}
if (a.name < b.name) {
return -1;
}
// a must be equal to b
return 0;
});
// convert the index to numeric for each node
nodes = nodes.map(function(d,i) {return {name: d.name, index: i};});
// add the node id to the nodeHash to create numeric source/target values that the matrix expects
nodes.forEach(function(node) { nodeHash[node.name] = {id: node.index, name: node.name};});
//update the edges with numeric source/target values that the matrix expects
newedges = edges.map(function(d) {
return {
'sourceName' : d.source.name,
'targetName' : d.target.name,
'source' : nodeHash[d.source.name].id,
'target' : nodeHash[d.target.name].id,
'weight' : +d.weight
}
});
// create the matrix
createMatrix(nodes, newedges);
}
function modularityCensus(nodes, edges) {
nodes.forEach(function (node) {
var theseEdges = edges.filter(function(d) {return d.sourceName === node.name || d.targetName === node.name});
node.majors = d3.sum(theseEdges.map(function(d) {return +d.weight;}));
});
}
function createMatrix(nodes, edges) {
// reset the matrix
matrix = [];
// set up the data for the Louvain method of detecting communities
var node_data = nodes.map(function (d) {return d.name});
var edge_data = edges.map(function (d) {return {source: d.sourceName, target: d.targetName, weight: d.weight}; });
// pass the data to the Louvain algorithm. The logic in an external jsLouvain.js file is what returns the result:
var community = jLouvain().nodes(node_data).edges(edge_data);
var result = community();
// update each node with its group number
nodes.forEach(function (node) {
node.group = result[node.name]
});
// sum up the total number of edges for each node
modularityCensus(nodes, edges);
n = nodes.length;
// Compute index per node.
nodes.forEach(function(node, i) {
node.count = 0;
matrix[i] = d3.range(n).map(function(j) { return {x: j, y: i, z: 0}; });
});
// Convert edges to a matrix; count major occurrences.
edges.forEach(function(edge) {
matrix[edge.source][edge.target].z += edge.weight;
matrix[edge.target][edge.source].z += edge.weight;
matrix[edge.source][edge.source].z += edge.weight;
matrix[edge.target][edge.target].z += edge.weight;
nodes[edge.source].count += edge.weight;
nodes[edge.target].count += edge.weight;
});
// setTimeout is a web browser function that can be used to execute a code snippet after a specified delay.
// var timeout = setTimeout(function() {
// }, 5000);
} // end ?
function updateMatrix(matrix) {
orders = {};
// Precompute the orders.
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[a].group - nodes[b].group; })
};
// use the current sort selection
x.domain(orders[d3.select("#order").property("value")]);
var rows = svg.selectAll("g.row")
.data(matrix)
.each(row);
rows.select("text")
.attr("x", -6)
.attr("y", x.rangeBand() / 2)
.attr("dy", ".32em")
.attr("text-anchor", "end")
.style("fill", function(d,i) { return c(nodes[i].group); })
.text(function(d, i) {
return nodes[i].name.replace(/&amp;/g, '&');
});
rows.select("line")
.attr("x2", width);
var newrows = rows.enter()
.append("g")
.attr("class", "row")
.each(row);
newrows.append("line")
.attr("x2", width);
newrows.append("text")
.attr("x", -6)
.attr("y", x.rangeBand() / 2)
.attr("dy", ".32em")
.attr("text-anchor", "end")
.style("fill", function(d,i) { return c(nodes[i].group); })
.text(function(d, i) {
return nodes[i].name.replace(/&amp;/g, '&');
});
rows.exit().remove();
svg.selectAll("g.row text")
.on("mouseover", mouseover_row)
.on("mouseout", mouseout_row);
// each column is also a group containing a line and some text, both rotated -90 degrees
var columns = svg.selectAll("g.column")
.data(matrix)
columns.enter().append("g")
.attr("class", "column")
.attr("transform", function(d, i) { return "translate(" + x(i) + ")rotate(-90)"; })
.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.replace(/&amp;/g, '&'); })
.style("fill", function(d,i) { return c(nodes[i].group); })
.on("mouseover", mouseover_col)
.on("mouseout", mouseout_col);
columns.append("line")
.attr("x1", -width);
columns.exit().remove();
columns.select("line")
.attr("x1", -width);
columns.select("text")
.attr("x", 6)
.attr("y", x.rangeBand() / 2)
.attr("dy", ".32em")
.attr("text-anchor", "start")
.text(function(d, i) { return nodes[i].name.replace(/&amp;/g, '&'); })
.style("fill", function(d,i) { return c(nodes[i].group); })
.on("mouseover", mouseover_col)
.on("mouseout", mouseout_col);
// create the actual colored square cells and color them according to their group.
function row(row) {
var cell = d3.select(this).selectAll(".cell")
.data(row.filter(function(d) {
return d.z;
}))
// .attr("x", function(d) { return x(d.x); })
.attr("width", x.rangeBand())
.attr("height", x.rangeBand())
.style("fill-opacity", function(d) { return d.x == d.y ? 1 : z(d.z); })
.style("fill", function(d) {
return d.x == d.y ? '#fff' : (nodes[d.x].group == nodes[d.y].group ? c(nodes[d.x].group) : '#565656');
})
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.text(function(d) {return d.x == d.y ? "" : d.z + " value pair rating";});
cell.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 d.x == d.y ? 1 : z(d.z); })
.style("fill", function(d) {
return d.x == d.y ? '#fff' : (nodes[d.x].group == nodes[d.y].group ? c(nodes[d.x].group) : '#ff0586');
})
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.append("title")
.text(function(d) {return d.x == d.y ? "" : d.z + " value pair relations";});
cell.exit().remove();
}
// set up the transition to last a total of 3 seconds
var t = svg.transition().duration(3000);
// have each row and column move after a delay that is a function of the index of its location
t.selectAll(".row")
.delay(function(d, i) { return x(i) * 5; })
.attr("transform", function(d, i) { return "translate(0," + x(i) + ")"; })
.selectAll(".cell")
.delay(function(d) { return x(d.x) * 5; })
.attr("x", function(d) { return x(d.x); });
t.selectAll(".column")
.delay(function(d, i) { return x(i) * 5; })
.attr("transform", function(d, i) { return "translate(" + x(i) + ")rotate(-90)"; });
function key(d) {return d.x;}
// order(d3.select("#order").property("value"));
} //updateMatrix
});
</script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-53090028-12', 'auto');
ga('send', 'pageview');
</script>
</html>
/*
Author: Corneliu S. (github.com/upphiminn)
This is a javascript implementation of the Louvain
community detection algorithm (http://arxiv.org/abs/0803.0476)
Based on https://bitbucket.org/taynaud/python-louvain/overview
*/
(function(){
jLouvain = function(){
//Constants
var __PASS_MAX = -1
var __MIN = 0.0000001
// var __MIN = 0.065
//Local vars
var original_graph_nodes;
var original_graph_edges;
var original_graph = {};
var partition_init;
//Helpers
function make_set(array){
var set = {};
array.forEach(function(d,i){
set[d] = true;
});
return Object.keys(set);
};
function obj_values(obj){
var vals = [];
for( var key in obj ) {
if ( obj.hasOwnProperty(key) ) {
vals.push(obj[key]);
}
}
return vals;
};
function get_degree_for_node(graph, node){
var neighbours = graph._assoc_mat[node] ? Object.keys(graph._assoc_mat[node]) : [];
var weight = 0;
neighbours.forEach(function(neighbour,i){
var value = graph._assoc_mat[node][neighbour] || 1;
if(node == neighbour)
value *= 2;
weight += value;
});
return weight;
};
function get_neighbours_of_node(graph, node){
if(typeof graph._assoc_mat[node] == 'undefined')
return [];
var neighbours = Object.keys(graph._assoc_mat[node]);
return neighbours;
}
function get_edge_weight(graph, node1, node2){
return graph._assoc_mat[node1] ? graph._assoc_mat[node1][node2] : undefined;
}
function get_graph_size(graph){
var size = 0;
graph.edges.forEach(function(edge){
size += edge.weight;
});
return size;
}
function add_edge_to_graph(graph, edge){
update_assoc_mat(graph, edge);
var edge_index = graph.edges.map(function(d){
return d.source+'_'+d.target;
}).indexOf(edge.source+'_'+edge.target);
if(edge_index != -1)
graph.edges[edge_index].weight = edge.weight;
else
graph.edges.push(edge);
}
function make_assoc_mat(edge_list){
var mat = {};
edge_list.forEach(function(edge, i){
mat[edge.source] = mat[edge.source] || {};
mat[edge.source][edge.target] = edge.weight;
mat[edge.target] = mat[edge.target] || {};
mat[edge.target][edge.source] = edge.weight;
});
return mat;
}
function update_assoc_mat(graph, edge){
graph._assoc_mat[edge.source] = graph._assoc_mat[edge.source] || {};
graph._assoc_mat[edge.source][edge.target] = edge.weight;
graph._assoc_mat[edge.target] = graph._assoc_mat[edge.target] || {};
graph._assoc_mat[edge.target][edge.source] = edge.weight;
}
function clone(obj){
if(obj == null || typeof(obj) != 'object')
return obj;
var temp = obj.constructor();
for(var key in obj)
temp[key] = clone(obj[key]);
return temp;
}
//Core-Algorithm Related
function init_status(graph, status, part){
status['nodes_to_com'] = {};
status['total_weight'] = 0;
status['internals'] = {};
status['degrees'] = {};
status['gdegrees'] = {};
status['loops'] = {};
status['total_weight'] = get_graph_size(graph);
if(typeof part == 'undefined'){
graph.nodes.forEach(function(node,i){
status.nodes_to_com[node] = i;
var deg = get_degree_for_node(graph, node);
if (deg < 0)
throw 'Bad graph type, use positive weights!';
status.degrees[i] = deg;
status.gdegrees[node] = deg;
status.loops[node] = get_edge_weight(graph, node, node) || 0;
status.internals[i] = status.loops[node];
});
}else{
graph.nodes.forEach(function(node,i){
var com = part[node];
status.nodes_to_com[node] = com;
var deg = get_degree_for_node(graph, node);
status.degrees[com] = (status.degrees[com] || 0) + deg;
status.gdegrees[node] = deg;
var inc = 0.0;
var neighbours = get_neighbours_of_node(graph, node);
neighbours.forEach(function(neighbour, i){
var weight = graph._assoc_mat[node][neighbour];
if (weight <= 0){
throw "Bad graph type, use positive weights";
}
if(part[neighbour] == com){
if (neighbour == node){
inc += weight;
}else{
inc += weight/2.0;
}
}
});
status.internals[com] = (status.internals[com] || 0) + inc;
});
}
}
function __modularity(status){
var links = status.total_weight;
var result = 0.0;
var communities = make_set(obj_values(status.nodes_to_com));
communities.forEach(function(com,i){
var in_degree = status.internals[com] || 0 ;
var degree = status.degrees[com] || 0 ;
if(links > 0){
result = result + in_degree / links - Math.pow((degree / (2.0*links)), 2);
}
});
return result;
}
function __neighcom(node, graph, status){
// compute the communities in the neighb. of the node, with the graph given by
// node_to_com
var weights = {};
var neighboorhood = get_neighbours_of_node(graph, node);//make iterable;
neighboorhood.forEach(function(neighbour, i){
if(neighbour != node){
var weight = graph._assoc_mat[node][neighbour] || 1;
var neighbourcom = status.nodes_to_com[neighbour];
weights[neighbourcom] = (weights[neighbourcom] || 0) + weight;
}
});
return weights;
}
function __insert(node, com, weight, status){
//insert node into com and modify status
status.nodes_to_com[node] = +com;
status.degrees[com] = (status.degrees[com] || 0) + (status.gdegrees[node]||0);
status.internals[com] = (status.internals[com] || 0) + weight + (status.loops[node]||0);
}
function __remove(node, com, weight, status){
//remove node from com and modify status
status.degrees[com] = ((status.degrees[com] || 0) - (status.gdegrees[node] || 0));
status.internals[com] = ((status.internals[com] || 0) - weight -(status.loops[node] ||0));
status.nodes_to_com[node] = -1;
}
function __renumber(dict){
var count = 0;
var ret = clone(dict); //deep copy :)
var new_values = {};
var dict_keys = Object.keys(dict);
dict_keys.forEach(function(key){
var value = dict[key];
var new_value = typeof new_values[value] =='undefined' ? -1 : new_values[value];
if(new_value == -1){
new_values[value] = count;
new_value = count;
count = count + 1;
}
ret[key] = new_value;
});
return ret;
}
function __one_level(graph, status){
//Compute one level of the Communities Dendogram.
var modif = true,
nb_pass_done = 0,
cur_mod = __modularity(status),
new_mod = cur_mod;
while (modif && nb_pass_done != __PASS_MAX){
cur_mod = new_mod;
modif = false;
nb_pass_done += 1
graph.nodes.forEach(function(node,i){
var com_node = status.nodes_to_com[node];
var degc_totw = (status.gdegrees[node] || 0) / (status.total_weight * 2.0);
var neigh_communities = __neighcom(node, graph, status);
__remove(node, com_node, (neigh_communities[com_node] || 0.0), status);
var best_com = com_node;
var best_increase = 0;
var neigh_communities_entries = Object.keys(neigh_communities);//make iterable;
neigh_communities_entries.forEach(function(com,i){
var incr = neigh_communities[com] - (status.degrees[com] || 0.0) * degc_totw;
if (incr > best_increase){
best_increase = incr;
best_com = com;
}
});
__insert(node, best_com, neigh_communities[best_com] || 0, status);
if(best_com != com_node)
modif = true;
});
new_mod = __modularity(status);
if(new_mod - cur_mod < __MIN)
break;
}
}
function induced_graph(partition, graph){
var ret = {nodes:[], edges:[], _assoc_mat: {}};
var w_prec, weight;
//add nodes from partition values
var partition_values = obj_values(partition);
ret.nodes = ret.nodes.concat(make_set(partition_values)); //make set
graph.edges.forEach(function(edge,i){
weight = edge.weight || 1;
var com1 = partition[edge.source];
var com2 = partition[edge.target];
w_prec = (get_edge_weight(ret, com1, com2) || 0);
var new_weight = (w_prec + weight);
add_edge_to_graph(ret, {'source': com1, 'target': com2, 'weight': new_weight});
});
return ret;
}
function partition_at_level(dendogram, level){
var partition = clone(dendogram[0]);
for(var i = 1; i < level + 1; i++ )
Object.keys(partition).forEach(function(key,j){
var node = key;
var com = partition[key];
partition[node] = dendogram[i][com];
});
return partition;
}
function generate_dendogram(graph, part_init){
if(graph.edges.length == 0){
var part = {};
graph.nodes.forEach(function(node,i){
part[node] = node;
});
return part;
}
var status = {};
init_status(original_graph, status, part_init);
var mod = __modularity(status);
var status_list = [];
__one_level(original_graph, status);
var new_mod = __modularity(status);
var partition = __renumber(status.nodes_to_com);
status_list.push(partition);
mod = new_mod;
var current_graph = induced_graph(partition, original_graph);
init_status(current_graph, status);
while (true){
__one_level(current_graph, status);
new_mod = __modularity(status);
if(new_mod - mod < __MIN)
break;
partition = __renumber(status.nodes_to_com);
status_list.push(partition);
mod = new_mod;
current_graph = induced_graph(partition, current_graph);
init_status(current_graph, status);
}
return status_list;
}
var core = function(){
var status = {};
var dendogram = generate_dendogram(original_graph, partition_init);
return partition_at_level(dendogram, dendogram.length - 1);
};
core.nodes = function(nds){
if(arguments.length > 0){
original_graph_nodes = nds;
}
return core;
};
core.edges = function(edgs){
if(typeof original_graph_nodes == 'undefined')
throw 'Please provide the graph nodes first!';
if(arguments.length > 0){
original_graph_edges = edgs;
var assoc_mat = make_assoc_mat(edgs);
original_graph = { 'nodes': original_graph_nodes,
'edges': original_graph_edges,
'_assoc_mat': assoc_mat };
}
return core;
};
core.partition_init = function(prttn){
if(arguments.length > 0){
partition_init = prttn;
}
return core;
};
return core;
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment