Skip to content

Instantly share code, notes, and snippets.

@tgk
Created July 20, 2013 07:56
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save tgk/6044254 to your computer and use it in GitHub Desktop.
Demographic treemap with tooltip

Demographics treemap with population distribution and revenue obtained from that demographic associated with each node. The (fictitious) demographics data is loaded from a CSV file, whereas the revenue data is randomly generated.

Group Group description Type Type description Percentage
A The Rich A1 Privileged 3
A The Rich A2 Entrepaneurs 7
A The Rich A3 Empty Nesters 10
B Upper Class Families B4 Well-established Volvo 23
B Upper Class Families B5 Optimist 7
C Suburban C6 Small Business 5
C Suburban C7 Never Left Home 15
E Students D8 Students 12
F Troubled E9 Borderline Beneficiaries 8
F Welfare F10 Families on Benefits 10
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<style>
form {
position: absolute;
left: 20px;
top: 10px;
}
.node {
border: solid 2px white;
position: absolute;
}
#tooltip {
position: absolute;
width: 220px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
</style>
<form>
<label><input type="radio" name="mode" value="population" checked> Population distribution</label>
<label><input type="radio" name="mode" value="revenue"> Revenue</label>
</form>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = { top: 40, right: 10, bottom: 10, left: 10 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var percentage = function(d) { return d["percentage"]; };
var revenue = function(d) { return d["revenue"]; };
var colorScale = d3.scale.category20c();
var color = function(d) { return d.children ? colorScale(d["Group"]) : null; }
var treemap = d3.layout.treemap()
.size([width, height])
.sticky(true)
.value(percentage);
var div = d3.select("body").append("div")
.style("position", "relative")
.style("width", (width + margin.left + margin.right) + "px")
.style("height", (height + margin.top + margin.bottom) + "px")
.style("left", margin.left + "px")
.style("top", margin.top + "px");
var mousemove = function(d) {
var xPosition = d3.event.pageX + 5;
var yPosition = d3.event.pageY + 5;
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px");
d3.select("#tooltip #heading")
.text(d["demographics"]["Group"] + " - " + d["demographics"]["Group description"]);
d3.select("#tooltip #percentage")
.text(d["demographics"]["Type description"] + "\n" + d["percentage"] + "%");
d3.select("#tooltip #revenue")
.text("£" + d["revenue"].toFixed(0));
d3.select("#tooltip").classed("hidden", false);
};
var mouseout = function() {
d3.select("#tooltip").classed("hidden", true);
};
var randomRevenue = function(data) {
var rows = [];
data.forEach(function(d){
if (Math.random() < 0.75) {
rows.push({"Group": d["Group"],
"Type": d["Type"],
"Revenue": Math.random() * 100});
}
});
return rows;
}
var groupData = function(groupings, data, key){
data.forEach(function(d){
if(!groupings[d["Group"]]) {
groupings[d["Group"]] = {};
}
if(!groupings[d["Group"]][d["Type"]]) {
groupings[d["Group"]][d["Type"]] = {};
}
groupings[d["Group"]][d["Type"]][key] = d;
});
}
d3.csv("demographics.csv", function(error, data) {
var revenueData = randomRevenue(data);
var groupings = {}
groupData(groupings, data, "demographics");
groupData(groupings, revenueData, "revenue");
var trees = [];
for(group in groupings){
var children = [];
for(type in groupings[group]){
var elm = groupings[group][type];
elm["percentage"] = elm["demographics"]["Percentage"];
elm["revenue"] = elm["revenue"]
? parseFloat(elm["revenue"]["Revenue"])
: 0.0;
children.push(elm);
}
trees.push({"children": children,
"Group": group});
}
var root = {"children": trees};
var node = div.datum(root).selectAll(".node")
.data(treemap.nodes)
.enter().append("div")
.attr("class", "node")
.call(position)
.style("background", color)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
d3.selectAll("input").on("change", function change() {
var value = this.value === "population" ? percentage : revenue;
node
.data(treemap.value(value).nodes)
.transition()
.duration(1500)
.call(position);
});
});
function position() {
this.style("left", function(d) { return d.x + "px"; })
.style("top", function(d) { return d.y + "px"; })
.style("width", function(d) { return Math.max(0, d.dx - 2) + "px"; })
.style("height", function(d) { return Math.max(0, d.dy - 2) + "px"; });
}
</script>
<div id="tooltip" class="hidden">
<p><strong id="heading"></strong></p>
<p><span id="percentage"></span></p>
<p><span id="revenue"></span></p>
</div>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment