Skip to content

Instantly share code, notes, and snippets.

@jeremy6462
Created April 20, 2018 14:30
Show Gist options
  • Save jeremy6462/b4147dc592626461f8fedd477bc8f608 to your computer and use it in GitHub Desktop.
Save jeremy6462/b4147dc592626461f8fedd477bc8f608 to your computer and use it in GitHub Desktop.
Graph Visualization - CS 590V
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="links.js"></script>
<script src="nodes.js"></script>
<!-- Dataset found here: http://snap.stanford.edu/data/soc-sign-bitcoinotc.html -->
<style>
text {
font-size: 14px;
font-family: monospace;
}
#sliderContainer {
text-align: center;
top: 600px;
}
div.tooltip {
position: absolute;
text-align: center;
width: 160px;
height: 108px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.selected {
fill: "green";
opacity: 0.7;
}
.node {
fill: "red";
opacity: 0.7;
}
.bar-on {
fill: steelblue;
opacity: 1;
}
.bar-off {
fill: steelblue;
opacity: 0.3;
}
.line-on {
stroke: steelblue;
stroke-width: 2;
opacity: 1;
}
.line-off {
stroke: steelblue;
stroke-width: 2;
opacity: 0;
}
div.tooltip {
position: absolute;
text-align: center;
width: 160px;
height: 108px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
</head>
<body>
<h3>My visualization shows bitcoin node ratings between two parties. Each node represents a miner on bitcoin and each connection is a rating from one node to another. The bottom bar graph shows the count for each rating and interactions highlight the node relationships in the graph. This dataset contains around 500 nodes and 2000 edges (after trimming to make the visualization more performant). This dataset can be used to understand where untrustworthy nodes lie in the network and to understand which participants are most active in the bitcoin system.</h3>
<svg class="graph-svg" width="960" height="900"></svg>
<script>
// the count for each rating
var cluster_ratings = [];
// format link data
links_data.forEach(function (link) {
link.rate = +link.rate + 10
var d = new Date(0);
d.setUTCSeconds(link.time);
link.date = d;
if (link.rate in cluster_ratings) {
cluster_ratings[link.rate] += 1;
} else {
cluster_ratings[link.rate] = 1;
}
});
// create tool tip
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// set up simulation
var graphSvg = d3.select(".graph-svg"),
width = +graphSvg.attr("width"),
height = +graphSvg.attr("height");
var simulation = d3.forceSimulation()
.nodes(nodesByName);
simulation
.force("charge_force", d3.forceManyBody())
.force("center_force", d3.forceCenter(width / 2, height / 2));
var node = graphSvg.append("g")
.attr("class", "node")
.selectAll("circle")
.data(nodesByName)
.enter()
.append("circle")
.attr("r", 5);
simulation.on("tick", tickActions );
simulation.force("links", d3.forceLink(links_data).id(function(d) { return d.name; }))
// Layout the graph statically so that the website is performant
// https://bl.ocks.org/mbostock/1667139
for (var i = 0, n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay())); i < n; ++i) {
simulation.tick();
}
var link = graphSvg.append("g")
.attr("class", "line-on")
.selectAll("line")
.data(links_data)
.enter().append("line");
function tickActions() {
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
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; });
};
// Create SVG for bar graph
// https://bl.ocks.org/d3noob/bdf28027e0ce70bd132edc64f1dd7ea4
var margin = {top: 20, right: 20, bottom: 30, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
var barSvg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
x.domain([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]);
y.domain([0, d3.max(cluster_ratings, function(d) { return d; })]);
var currentlySelectedRating = null;
barSvg.selectAll(".bar")
.data(cluster_ratings)
.enter().append("rect")
.attr("class", "bar-on")
.attr("x", function(d, i) { return x(i); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d); })
.attr("height", function(d) { return height - y(d); })
.on("mouseover", function() {
if (currentlySelectedRating != null) { return; }
var ratingCount;
barSvg.selectAll("rect")
.attr("class", "bar-off");
d3.select(this)
.attr("class", function(d) {
ratingCount = d;
return "bar-on";
});
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html("Count of Ratings: " + ratingCount)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d, i) {
if (currentlySelectedRating != null) { return; }
barSvg.selectAll("rect")
.attr("class", "bar-on");
tooltip.transition()
.duration(500)
.style("opacity", 0);
})
.on("click", function(ratingCount, rating) {
if (currentlySelectedRating != rating) { // click to turn on selected
currentlySelectedRating = rating
barSvg.selectAll("rect")
.attr("class", "bar-off");
d3.select(this)
.attr("class", "bar-on");
graphSvg.selectAll("line")
.attr("class", function(d) {
if (d.rate == currentlySelectedRating) {
return "line-on";
} else {
return "line-off";
}
});
} else { // click to turn off selected
currentlySelectedRating = null;
barSvg.selectAll("rect")
.attr("class", "bar-on");
graphSvg.selectAll("line")
.attr("class", "line-on");
}
});
barSvg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
barSvg.append("g")
.call(d3.axisLeft(y));
// add the brushing to the graph
var brushedNodes = [];
var brush = graphSvg.append("g")
.attr("class", "brush");
brush.call(d3.brush()
.extent([[0, 0], [width, height]])
.on("end", brushended));
function brushended() {
if (d3.event.selection != null) {
d3.select(this).call(d3.event.target.move, null);
var selection = d3.event.selection;
brushedNodes = [];
ratingsToShow = new Set();
nodesByName.forEach(function (d) {
if (selection != null &&
(selection[0][0] <= d.x && d.x < selection[1][0]) &&
(selection[0][1] <= d.y && d.y < selection[1][1])) {
brushedNodes.push(d.name);
}
});
graphSvg.selectAll("line")
.attr("class", function(link) {
if (brushedNodes.includes(link.source.name) || brushedNodes.includes(link.source.target)) {
ratingsToShow.add(link.rate);
return "line-on";
} else {
return "line-off";
}
});
barSvg.selectAll("rect")
.attr("class", function(d, i) {
if (ratingsToShow.has(i)) {
return "bar-on";
} else {
return "bar-off";
}
});
}
}
// reset click
d3.select("body").on("click",function(){
if (currentlySelectedRating == null) {
currentlySelectedRating = null;
barSvg.selectAll("rect")
.attr("class", "bar-on");
graphSvg.selectAll("line")
.attr("class", "line-on");
}
});
</script>
</body>
var nodesByName = [
{
"name": "2"
},
{
"name": "1"
},
{
"name": "4"
},
{
"name": "5"
},
{
"name": "6"
},
{
"name": "7"
},
{
"name": "8"
},
{
"name": "9"
},
{
"name": "10"
},
{
"name": "13"
},
{
"name": "17"
},
{
"name": "19"
},
{
"name": "21"
},
{
"name": "23"
},
{
"name": "29"
},
{
"name": "31"
},
{
"name": "32"
},
{
"name": "33"
},
{
"name": "34"
},
{
"name": "35"
},
{
"name": "36"
},
{
"name": "39"
},
{
"name": "41"
},
{
"name": "47"
},
{
"name": "51"
},
{
"name": "54"
},
{
"name": "56"
},
{
"name": "57"
},
{
"name": "60"
},
{
"name": "64"
},
{
"name": "68"
},
{
"name": "71"
},
{
"name": "74"
},
{
"name": "75"
},
{
"name": "76"
},
{
"name": "77"
},
{
"name": "78"
},
{
"name": "81"
},
{
"name": "100"
},
{
"name": "101"
},
{
"name": "104"
},
{
"name": "110"
},
{
"name": "111"
},
{
"name": "112"
},
{
"name": "114"
},
{
"name": "115"
},
{
"name": "119"
},
{
"name": "125"
},
{
"name": "132"
},
{
"name": "134"
},
{
"name": "135"
},
{
"name": "138"
},
{
"name": "139"
},
{
"name": "141"
},
{
"name": "143"
},
{
"name": "144"
},
{
"name": "145"
},
{
"name": "165"
},
{
"name": "171"
},
{
"name": "177"
},
{
"name": "178"
},
{
"name": "180"
},
{
"name": "192"
},
{
"name": "215"
},
{
"name": "219"
},
{
"name": "225"
},
{
"name": "227"
},
{
"name": "234"
},
{
"name": "241"
},
{
"name": "247"
},
{
"name": "251"
},
{
"name": "261"
},
{
"name": "266"
},
{
"name": "270"
},
{
"name": "271"
},
{
"name": "274"
},
{
"name": "304"
},
{
"name": "308"
},
{
"name": "309"
},
{
"name": "311"
},
{
"name": "312"
},
{
"name": "320"
},
{
"name": "324"
},
{
"name": "330"
},
{
"name": "350"
},
{
"name": "353"
},
{
"name": "356"
},
{
"name": "384"
},
{
"name": "396"
},
{
"name": "399"
},
{
"name": "416"
},
{
"name": "421"
},
{
"name": "425"
},
{
"name": "431"
},
{
"name": "438"
},
{
"name": "442"
},
{
"name": "446"
},
{
"name": "464"
},
{
"name": "484"
},
{
"name": "486"
},
{
"name": "492"
},
{
"name": "494"
},
{
"name": "505"
},
{
"name": "511"
},
{
"name": "522"
},
{
"name": "537"
},
{
"name": "539"
},
{
"name": "540"
},
{
"name": "545"
},
{
"name": "563"
},
{
"name": "567"
},
{
"name": "569"
},
{
"name": "579"
},
{
"name": "592"
},
{
"name": "61"
},
{
"name": "142"
},
{
"name": "146"
},
{
"name": "149"
},
{
"name": "156"
},
{
"name": "166"
},
{
"name": "167"
},
{
"name": "198"
},
{
"name": "202"
},
{
"name": "401"
},
{
"name": "455"
},
{
"name": "516"
},
{
"name": "523"
},
{
"name": "3"
},
{
"name": "217"
},
{
"name": "269"
},
{
"name": "361"
},
{
"name": "397"
},
{
"name": "439"
},
{
"name": "26"
},
{
"name": "45"
},
{
"name": "66"
},
{
"name": "72"
},
{
"name": "87"
},
{
"name": "189"
},
{
"name": "282"
},
{
"name": "319"
},
{
"name": "352"
},
{
"name": "423"
},
{
"name": "466"
},
{
"name": "96"
},
{
"name": "258"
},
{
"name": "268"
},
{
"name": "280"
},
{
"name": "521"
},
{
"name": "550"
},
{
"name": "28"
},
{
"name": "53"
},
{
"name": "55"
},
{
"name": "80"
},
{
"name": "88"
},
{
"name": "89"
},
{
"name": "108"
},
{
"name": "109"
},
{
"name": "113"
},
{
"name": "116"
},
{
"name": "120"
},
{
"name": "122"
},
{
"name": "133"
},
{
"name": "137"
},
{
"name": "153"
},
{
"name": "155"
},
{
"name": "159"
},
{
"name": "161"
},
{
"name": "175"
},
{
"name": "184"
},
{
"name": "185"
},
{
"name": "188"
},
{
"name": "193"
},
{
"name": "195"
},
{
"name": "200"
},
{
"name": "203"
},
{
"name": "204"
},
{
"name": "206"
},
{
"name": "207"
},
{
"name": "209"
},
{
"name": "210"
},
{
"name": "212"
},
{
"name": "222"
},
{
"name": "228"
},
{
"name": "232"
},
{
"name": "235"
},
{
"name": "240"
},
{
"name": "243"
},
{
"name": "249"
},
{
"name": "254"
},
{
"name": "272"
},
{
"name": "283"
},
{
"name": "285"
},
{
"name": "293"
},
{
"name": "294"
},
{
"name": "296"
},
{
"name": "310"
},
{
"name": "314"
},
{
"name": "322"
},
{
"name": "326"
},
{
"name": "345"
},
{
"name": "351"
},
{
"name": "354"
},
{
"name": "358"
},
{
"name": "365"
},
{
"name": "368"
},
{
"name": "378"
},
{
"name": "383"
},
{
"name": "386"
},
{
"name": "393"
},
{
"name": "405"
},
{
"name": "410"
},
{
"name": "417"
},
{
"name": "418"
},
{
"name": "419"
},
{
"name": "424"
},
{
"name": "429"
},
{
"name": "440"
},
{
"name": "447"
},
{
"name": "449"
},
{
"name": "460"
},
{
"name": "465"
},
{
"name": "473"
},
{
"name": "480"
},
{
"name": "481"
},
{
"name": "489"
},
{
"name": "498"
},
{
"name": "533"
},
{
"name": "538"
},
{
"name": "547"
},
{
"name": "548"
},
{
"name": "584"
},
{
"name": "593"
},
{
"name": "160"
},
{
"name": "199"
},
{
"name": "342"
},
{
"name": "468"
},
{
"name": "491"
},
{
"name": "493"
},
{
"name": "518"
},
{
"name": "546"
},
{
"name": "591"
},
{
"name": "594"
},
{
"name": "15"
},
{
"name": "70"
},
{
"name": "97"
},
{
"name": "239"
},
{
"name": "297"
},
{
"name": "16"
},
{
"name": "325"
},
{
"name": "20"
},
{
"name": "93"
},
{
"name": "257"
},
{
"name": "284"
},
{
"name": "467"
},
{
"name": "490"
},
{
"name": "506"
},
{
"name": "69"
},
{
"name": "263"
},
{
"name": "527"
},
{
"name": "25"
},
{
"name": "363"
},
{
"name": "427"
},
{
"name": "231"
},
{
"name": "374"
},
{
"name": "52"
},
{
"name": "62"
},
{
"name": "186"
},
{
"name": "256"
},
{
"name": "328"
},
{
"name": "411"
},
{
"name": "476"
},
{
"name": "65"
},
{
"name": "79"
},
{
"name": "95"
},
{
"name": "246"
},
{
"name": "248"
},
{
"name": "267"
},
{
"name": "371"
},
{
"name": "387"
},
{
"name": "390"
},
{
"name": "415"
},
{
"name": "436"
},
{
"name": "458"
},
{
"name": "528"
},
{
"name": "37"
},
{
"name": "46"
},
{
"name": "500"
},
{
"name": "44"
},
{
"name": "301"
},
{
"name": "298"
},
{
"name": "83"
},
{
"name": "86"
},
{
"name": "105"
},
{
"name": "140"
},
{
"name": "147"
},
{
"name": "148"
},
{
"name": "150"
},
{
"name": "157"
},
{
"name": "162"
},
{
"name": "163"
},
{
"name": "168"
},
{
"name": "174"
},
{
"name": "265"
},
{
"name": "372"
},
{
"name": "176"
},
{
"name": "313"
},
{
"name": "318"
},
{
"name": "435"
},
{
"name": "404"
},
{
"name": "451"
},
{
"name": "453"
},
{
"name": "129"
},
{
"name": "164"
},
{
"name": "172"
},
{
"name": "194"
},
{
"name": "208"
},
{
"name": "213"
},
{
"name": "214"
},
{
"name": "218"
},
{
"name": "224"
},
{
"name": "229"
},
{
"name": "242"
},
{
"name": "244"
},
{
"name": "250"
},
{
"name": "252"
},
{
"name": "255"
},
{
"name": "273"
},
{
"name": "276"
},
{
"name": "278"
},
{
"name": "279"
},
{
"name": "299"
},
{
"name": "339"
},
{
"name": "340"
},
{
"name": "359"
},
{
"name": "414"
},
{
"name": "441"
},
{
"name": "519"
},
{
"name": "196"
},
{
"name": "346"
},
{
"name": "496"
},
{
"name": "94"
},
{
"name": "221"
},
{
"name": "238"
},
{
"name": "288"
},
{
"name": "300"
},
{
"name": "316"
},
{
"name": "317"
},
{
"name": "488"
},
{
"name": "503"
},
{
"name": "520"
},
{
"name": "541"
},
{
"name": "99"
},
{
"name": "152"
},
{
"name": "179"
},
{
"name": "412"
},
{
"name": "286"
},
{
"name": "403"
},
{
"name": "107"
},
{
"name": "558"
},
{
"name": "253"
},
{
"name": "106"
},
{
"name": "230"
},
{
"name": "454"
},
{
"name": "103"
},
{
"name": "190"
},
{
"name": "191"
},
{
"name": "499"
},
{
"name": "535"
},
{
"name": "576"
},
{
"name": "158"
},
{
"name": "173"
},
{
"name": "295"
},
{
"name": "448"
},
{
"name": "127"
},
{
"name": "131"
},
{
"name": "169"
},
{
"name": "181"
},
{
"name": "389"
},
{
"name": "187"
},
{
"name": "303"
},
{
"name": "355"
},
{
"name": "395"
},
{
"name": "275"
},
{
"name": "497"
},
{
"name": "170"
},
{
"name": "183"
},
{
"name": "380"
},
{
"name": "366"
},
{
"name": "408"
},
{
"name": "526"
},
{
"name": "182"
},
{
"name": "388"
},
{
"name": "409"
},
{
"name": "154"
},
{
"name": "236"
},
{
"name": "532"
},
{
"name": "580"
},
{
"name": "581"
},
{
"name": "585"
},
{
"name": "600"
},
{
"name": "452"
},
{
"name": "530"
},
{
"name": "515"
},
{
"name": "445"
},
{
"name": "201"
},
{
"name": "197"
},
{
"name": "262"
},
{
"name": "394"
},
{
"name": "443"
},
{
"name": "457"
},
{
"name": "472"
},
{
"name": "501"
},
{
"name": "544"
},
{
"name": "559"
},
{
"name": "586"
},
{
"name": "211"
},
{
"name": "237"
},
{
"name": "289"
},
{
"name": "292"
},
{
"name": "321"
},
{
"name": "347"
},
{
"name": "377"
},
{
"name": "205"
},
{
"name": "233"
},
{
"name": "307"
},
{
"name": "216"
},
{
"name": "432"
},
{
"name": "495"
},
{
"name": "290"
},
{
"name": "434"
},
{
"name": "507"
},
{
"name": "259"
},
{
"name": "514"
},
{
"name": "220"
},
{
"name": "578"
},
{
"name": "223"
},
{
"name": "590"
},
{
"name": "348"
},
{
"name": "406"
},
{
"name": "402"
},
{
"name": "245"
},
{
"name": "291"
},
{
"name": "323"
},
{
"name": "349"
},
{
"name": "367"
},
{
"name": "287"
},
{
"name": "428"
},
{
"name": "469"
},
{
"name": "333"
},
{
"name": "400"
},
{
"name": "260"
},
{
"name": "562"
},
{
"name": "264"
},
{
"name": "357"
},
{
"name": "475"
},
{
"name": "277"
},
{
"name": "502"
},
{
"name": "534"
},
{
"name": "327"
},
{
"name": "281"
},
{
"name": "305"
},
{
"name": "306"
},
{
"name": "479"
},
{
"name": "508"
},
{
"name": "360"
},
{
"name": "531"
},
{
"name": "529"
},
{
"name": "437"
},
{
"name": "302"
},
{
"name": "485"
},
{
"name": "382"
},
{
"name": "315"
},
{
"name": "329"
},
{
"name": "379"
},
{
"name": "331"
},
{
"name": "332"
},
{
"name": "334"
},
{
"name": "335"
},
{
"name": "336"
},
{
"name": "337"
},
{
"name": "338"
},
{
"name": "341"
},
{
"name": "343"
},
{
"name": "344"
},
{
"name": "398"
},
{
"name": "589"
},
{
"name": "433"
},
{
"name": "370"
},
{
"name": "524"
},
{
"name": "362"
},
{
"name": "450"
},
{
"name": "364"
},
{
"name": "369"
},
{
"name": "385"
},
{
"name": "525"
},
{
"name": "373"
},
{
"name": "375"
},
{
"name": "376"
},
{
"name": "459"
},
{
"name": "381"
},
{
"name": "482"
},
{
"name": "517"
},
{
"name": "596"
},
{
"name": "391"
},
{
"name": "561"
},
{
"name": "392"
},
{
"name": "430"
},
{
"name": "557"
},
{
"name": "560"
},
{
"name": "542"
},
{
"name": "477"
},
{
"name": "407"
},
{
"name": "470"
},
{
"name": "471"
},
{
"name": "413"
},
{
"name": "572"
},
{
"name": "420"
},
{
"name": "422"
},
{
"name": "509"
},
{
"name": "463"
},
{
"name": "478"
},
{
"name": "426"
},
{
"name": "595"
},
{
"name": "444"
},
{
"name": "462"
},
{
"name": "456"
},
{
"name": "575"
},
{
"name": "577"
},
{
"name": "461"
},
{
"name": "474"
},
{
"name": "552"
},
{
"name": "566"
},
{
"name": "483"
},
{
"name": "487"
},
{
"name": "543"
},
{
"name": "597"
},
{
"name": "554"
},
{
"name": "504"
},
{
"name": "510"
},
{
"name": "512"
},
{
"name": "513"
},
{
"name": "549"
},
{
"name": "555"
},
{
"name": "556"
},
{
"name": "536"
},
{
"name": "587"
},
{
"name": "551"
},
{
"name": "553"
},
{
"name": "564"
},
{
"name": "570"
},
{
"name": "598"
},
{
"name": "565"
},
{
"name": "583"
},
{
"name": "571"
},
{
"name": "573"
},
{
"name": "574"
},
{
"name": "582"
},
{
"name": "588"
},
{
"name": "599"
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment