Last active
August 29, 2015 14:00
-
-
Save ihodes/11526917 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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(5, POP); | |
var width = height = 1200; // have to be the same due to implementation | |
var radius = 50; | |
function r(d) { | |
return d.population / POP * 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.level/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