Skip to content

Instantly share code, notes, and snippets.

@rsloan
Last active April 10, 2019 08:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rsloan/7123450 to your computer and use it in GitHub Desktop.
Save rsloan/7123450 to your computer and use it in GitHub Desktop.
d3 Waterfall Chart

A simple example of a waterfall chart using d3, showing customer retention on a week by week basis for the past month.

period start of period new returned went inactive
Four Weeks Ago 100 20 30 40
Three Weeks Ago 110 30 20 30
Two Weeks Ago 130 40 25 40
One Week Ago 155 30 55 50
Current Week 190 0 0 0
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], 0, .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#2DBAD4", "#33CC33", "#00E6B8", "#C9081D"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
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 + ")");
d3.csv("data.csv", function(error, data) {
var categories = d3.keys(data[0]).filter(function(key) {
return key !== "period";
});
data.forEach(function(d) {
//first calculate the starting height for each category
total = 0
totals = {}
for (var i=0; i<categories.length; i++) {
if (i == 3) {
total = total - +d[categories[i]];
totals[categories[i]] = total;
}
else {
totals[categories[i]] = total;
total += +d[categories[i]];
}
}
//then map category name, value, and starting height to each observation
d.formatted = categories.map(function(category) {
return {
name: category,
value: +d[category],
baseHeight: +totals[category]
};
});
});
x0.domain(data.map(function(d) { return d.period; }));
x1.domain(categories).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.formatted, function(d) {
return d.value + d.baseHeight; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Customers");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) {
return "translate(" + x0(d.period) + ",0)";
});
state.selectAll("rect")
.data(function(d) { return d.formatted; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value + d.baseHeight); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(categories.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
</script>
@jacoduplessis
Copy link

Thanks for this. If anyone is looking for a v5 compatible example: https://gist.github.com/jacoduplessis/29209d40299e13d9a1aa09ce0eecf02f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment