Skip to content

Instantly share code, notes, and snippets.

@ihodes
Created May 5, 2014 20:21
Show Gist options
  • Save ihodes/11546494 to your computer and use it in GitHub Desktop.
Save ihodes/11546494 to your computer and use it in GitHub Desktop.
<html>
<head>
<style>
.node circle {
fill: white;
stroke: steelblue;
stroke-width: 1.5px;
}
.node {
font: 15px sans-serif;
font-weight: 600;
fill: black;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 0.5px;
crispEdges: true;
}
</style>
</head>
<body>
<ul id="pops">
</ul>
<a id="add-week">Add a week</a>
<div id="viz">
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
///////////////////
// My preciouses //
///////////////////
function reduce(f, acc, lst) {
for (var i in lst) {
acc = f(acc, lst[i]);
}
return acc;
}
function map(f, lst) {
var result = [];
for (var i in lst) {
result.push(f(lst[i]));
}
return result;
}
function mapcat(f, lst) {
var result = [];
for (var i in lst) {
result = result.concat(f(lst[i]));
}
return result;
}
////////////////////////
/// Generate VR tree ///
////////////////////////
function virality(level) {
switch (level) {
case 1:
return 0.3;
case 2:
return 0.1;
case 3:
return 0.075;
case 4:
return 0.05;
case 5:
return 0.02;
default:
return 0;
}
}
function retention(level) {
switch (level) {
case 1:
return 0.5;
case 2:
return 0.8;
case 3:
return 0.9;
case 4:
return 0.95;
case 5:
return 0.99;
default:
return 0.999;
}
}
var V = "V";
var R = "R";
var ROOT = "ROOT"
function Node(type, population, parent, level) {
var lineage = [].concat(parent.lineage);
lineage.push(parent);
return {type: type, level: level, population: population,
lineage: lineage, children: []};
}
function spawn(node) {
var lvl = node.level;
if (node.type === V || node.type === ROOT)
return [Node(R, node.population * retention(1), node, 1),
Node(V, node.population * virality(1), node, 1)];
else
return [Node(R, node.population * retention(lvl + 1), node, lvl + 1),
Node(V, node.population * virality(lvl + 1) , node, lvl + 1)];
}
function treeDepth(tree) {
var nodes = tree.children;
var depth = 0;
while (nodes && nodes[0] && nodes[0].children) {
depth = depth + 1;
nodes = nodes[0].children;
}
return depth;
}
function childrenAtDepth(tree, depth) {
var d = 0;
var nodes = [tree];
while (d < depth) {
nodes = mapcat(function(n){return n.children;}, nodes);
d = d + 1;
}
return nodes;
}
function deepestChildren(tree) {
return childrenAtDepth(tree, treeDepth(tree));
}
function populationAtDepth(tree, depth) {
var nodes = childrenAtDepth(tree, depth);
return reduce(function(acc, n){ return acc + n.population; }, 0, nodes);
}
function generateTree(depth, initialPopulation, /* ignore: */ tree) {
if (tree === undefined)
return generateTree(depth, null, initializeTree(initialPopulation));
else if (depth === 0)
return tree;
else
return generateTree(depth - 1, null, generateLevel(tree));
}
function initializeTree(initialPopulation) {
return { type: ROOT, level: 0,
children: [], lineage: [],
population: initialPopulation };
}
function generateLevel(tree) {
var children = deepestChildren(tree);
map(function(n){ return n.children = spawn(n); }, children);
return tree;
}
///////////////////////////////////////////////////
/////////// Visualization!!!11!one! //////////
///////////////////////////////////////////////////
var POP = 10000;
var data = generateTree(6, POP);
var width = height = 1200; // have to be the same due to implementation
var radius = 50;
function r(d) {
return Math.sqrt((d.population / POP) * radius * radius);
}
var cluster = d3.layout.cluster()
.size([height, width - 160]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.x, d.y]; });
var svg = d3.select("#viz").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(40,0)");
var root = data;
var nodes = cluster.nodes(root),
links = cluster.links(nodes);
var link = svg.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal);
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("mouseover", function(d) { console.log( String(d.population)); })
node.append("circle")
.attr("r", r);
node.append("text")
.attr("dx", function(d) { return d.children ? -5 - r(d) : 10; })
.attr("dy", 5)
.style("font-size", function (d) {return 10/(d.lineage.length/3)})
.style("text-anchor", function(d) { return d.children ? "end" : "start"; })
.text(function(d) { return d.type === ROOT ? ""
: d.type + String(d.level); });
for (var i = 0; i <= treeDepth(data); i++) {
d3.select("#pops")
.append("li")
.text("Population at week " + i + " is " + populationAtDepth(data, i));
}
d3.select(self.frameElement).style("height", height + "px");
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment