Adapts the "Stacked to multiples" chart (http://bl.ocks.org/mbostock/4679202)
This chart shows how small changes in sub-categories affect the total value.
Adapts the "Stacked to multiples" chart (http://bl.ocks.org/mbostock/4679202)
This chart shows how small changes in sub-categories affect the total value.
location | capabilitygroup | capability | assessmentdate | totalpeople | totalyears | |
---|---|---|---|---|---|---|
Auckland | BA Methodologies | Planning and Monitoring | Q1 | 13 | 20 | |
Auckland | BA Methodologies | Elicitation | Q1 | 13 | 25 | |
Auckland | BA Methodologies | Requirements Management and Communication | Q1 | 13 | 20 | |
Auckland | BA Methodologies | Enterprise Analysis | Q1 | 11 | 30 | |
Auckland | BA Methodologies | Requirements Analysis | Q1 | 13 | 10 | |
Auckland | BA Methodologies | Solution Assessment and Validation | Q1 | 11 | 31 | |
Auckland | BA Methodologies | Iterative e.g. RUP | Q1 | 13 | 41 | |
Auckland | BA Methodologies | Agile - Scrum | Q1 | 11 | 20 | |
Auckland | BA Methodologies | BA Practise Mgmt | Q1 | 7 | 15 | |
Auckland | BA Methodologies | Kan Ban Lean Six Sigma Kaizan | Q1 | 10 | 25 | |
Auckland | BA Methodologies | Planning and Monitoring | Q2 | 13 | 44 | |
Auckland | BA Methodologies | Elicitation | Q2 | 13 | 19 | |
Auckland | BA Methodologies | Requirements Management and Communication | Q2 | 13 | 36 | |
Auckland | BA Methodologies | Enterprise Analysis | Q2 | 11 | 30 | |
Auckland | BA Methodologies | Requirements Analysis | Q2 | 13 | 42 | |
Auckland | BA Methodologies | Solution Assessment and Validation | Q2 | 11 | 31 | |
Auckland | BA Methodologies | Iterative e.g. RUP | Q2 | 13 | 35 | |
Auckland | BA Methodologies | Agile - Scrum | Q2 | 11 | 20 | |
Auckland | BA Methodologies | BA Practise Mgmt | Q2 | 7 | 15 | |
Auckland | BA Methodologies | Kan Ban Lean Six Sigma Kaizan | Q2 | 10 | 20 | |
Auckland | BA Methodologies | Planning and Monitoring | Q3 | 13 | 42 | |
Auckland | BA Methodologies | Elicitation | Q3 | 13 | 38 | |
Auckland | BA Methodologies | Requirements Management and Communication | Q3 | 13 | 39 | |
Auckland | BA Methodologies | Enterprise Analysis | Q3 | 12 | 33 | |
Auckland | BA Methodologies | Requirements Analysis | Q3 | 13 | 39 | |
Auckland | BA Methodologies | Solution Assessment and Validation | Q3 | 12 | 34 | |
Auckland | BA Methodologies | Iterative e.g. RUP | Q3 | 13 | 24 | |
Auckland | BA Methodologies | Agile - Scrum | Q3 | 11 | 20 | |
Auckland | BA Methodologies | BA Practise Mgmt | Q3 | 7 | 15 | |
Auckland | BA Methodologies | Kan Ban Lean Six Sigma Kaizan | Q3 | 11 | 22 | |
Auckland | BA Methodologies | Planning and Monitoring | Q4 | 11 | 39 | |
Auckland | BA Methodologies | Elicitation | Q4 | 11 | 37 | |
Auckland | BA Methodologies | Requirements Management and Communication | Q4 | 11 | 36 | |
Auckland | BA Methodologies | Enterprise Analysis | Q4 | 11 | 30 | |
Auckland | BA Methodologies | Requirements Analysis | Q4 | 11 | 36 | |
Auckland | BA Methodologies | Solution Assessment and Validation | Q4 | 11 | 31 | |
Auckland | BA Methodologies | Iterative e.g. RUP | Q4 | 11 | 25 | |
Auckland | BA Methodologies | Agile - Scrum | Q4 | 11 | 20 | |
Auckland | BA Methodologies | BA Practise Mgmt | Q4 | 11 | 26 | |
Auckland | BA Methodologies | Kan Ban Lean Six Sigma Kaizan | Q4 | 11 | 22 |
// d3.tip | |
// Copyright (c) 2013 Justin Palmer | |
// | |
// Tooltips for d3.js SVG visualizations | |
(function (root, factory) { | |
if (typeof define === 'function' && define.amd) { | |
// AMD. Register as an anonymous module with d3 as a dependency. | |
define(['d3'], factory) | |
} else { | |
// Browser global. | |
root.d3.tip = factory(root.d3) | |
} | |
}(this, function (d3) { | |
// Public - contructs a new tooltip | |
// | |
// Returns a tip | |
return function() { | |
var direction = d3_tip_direction, | |
offset = d3_tip_offset, | |
html = d3_tip_html, | |
node = initNode(), | |
svg = null, | |
point = null, | |
target = null | |
function tip(vis) { | |
svg = getSVGNode(vis) | |
point = svg.createSVGPoint() | |
document.body.appendChild(node) | |
} | |
// Public - show the tooltip on the screen | |
// | |
// Returns a tip | |
tip.show = function() { | |
var args = Array.prototype.slice.call(arguments) | |
if(args[args.length - 1] instanceof SVGElement) target = args.pop() | |
var content = html.apply(this, args), | |
poffset = offset.apply(this, args), | |
dir = direction.apply(this, args), | |
nodel = d3.select(node), | |
i = directions.length, | |
coords, | |
scrollTop = document.documentElement.scrollTop || document.body.scrollTop, | |
scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft | |
nodel.html(content) | |
.style({ opacity: 1, 'pointer-events': 'all' }) | |
while(i--) nodel.classed(directions[i], false) | |
coords = direction_callbacks.get(dir).apply(this) | |
nodel.classed(dir, true).style({ | |
top: (coords.top + poffset[0]) + scrollTop + 'px', | |
left: (coords.left + poffset[1]) + scrollLeft + 'px' | |
}) | |
return tip | |
} | |
// Public - hide the tooltip | |
// | |
// Returns a tip | |
tip.hide = function() { | |
nodel = d3.select(node) | |
nodel.style({ opacity: 0, 'pointer-events': 'none' }) | |
return tip | |
} | |
// Public: Proxy attr calls to the d3 tip container. Sets or gets attribute value. | |
// | |
// n - name of the attribute | |
// v - value of the attribute | |
// | |
// Returns tip or attribute value | |
tip.attr = function(n, v) { | |
if (arguments.length < 2 && typeof n === 'string') { | |
return d3.select(node).attr(n) | |
} else { | |
var args = Array.prototype.slice.call(arguments) | |
d3.selection.prototype.attr.apply(d3.select(node), args) | |
} | |
return tip | |
} | |
// Public: Proxy style calls to the d3 tip container. Sets or gets a style value. | |
// | |
// n - name of the property | |
// v - value of the property | |
// | |
// Returns tip or style property value | |
tip.style = function(n, v) { | |
if (arguments.length < 2 && typeof n === 'string') { | |
return d3.select(node).style(n) | |
} else { | |
var args = Array.prototype.slice.call(arguments) | |
d3.selection.prototype.style.apply(d3.select(node), args) | |
} | |
return tip | |
} | |
// Public: Set or get the direction of the tooltip | |
// | |
// v - One of n(north), s(south), e(east), or w(west), nw(northwest), | |
// sw(southwest), ne(northeast) or se(southeast) | |
// | |
// Returns tip or direction | |
tip.direction = function(v) { | |
if (!arguments.length) return direction | |
direction = v == null ? v : d3.functor(v) | |
return tip | |
} | |
// Public: Sets or gets the offset of the tip | |
// | |
// v - Array of [x, y] offset | |
// | |
// Returns offset or | |
tip.offset = function(v) { | |
if (!arguments.length) return offset | |
offset = v == null ? v : d3.functor(v) | |
return tip | |
} | |
// Public: sets or gets the html value of the tooltip | |
// | |
// v - String value of the tip | |
// | |
// Returns html value or tip | |
tip.html = function(v) { | |
if (!arguments.length) return html | |
html = v == null ? v : d3.functor(v) | |
return tip | |
} | |
function d3_tip_direction() { return 'n' } | |
function d3_tip_offset() { return [0, 0] } | |
function d3_tip_html() { return ' ' } | |
var direction_callbacks = d3.map({ | |
n: direction_n, | |
s: direction_s, | |
e: direction_e, | |
w: direction_w, | |
nw: direction_nw, | |
ne: direction_ne, | |
sw: direction_sw, | |
se: direction_se | |
}), | |
directions = direction_callbacks.keys() | |
function direction_n() { | |
var bbox = getScreenBBox() | |
return { | |
top: bbox.n.y - node.offsetHeight, | |
left: bbox.n.x - node.offsetWidth / 2 | |
} | |
} | |
function direction_s() { | |
var bbox = getScreenBBox() | |
return { | |
top: bbox.s.y, | |
left: bbox.s.x - node.offsetWidth / 2 | |
} | |
} | |
function direction_e() { | |
var bbox = getScreenBBox() | |
return { | |
top: bbox.e.y - node.offsetHeight / 2, | |
left: bbox.e.x | |
} | |
} | |
function direction_w() { | |
var bbox = getScreenBBox() | |
return { | |
top: bbox.w.y - node.offsetHeight / 2, | |
left: bbox.w.x - node.offsetWidth | |
} | |
} | |
function direction_nw() { | |
var bbox = getScreenBBox() | |
return { | |
top: bbox.nw.y - node.offsetHeight, | |
left: bbox.nw.x - node.offsetWidth | |
} | |
} | |
function direction_ne() { | |
var bbox = getScreenBBox() | |
return { | |
top: bbox.ne.y - node.offsetHeight, | |
left: bbox.ne.x | |
} | |
} | |
function direction_sw() { | |
var bbox = getScreenBBox() | |
return { | |
top: bbox.sw.y, | |
left: bbox.sw.x - node.offsetWidth | |
} | |
} | |
function direction_se() { | |
var bbox = getScreenBBox() | |
return { | |
top: bbox.se.y, | |
left: bbox.e.x | |
} | |
} | |
function initNode() { | |
var node = d3.select(document.createElement('div')) | |
node.style({ | |
position: 'absolute', | |
top: 0, | |
opacity: 0, | |
'pointer-events': 'none', | |
'box-sizing': 'border-box' | |
}) | |
return node.node() | |
} | |
function getSVGNode(el) { | |
el = el.node() | |
if(el.tagName.toLowerCase() == 'svg') | |
return el | |
return el.ownerSVGElement | |
} | |
// Private - gets the screen coordinates of a shape | |
// | |
// Given a shape on the screen, will return an SVGPoint for the directions | |
// n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest), | |
// sw(southwest). | |
// | |
// +-+-+ | |
// | | | |
// + + | |
// | | | |
// +-+-+ | |
// | |
// Returns an Object {n, s, e, w, nw, sw, ne, se} | |
function getScreenBBox() { | |
var targetel = target || d3.event.target, | |
bbox = {}, | |
matrix = targetel.getScreenCTM(), | |
tbbox = targetel.getBBox(), | |
width = tbbox.width, | |
height = tbbox.height, | |
x = tbbox.x, | |
y = tbbox.y | |
point.x = x | |
point.y = y | |
bbox.nw = point.matrixTransform(matrix) | |
point.x += width | |
bbox.ne = point.matrixTransform(matrix) | |
point.y += height | |
bbox.se = point.matrixTransform(matrix) | |
point.x -= width | |
bbox.sw = point.matrixTransform(matrix) | |
point.y -= height / 2 | |
bbox.w = point.matrixTransform(matrix) | |
point.x += width | |
bbox.e = point.matrixTransform(matrix) | |
point.x -= width / 2 | |
point.y -= height / 2 | |
bbox.n = point.matrixTransform(matrix) | |
point.y += height | |
bbox.s = point.matrixTransform(matrix) | |
return bbox | |
} | |
return tip | |
}; | |
})); |
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<link type="text/css" rel="stylesheet" href="tooltip-styles-stackedchart.css"> | |
<style> | |
body { | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
color: white; | |
background-color: black; | |
margin: auto; | |
position: relative; | |
width: 960px; | |
} | |
text { | |
font: 10px sans-serif; | |
fill: white; | |
} | |
.axis path { | |
display: none; | |
} | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.group-label { | |
font-weight: bold; | |
text-anchor: end; | |
} | |
form { | |
position: absolute; | |
right: 10px; | |
top: 10px; | |
} | |
label { | |
top: 10px; | |
right: 0px; | |
} | |
</style> | |
<form> | |
<label><input type="radio" name="mode" value="multiples" checked> Multiples</label> | |
<label><input type="radio" name="mode" value="stacked"> Stacked</label> | |
</form> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="d3-tip.js"></script> | |
<script> | |
var margin = {top: 10, right: 20, bottom: 20, left: 240}, | |
width = 960 - margin.left - margin.right, | |
height = 700 - margin.top - margin.bottom; | |
var y0 = d3.scale.ordinal() | |
.rangeRoundBands([height, 0], .4); | |
var y1 = d3.scale.linear(); | |
var x = d3.scale.ordinal() | |
.rangeRoundBands([0, width], .1, 0); | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient("bottom"); | |
var nest = d3.nest() | |
.key(function(d) { return d.capability; }); | |
var stack = d3.layout.stack() | |
.values(function(d) { return d.values; }) | |
.x(function(d) { return d.assessmentdate; }) | |
.y(function(d) { return d.totalyears; }) | |
.out(function(d, y0) { d.valueOffset = y0; }); | |
var color = d3.scale.ordinal(); | |
var svg = 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 + ")"); | |
var tip = d3.tip() | |
.attr('class', 'd3-tip') | |
.offset([0, -195]) | |
.html(function(d) { | |
return "<strong>Total years:</strong> " + d.totalyears; | |
}) | |
svg.call(tip); | |
d3.tsv("ba-skills.tsv", function(error, data) { | |
data.forEach(function(d) { | |
d.totalyears = +d.totalyears; | |
}); | |
var dataByGroup = nest.entries(data); | |
stack(dataByGroup); | |
x.domain(dataByGroup[0].values.map(function(d) { return d.assessmentdate; })); | |
y0.domain(dataByGroup.map(function(d) { return d.key; })); | |
y1.domain([0, d3.max(data, function(d) { return d.totalyears; })]).range([y0.rangeBand(), 0]); | |
color | |
.range(["darkred","red"]) | |
.domain([0, dataByGroup.length - 1]); | |
/*color | |
.range(d3.range(dataByGroup.length).map(d3.scale.linear() | |
.domain([0, dataByGroup.length - 1]) | |
.range(["blue", "red"]) | |
.interpolate(d3.interpolateLab)));*/ | |
/*color | |
.range(d3.range(dataByGroup.length).map(d3.scale.linear() | |
.domain([0, dataByGroup.length - 1]) | |
.range(["blue", "red"]) | |
.interpolate(d3.interpolateLab)));*/ | |
var group = svg.selectAll(".group") | |
.data(dataByGroup) | |
.enter().append("g") | |
.attr("class", "group") | |
.attr("transform", function(d) { return "translate(0," + y0(d.key) + ")"; }); | |
group.append("text") | |
.attr("class", "group-label") | |
.attr("x", -6) | |
.attr("y", function(d) { return y1(d.values[0].totalyears / 2); }) | |
.attr("dy", ".35em") | |
.text(function(d) { return d.key; }); | |
group.selectAll("rect") | |
.data(function(d) { return d.values; }) | |
.enter().append("rect") | |
.attr("class", "bar") | |
.style("fill", function(d) { return color(d.capability); }) | |
.attr("x", function(d) { return x(d.assessmentdate); }) | |
.attr("y", function(d) { return y1(d.totalyears); }) | |
.attr("width", x.rangeBand()) | |
.attr("height", function(d) { return y0.rangeBand() - y1(d.totalyears); }) | |
.on('mouseover', tip.show) | |
.on('mouseout', tip.hide);; | |
group.filter(function(d, i) { return !i; }).append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + y0.rangeBand() + ")") | |
.call(xAxis); | |
d3.selectAll("input").on("change", change); | |
/*var timeout = setTimeout(function() { | |
d3.select("input[value=\"stacked\"]").property("checked", true).each(change); | |
}, 2000);*/ | |
function change() { | |
//clearTimeout(timeout); | |
if (this.value === "multiples") transitionMultiples(); | |
else transitionStacked(); | |
} | |
function transitionMultiples() { | |
var t = svg.transition().duration(750), | |
g = t.selectAll(".group").attr("transform", function(d) { return "translate(0," + y0(d.key) + ")"; }); | |
g.selectAll("rect").attr("y", function(d) { return y1(d.totalyears); }); | |
g.select(".group-label").attr("y", function(d) { return y1(d.values[0].totalyears / 2); }) | |
} | |
function transitionStacked() { | |
var t = svg.transition().duration(750), | |
g = t.selectAll(".group").attr("transform", "translate(0," + y0(y0.domain()[0]) + ")"); | |
g.selectAll("rect").attr("y", function(d) { return y1(d.totalyears + d.valueOffset); }); | |
g.select(".group-label").attr("y", function(d) { return y1(d.values[0].totalyears / 2 + d.values[0].valueOffset); }) | |
} | |
}); | |
</script> |
/*.d3-tip { | |
line-height: 1; | |
font-weight: bold; | |
padding: 12px; | |
background: rgba(0, 0, 0, 0.8); | |
color: #fff; | |
border-radius: 2px; | |
pointer-events: none; | |
}*/ | |
.d3-tip { | |
display: block; | |
position: absolute; | |
padding: .5em; | |
content: attr(title); | |
min-width: 120px; | |
text-align: center; | |
width: auto; | |
height: auto; | |
white-space: nowrap; | |
top: -32px; | |
background: white; | |
-moz-border-radius:5px; | |
-webkit-border-radius:5px; | |
border-radius:5px; | |
color: black; | |
font-size: .86em; | |
pointer-events: none; | |
} | |
/* Eastward tooltips */ | |
.d3-tip.e:after { | |
content: "\25C0"; | |
margin: -4px 0 0 0; | |
top: 50%; | |
left: -8px; | |
} | |
/* Southward tooltips */ | |
.d3-tip.s:after { | |
content: "\25B2"; | |
margin: 0 0 1px 0; | |
top: -8px; | |
left: 0; | |
text-align: center; | |
} | |
/* Westward tooltips */ | |
.d3-tip.w:after { | |
content: "\25B6"; | |
margin: -4px 0 0 -1px; | |
top: 50%; | |
left: 100%; | |
} |