Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
D3 Interactive Streamgraph

D3 Streamgraph Example

Series Hover

The series hover interactivity uses the technique from lgrammel seen here: http://bl.ocks.org/1963983

Data Tooltip

It isn't necessarily a tooltip, but data is displayed by inverting the x-axis value into a date, and mapping the date to the corresponding data value for the series.

If there is a simpler way to do this, I'd love to see an example, as I couldn't find any other examples of this out there.

Vertical Line Tooltip

I think the vertical line adds a little bit of visual orientation to the streamgraph.

Colors

I left a few alternate color ranges in the code (which are essentially taken straight from colorbrewer), in case orange doesn't sit well.

key value date
AR 0.1 01/08/13
AR 0.15 01/09/13
AR 0.35 01/10/13
AR 0.38 01/11/13
AR 0.22 01/12/13
AR 0.16 01/13/13
AR 0.07 01/14/13
AR 0.02 01/15/13
AR 0.17 01/16/13
AR 0.33 01/17/13
AR 0.4 01/18/13
AR 0.32 01/19/13
AR 0.26 01/20/13
AR 0.35 01/21/13
AR 0.4 01/22/13
AR 0.32 01/23/13
AR 0.26 01/24/13
AR 0.22 01/25/13
AR 0.16 01/26/13
AR 0.22 01/27/13
AR 0.1 01/28/13
DJ 0.35 01/08/13
DJ 0.36 01/09/13
DJ 0.37 01/10/13
DJ 0.22 01/11/13
DJ 0.24 01/12/13
DJ 0.26 01/13/13
DJ 0.34 01/14/13
DJ 0.21 01/15/13
DJ 0.18 01/16/13
DJ 0.45 01/17/13
DJ 0.32 01/18/13
DJ 0.35 01/19/13
DJ 0.3 01/20/13
DJ 0.28 01/21/13
DJ 0.27 01/22/13
DJ 0.26 01/23/13
DJ 0.15 01/24/13
DJ 0.3 01/25/13
DJ 0.35 01/26/13
DJ 0.42 01/27/13
DJ 0.42 01/28/13
MS 0.21 01/08/13
MS 0.25 01/09/13
MS 0.27 01/10/13
MS 0.23 01/11/13
MS 0.24 01/12/13
MS 0.21 01/13/13
MS 0.35 01/14/13
MS 0.39 01/15/13
MS 0.4 01/16/13
MS 0.36 01/17/13
MS 0.33 01/18/13
MS 0.43 01/19/13
MS 0.4 01/20/13
MS 0.34 01/21/13
MS 0.28 01/22/13
MS 0.26 01/23/13
MS 0.37 01/24/13
MS 0.41 01/25/13
MS 0.46 01/26/13
MS 0.47 01/27/13
MS 0.41 01/28/13
RC 0.1 01/08/13
RC 0.15 01/09/13
RC 0.35 01/10/13
RC 0.38 01/11/13
RC 0.22 01/12/13
RC 0.16 01/13/13
RC 0.07 01/14/13
RC 0.02 01/15/13
RC 0.17 01/16/13
RC 0.33 01/17/13
RC 0.4 01/18/13
RC 0.32 01/19/13
RC 0.26 01/20/13
RC 0.35 01/21/13
RC 0.4 01/22/13
RC 0.32 01/23/13
RC 0.26 01/24/13
RC 0.22 01/25/13
RC 0.16 01/26/13
RC 0.22 01/27/13
RC 0.1 01/28/13
CG 0.1 01/08/13
CG 0.15 01/09/13
CG 0.35 01/10/13
CG 0.38 01/11/13
CG 0.22 01/12/13
CG 0.16 01/13/13
CG 0.07 01/14/13
CG 0.02 01/15/13
CG 0.17 01/16/13
CG 0.33 01/17/13
CG 0.4 01/18/13
CG 0.32 01/19/13
CG 0.26 01/20/13
CG 0.35 01/21/13
CG 0.4 01/22/13
CG 0.32 01/23/13
CG 0.26 01/24/13
CG 0.22 01/25/13
CG 0.16 01/26/13
CG 0.22 01/27/13
CG 0.1 01/28/13
RI 0.1 01/08/13
RI 0.15 01/09/13
RI 0.35 01/10/13
RI 0.38 01/11/13
RI 0.22 01/12/13
RI 0.16 01/13/13
RI 0.07 01/14/13
RI 0.02 01/15/13
RI 0.17 01/16/13
RI 0.33 01/17/13
RI 0.4 01/18/13
RI 0.32 01/19/13
RI 0.26 01/20/13
RI 0.35 01/21/13
RI 0.4 01/22/13
RI 0.32 01/23/13
RI 0.26 01/24/13
RI 0.22 01/25/13
RI 0.16 01/26/13
RI 0.22 01/27/13
RI 0.1 01/28/13
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.chart {
background: #fff;
}
p {
font: 12px helvetica;
}
.axis path, .axis line {
fill: none;
stroke: #000;
stroke-width: 2px;
shape-rendering: crispEdges;
}
button {
position: absolute;
right: 50px;
top: 10px;
}
</style>
<body>
<script src="http://d3js.org/d3.v2.js"></script>
<div class="chart">
</div>
<script>
chart("data.csv", "orange");
var datearray = [];
var colorrange = [];
function chart(csvpath, color) {
if (color == "blue") {
colorrange = ["#045A8D", "#2B8CBE", "#74A9CF", "#A6BDDB", "#D0D1E6", "#F1EEF6"];
}
else if (color == "pink") {
colorrange = ["#980043", "#DD1C77", "#DF65B0", "#C994C7", "#D4B9DA", "#F1EEF6"];
}
else if (color == "orange") {
colorrange = ["#B30000", "#E34A33", "#FC8D59", "#FDBB84", "#FDD49E", "#FEF0D9"];
}
strokecolor = colorrange[0];
var format = d3.time.format("%m/%d/%y");
var margin = {top: 20, right: 40, bottom: 30, left: 30};
var width = document.body.clientWidth - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
var tooltip = d3.select("body")
.append("div")
.attr("class", "remove")
.style("position", "absolute")
.style("z-index", "20")
.style("visibility", "hidden")
.style("top", "30px")
.style("left", "55px");
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height-10, 0]);
var z = d3.scale.ordinal()
.range(colorrange);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(d3.time.weeks);
var yAxis = d3.svg.axis()
.scale(y);
var yAxisr = d3.svg.axis()
.scale(y);
var stack = d3.layout.stack()
.offset("silhouette")
.values(function(d) { return d.values; })
.x(function(d) { return d.date; })
.y(function(d) { return d.value; });
var nest = d3.nest()
.key(function(d) { return d.key; });
var area = d3.svg.area()
.interpolate("cardinal")
.x(function(d) { return x(d.date); })
.y0(function(d) { return y(d.y0); })
.y1(function(d) { return y(d.y0 + d.y); });
var svg = d3.select(".chart").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 graph = d3.csv(csvpath, function(data) {
data.forEach(function(d) {
d.date = format.parse(d.date);
d.value = +d.value;
});
var layers = stack(nest.entries(data));
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
.style("fill", function(d, i) { return z(i); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ", 0)")
.call(yAxis.orient("right"));
svg.append("g")
.attr("class", "y axis")
.call(yAxis.orient("left"));
svg.selectAll(".layer")
.attr("opacity", 1)
.on("mouseover", function(d, i) {
svg.selectAll(".layer").transition()
.duration(250)
.attr("opacity", function(d, j) {
return j != i ? 0.6 : 1;
})})
.on("mousemove", function(d, i) {
mousex = d3.mouse(this);
mousex = mousex[0];
var invertedx = x.invert(mousex);
invertedx = invertedx.getMonth() + invertedx.getDate();
var selected = (d.values);
for (var k = 0; k < selected.length; k++) {
datearray[k] = selected[k].date
datearray[k] = datearray[k].getMonth() + datearray[k].getDate();
}
mousedate = datearray.indexOf(invertedx);
pro = d.values[mousedate].value;
d3.select(this)
.classed("hover", true)
.attr("stroke", strokecolor)
.attr("stroke-width", "0.5px"),
tooltip.html( "<p>" + d.key + "<br>" + pro + "</p>" ).style("visibility", "visible");
})
.on("mouseout", function(d, i) {
svg.selectAll(".layer")
.transition()
.duration(250)
.attr("opacity", "1");
d3.select(this)
.classed("hover", false)
.attr("stroke-width", "0px"), tooltip.html( "<p>" + d.key + "<br>" + pro + "</p>" ).style("visibility", "hidden");
})
var vertical = d3.select(".chart")
.append("div")
.attr("class", "remove")
.style("position", "absolute")
.style("z-index", "19")
.style("width", "1px")
.style("height", "380px")
.style("top", "10px")
.style("bottom", "30px")
.style("left", "0px")
.style("background", "#fff");
d3.select(".chart")
.on("mousemove", function(){
mousex = d3.mouse(this);
mousex = mousex[0] + 5;
vertical.style("left", mousex + "px" )})
.on("mouseover", function(){
mousex = d3.mouse(this);
mousex = mousex[0] + 5;
vertical.style("left", mousex + "px")});
});
}
</script>

kielni commented Jan 3, 2014

I checked in some updates here: https://gist.github.com/kielni/8239732

I changed the y axis so that it's symmetric around the center instead of increasing from bottom to top, since it's the thickness of the stream that matters, not the height.

I made the tooltip follow the mouse, since it was hard to notice the values in the top left corner.

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