Last active
April 11, 2018 00:06
-
-
Save johnkiernander/6449830 to your computer and use it in GitHub Desktop.
Horizontal Waterfall
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
<div id="chartContainer"> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://dimplejs.org/dist/dimple.v1.js"></script> | |
<script type="text/javascript"> | |
// This code should support any data in this structure changing the data here | |
// should be dealt with by the the code below. The y axis min and max must | |
// be able to contain the data required or the results will be incorrect. | |
// Also the "bar" elements must have the correct value (sum of previous "bar" | |
// + intervening "var" elements) | |
var xMin = -400000, | |
xMax = 400000, | |
yLabel = "Bar", | |
xLabel = "Value"; | |
data = [ | |
{ "label":"2011", "value":200000, "type":"bar" }, | |
{ "label":"Alpha", "value":-100000, "type":"var" }, | |
{ "label":"Beta", "value":200000, "type":"var" }, | |
{ "label":"Delta", "value":-500000, "type":"var" }, | |
{ "label":"Gamma", "value":-100000, "type":"var" }, | |
{ "label":"2012", "value":-300000, "type":"bar" }, | |
{ "label":"Alpha", "value":150000, "type":"var" }, | |
{ "label":"Beta", "value":100000, "type":"var" }, | |
{ "label":"Delta", "value":-35000, "type":"var" }, | |
{ "label":"Gamma", "value":205000, "type":"var" }, | |
{ "label":"2013", "value":120000, "type":"bar" } | |
]; | |
// Create the svg as usual | |
var svg = dimple.newSvg("#chartContainer", 590, 400); | |
// The waterfall requires some data manipulation in a similar | |
// way that one would create a waterfall in Excel. The data | |
// will be padded and rearranged for display | |
var waterfallData = []; | |
// Maintain a running total for padding | |
var runningTotal = 0; | |
// Calculate the base level of padding, this is to deal with | |
// waterfalls which cross the x-axis | |
var padBase = Math.abs(xMin); | |
// Rearrange the data | |
data.forEach(function (d, i) { | |
// Add the padding bar, this will be removed later but is used | |
// for positioning | |
waterfallData.push({ | |
y: i, | |
x: padBase | |
+ (d.type === "var" ? runningTotal : 0) | |
+ (d.value < 0 ? d.value : 0), | |
series: "pad" }); | |
// Add the bar itself. The values are always positive here. The labels in | |
// popups will be changed later. | |
waterfallData.push({ | |
y: i, | |
x: Math.abs(d.value), | |
series: (d.type === "bar" ? | |
"bar" : (d.value < 0 ? | |
"negative" : "positive")) }); | |
// Move the running total for managing the flow | |
runningTotal = (d.type === "var" ? runningTotal : 0) + d.value; | |
}); | |
// Create the chart from the updated data | |
var myChart = new dimple.chart(svg, waterfallData); | |
myChart.setBounds(60, 30, 510, 335) | |
var y = myChart.addCategoryAxis("y", "y"); | |
// By default bar charts are ordered by value, this forces the ordering by | |
// name (which at this point includes a leading string for positioning) | |
y.addOrderRule("y"); | |
// We need 2 x axes. The bars will be positioned using | |
// the second axis (all positive) and the first axis | |
// will be used to draw the visible axis, In an all positive waterfall | |
// both axes will be the same but for a negative the x2 axis will go from 0 | |
// to abs(xMin) + xMax to allow axis crossing | |
var x1 = myChart.addMeasureAxis("x", xLabel); | |
x1.overrideMin = padBase * -1; | |
x1.overrideMax = xMax; | |
var x2 = myChart.addMeasureAxis("x", "x"); | |
x2.overrideMin = 0; | |
x2.overrideMax = padBase + xMax; | |
x2.hidden = true; | |
// Add the series | |
var s = myChart.addSeries(["series"], dimple.plot.bar, [y, x2]); | |
// Assign blue, red and green using the default colors, these can | |
// be any colors. We could assign transparency to the "pad" bars but | |
// to avoid tooltips we delete the bars after drawing instead | |
myChart.assignColor("bar", myChart.defaultColors[0].fill); | |
myChart.assignColor("negative", myChart.defaultColors[1].fill); | |
myChart.assignColor("positive", myChart.defaultColors[3].fill); | |
// Draw the chart. Code beyond this point manipulates the objects | |
// and hacks some changes to the chart which would break in the event | |
// of redrawing. | |
myChart.draw(); | |
// Remove the axis titles here | |
y.titleShape.remove(); | |
x1.titleShape.remove(); | |
// Remove the leading numbers from the y axis text | |
y.shapes.selectAll("text").text(function (d) { | |
return (d === "" ? "" : data[parseInt(d)].label); | |
}); | |
// Remove the padding elements entirely | |
svg.selectAll(".pad").remove(); | |
// Change the measure name and the category names for the tooltips | |
s.x.measure = xLabel; | |
s.y.categoryFields = [yLabel]; | |
// Remove the category fields from the series, to remove from the tooltips | |
s.categoryFields = []; | |
s.shapes.each(function (d) { | |
// Get the index from the original data | |
var j = parseInt(d.yField[0]); | |
// Remove the unwanted y label prefixes from the shapes for the tooltips | |
d.yField[0] = data[j].label; | |
// Change the values back to negatives where necessary for tooltips | |
d.width = data[j].value; | |
}); | |
</script> | |
</div> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment