A recreation of the waterfall chart example in Wikipedia. This type of chart is popularized by McKinsey & Company in explaining the cumulative impact of various factors on some quantitative value.
Last active
November 7, 2023 05:48
-
-
Save chucklam/f3c7b3e3709a0afd5d57 to your computer and use it in GitHub Desktop.
Waterfall Chart
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
name | value | |
---|---|---|
Product Revenue | 420000 | |
Services Revenue | 210000 | |
Fixed Costs | -170000 | |
Variable Costs | -140000 |
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
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
.bar.total rect { | |
fill: steelblue; | |
} | |
.bar.positive rect { | |
fill: darkolivegreen; | |
} | |
.bar.negative rect { | |
fill: crimson; | |
} | |
.bar line.connector { | |
stroke: grey; | |
stroke-dasharray: 3; | |
} | |
.bar text { | |
fill: white; | |
font: 12px sans-serif; | |
text-anchor: middle; | |
} | |
.axis text { | |
font: 10px sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
</style> | |
<svg class="chart"></svg> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var margin = {top: 20, right: 30, bottom: 30, left: 40}, | |
width = 960 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom, | |
padding = 0.3; | |
var x = d3.scale.ordinal() | |
.rangeRoundBands([0, width], padding); | |
var y = d3.scale.linear() | |
.range([height, 0]); | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient("bottom"); | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.orient("left") | |
.tickFormat(function(d) { return dollarFormatter(d); }); | |
var chart = d3.select(".chart") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
d3.csv("data.csv", type, function(error, data) { | |
// Transform data (i.e., finding cumulative values and total) for easier charting | |
var cumulative = 0; | |
for (var i = 0; i < data.length; i++) { | |
data[i].start = cumulative; | |
cumulative += data[i].value; | |
data[i].end = cumulative; | |
data[i].class = ( data[i].value >= 0 ) ? 'positive' : 'negative' | |
} | |
data.push({ | |
name: 'Total', | |
end: cumulative, | |
start: 0, | |
class: 'total' | |
}); | |
x.domain(data.map(function(d) { return d.name; })); | |
y.domain([0, d3.max(data, function(d) { return d.end; })]); | |
chart.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis); | |
chart.append("g") | |
.attr("class", "y axis") | |
.call(yAxis); | |
var bar = chart.selectAll(".bar") | |
.data(data) | |
.enter().append("g") | |
.attr("class", function(d) { return "bar " + d.class }) | |
.attr("transform", function(d) { return "translate(" + x(d.name) + ",0)"; }); | |
bar.append("rect") | |
.attr("y", function(d) { return y( Math.max(d.start, d.end) ); }) | |
.attr("height", function(d) { return Math.abs( y(d.start) - y(d.end) ); }) | |
.attr("width", x.rangeBand()); | |
bar.append("text") | |
.attr("x", x.rangeBand() / 2) | |
.attr("y", function(d) { return y(d.end) + 5; }) | |
.attr("dy", function(d) { return ((d.class=='negative') ? '-' : '') + ".75em" }) | |
.text(function(d) { return dollarFormatter(d.end - d.start);}); | |
bar.filter(function(d) { return d.class != "total" }).append("line") | |
.attr("class", "connector") | |
.attr("x1", x.rangeBand() + 5 ) | |
.attr("y1", function(d) { return y(d.end) } ) | |
.attr("x2", x.rangeBand() / ( 1 - padding) - 5 ) | |
.attr("y2", function(d) { return y(d.end) } ) | |
}); | |
function type(d) { | |
d.value = +d.value; | |
return d; | |
} | |
function dollarFormatter(n) { | |
n = Math.round(n); | |
var result = n; | |
if (Math.abs(n) > 1000) { | |
result = Math.round(n/1000) + 'K'; | |
} | |
return '$' + result; | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How can I reverse the x-axis and y-axis in waterfall chart