Skip to content

Instantly share code, notes, and snippets.

@emeeks
Last active March 17, 2016 02:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emeeks/02d6acaed240ba0a504e to your computer and use it in GitHub Desktop.
Save emeeks/02d6acaed240ba0a504e to your computer and use it in GitHub Desktop.
Simple Difference Chart with d3.svg.area

A difference chart of presidential approval ratings of the Obama and Bush (43) administrations. Difference charts graphically encode the variance between two time series (typically) coloring the variance to indicate which time series has a higher value during that period. All the examples I've seen of difference charts in D3 use clipping or convoluted mechanisms to create the difference area.

Here's an example that just uses d3.svg.area and defines the y value as the maximum of the two time series points at that position and the y0 value as whichever time series you're using as the baseline. To give the chart the expected outlines, d3.svg.line is used to draw each time series on top of the difference areas.

This chart was used in my exploration of figure/ground and metastability in data visualization.

[{"o_a":48,"o_d":48,"b_a":38,"b_d":58},{"o_a":45,"o_d":51,"b_a":34,"b_d":62},{"o_a":47,"o_d":48,"b_a":35,"b_d":61},{"o_a":47,"o_d":49,"b_a":33,"b_d":63},{"o_a":44,"o_d":51,"b_a":37,"b_d":59},{"o_a":47,"o_d":49,"b_a":32,"b_d":65},{"o_a":46,"o_d":49,"b_a":36,"b_d":61},{"o_a":48,"o_d":47,"b_a":34,"b_d":63},{"o_a":46,"o_d":49,"b_a":37,"b_d":59},{"o_a":45,"o_d":49,"b_a":35,"b_d":61},{"o_a":45,"o_d":50,"b_a":38,"b_d":59},{"o_a":43,"o_d":52,"b_a":33,"b_d":62},{"o_a":43,"o_d":52,"b_a":38,"b_d":56},{"o_a":42,"o_d":53,"b_a":37,"b_d":58},{"o_a":43,"o_d":52,"b_a":37,"b_d":57},{"o_a":40,"o_d":54,"b_a":37,"b_d":59},{"o_a":42,"o_d":53,"b_a":44,"b_d":51},{"o_a":41,"o_d":54,"b_a":39,"b_d":56},{"o_a":41,"o_d":54,"b_a":42,"b_d":54},{"o_a":43,"o_d":52,"b_a":37,"b_d":59},{"o_a":42,"o_d":53,"b_a":40,"b_d":56},{"o_a":41,"o_d":53,"b_a":37,"b_d":59},{"o_a":41,"o_d":52,"b_a":40,"b_d":55},{"o_a":42,"o_d":51,"b_a":37,"b_d":60},{"o_a":41,"o_d":53,"b_a":38,"b_d":56},{"o_a":41,"o_d":53,"b_a":36,"b_d":57},{"o_a":42,"o_d":52,"b_a":33,"b_d":61},{"o_a":43,"o_d":52,"b_a":31,"b_d":65},{"o_a":42,"o_d":52,"b_a":34,"b_d":63},{"o_a":41,"o_d":53,"b_a":36,"b_d":59},{"o_a":41,"o_d":54,"b_a":37,"b_d":60},{"o_a":44,"o_d":51,"b_a":37,"b_d":59},{"o_a":44,"o_d":51,"b_a":36,"b_d":60},{"o_a":44,"o_d":51,"b_a":38,"b_d":60},{"o_a":44,"o_d":51,"b_a":39,"b_d":56},{"o_a":44,"o_d":51,"b_a":42,"b_d":55},{"o_a":44,"o_d":49,"b_a":43,"b_d":54},{"o_a":44,"o_d":51,"b_a":43,"b_d":53},{"o_a":43,"o_d":52,"b_a":43,"b_d":54},{"o_a":43,"o_d":52,"b_a":43,"b_d":53},{"o_a":43,"o_d":53,"b_a":41,"b_d":56},{"o_a":42,"o_d":54,"b_a":42,"b_d":55},{"o_a":44,"o_d":51,"b_a":43,"b_d":52},{"o_a":41,"o_d":51,"b_a":38,"b_d":57},{"o_a":42,"o_d":50,"b_a":37,"b_d":60},{"o_a":42,"o_d":50,"b_a":40,"b_d":55},{"o_a":42,"o_d":51,"b_a":41,"b_d":56},{"o_a":42,"o_d":51,"b_a":41,"b_d":56},{"o_a":40,"o_d":52,"b_a":42,"b_d":55},{"o_a":41,"o_d":51,"b_a":39,"b_d":58},{"o_a":42,"o_d":52,"b_a":45,"b_d":50},{"o_a":40,"o_d":53,"b_a":40,"b_d":58},{"o_a":41,"o_d":52,"b_a":45,"b_d":52},{"o_a":43,"o_d":50,"b_a":46,"b_d":51},{"o_a":43,"o_d":51,"b_a":45,"b_d":52},{"o_a":44,"o_d":50,"b_a":40,"b_d":56},{"o_a":43,"o_d":50,"b_a":45,"b_d":51},{"o_a":45,"o_d":47,"b_a":45,"b_d":51},{"o_a":44,"o_d":47,"b_a":44,"b_d":51},{"o_a":44,"o_d":48,"b_a":49,"b_d":48},{"o_a":45,"o_d":47,"b_a":49,"b_d":48},{"o_a":45,"o_d":47,"b_a":46,"b_d":51},{"o_a":44,"o_d":47,"b_a":45,"b_d":53},{"o_a":45,"o_d":47,"b_a":47,"b_d":51},{"o_a":46,"o_d":47,"b_a":47,"b_d":49},{"o_a":46,"o_d":47,"b_a":48,"b_d":47},{"o_a":46,"o_d":46,"b_a":46,"b_d":50},{"o_a":47,"o_d":45,"b_a":50,"b_d":45},{"o_a":47,"o_d":45,"b_a":48,"b_d":49},{"o_a":48,"o_d":44,"b_a":48,"b_d":49},{"o_a":50,"o_d":44,"b_a":50,"b_d":45},{"o_a":49,"o_d":45,"b_a":48,"b_d":48},{"o_a":50,"o_d":44,"b_a":45,"b_d":49},{"o_a":51,"o_d":43,"b_a":52,"b_d":44},{"o_a":49,"o_d":45,"b_a":52,"b_d":44},{"o_a":49,"o_d":44,"b_a":52,"b_d":45},{"o_a":48,"o_d":46,"b_a":51,"b_d":45},{"o_a":47,"o_d":45,"b_a":49,"b_d":48},{"o_a":48,"o_d":45,"b_a":57,"b_d":40},{"o_a":49,"o_d":44,"b_a":51,"b_d":46},{"o_a":51,"o_d":42,"b_a":52,"b_d":44},{"o_a":51,"o_d":43,"b_a":52,"b_d":44},{"o_a":52,"o_d":42,"b_a":49,"b_d":46},{"o_a":52,"o_d":42,"b_a":53,"b_d":44},{"o_a":50,"o_d":43,"b_a":55,"b_d":42},{"o_a":54,"o_d":41,"b_a":53,"b_d":44},{"o_a":56,"o_d":38,"b_a":48,"b_d":47},{"o_a":52,"o_d":41,"b_a":51,"b_d":46},{"o_a":51,"o_d":43,"b_a":51,"b_d":47},{"o_a":51,"o_d":41,"b_a":48,"b_d":49},{"o_a":52,"o_d":41,"b_a":47,"b_d":49},{"o_a":51,"o_d":43,"b_a":50,"b_d":48},{"o_a":50,"o_d":45,"b_a":54,"b_d":44},{"o_a":50,"o_d":44,"b_a":52,"b_d":45},{"o_a":50,"o_d":44,"b_a":52,"b_d":46},{"o_a":48,"o_d":45,"b_a":49,"b_d":47},{"o_a":50,"o_d":43,"b_a":51,"b_d":46},{"o_a":49,"o_d":45,"b_a":48,"b_d":49},{"o_a":46,"o_d":48,"b_a":49,"b_d":47},{"o_a":45,"o_d":49,"b_a":47,"b_d":49},{"o_a":45,"o_d":49,"b_a":48,"b_d":49},{"o_a":46,"o_d":48,"b_a":49,"b_d":49},{"o_a":46,"o_d":47,"b_a":47,"b_d":49},{"o_a":45,"o_d":47,"b_a":46,"b_d":51},{"o_a":47,"o_d":46,"b_a":49,"b_d":48},{"o_a":45,"o_d":48,"b_a":52,"b_d":45},{"o_a":47,"o_d":46,"b_a":52,"b_d":45},{"o_a":47,"o_d":46,"b_a":53,"b_d":44},{"o_a":47,"o_d":46,"b_a":50,"b_d":47},{"o_a":47,"o_d":47,"b_a":49,"b_d":48},{"o_a":49,"o_d":46,"b_a":51,"b_d":46},{"o_a":48,"o_d":45,"b_a":51,"b_d":46},{"o_a":47,"o_d":47,"b_a":52,"b_d":44},{"o_a":46,"o_d":46,"b_a":49,"b_d":48},{"o_a":47,"o_d":45,"b_a":53,"b_d":44},{"o_a":46,"o_d":47,"b_a":59,"b_d":38},{"o_a":46,"o_d":47,"b_a":60,"b_d":35},{"o_a":45,"o_d":48,"b_a":63,"b_d":34},{"o_a":45,"o_d":48,"b_a":56,"b_d":41},{"o_a":46,"o_d":47,"b_a":55,"b_d":43},{"o_a":45,"o_d":48,"b_a":50,"b_d":47},{"o_a":45,"o_d":46,"b_a":51,"b_d":45},{"o_a":46,"o_d":46,"b_a":54,"b_d":43},{"o_a":44,"o_d":48,"b_a":53,"b_d":42},{"o_a":42,"o_d":50,"b_a":56,"b_d":40},{"o_a":42,"o_d":50,"b_a":55,"b_d":42},{"o_a":43,"o_d":49,"b_a":50,"b_d":47},{"o_a":43,"o_d":49,"b_a":52,"b_d":43},{"o_a":43,"o_d":50,"b_a":59,"b_d":37},{"o_a":43,"o_d":50,"b_a":60,"b_d":36},{"o_a":41,"o_d":51,"b_a":58,"b_d":38},{"o_a":40,"o_d":52,"b_a":59,"b_d":38},{"o_a":41,"o_d":51,"b_a":62,"b_d":34},{"o_a":41,"o_d":51,"b_a":61,"b_d":36},{"o_a":43,"o_d":49,"b_a":63,"b_d":33},{"o_a":40,"o_d":52,"b_a":62,"b_d":34},{"o_a":40,"o_d":52,"b_a":64,"b_d":32},{"o_a":41,"o_d":50,"b_a":66,"b_d":30},{"o_a":42,"o_d":51,"b_a":69,"b_d":28},{"o_a":44,"o_d":48,"b_a":70,"b_d":26},{"o_a":45,"o_d":46,"b_a":71,"b_d":24},{"o_a":46,"o_d":46,"b_a":69,"b_d":26},{"o_a":45,"o_d":48,"b_a":70,"b_d":27},{"o_a":46,"o_d":46,"b_a":71,"b_d":26},{"o_a":49,"o_d":43,"b_a":69,"b_d":27},{"o_a":50,"o_d":42,"b_a":71,"b_d":25},{"o_a":48,"o_d":43,"b_a":58,"b_d":38},{"o_a":51,"o_d":40,"b_a":57,"b_d":37},{"o_a":46,"o_d":46,"b_a":57,"b_d":37},{"o_a":43,"o_d":48,"b_a":58,"b_d":37},{"o_a":45,"o_d":46,"b_a":61,"b_d":34},{"o_a":47,"o_d":45,"b_a":59,"b_d":35},{"o_a":46,"o_d":46,"b_a":61,"b_d":35},{"o_a":48,"o_d":44,"b_a":60,"b_d":36},{"o_a":47,"o_d":45,"b_a":58,"b_d":36},{"o_a":46,"o_d":46,"b_a":61,"b_d":34},{"o_a":48,"o_d":44,"b_a":58,"b_d":37},{"o_a":48,"o_d":44,"b_a":63,"b_d":32},{"o_a":48,"o_d":44,"b_a":61,"b_d":32},{"o_a":50,"o_d":42,"b_a":63,"b_d":33},{"o_a":49,"o_d":43,"b_a":63,"b_d":32},{"o_a":48,"o_d":45,"b_a":64,"b_d":29},{"o_a":45,"o_d":47,"b_a":65,"b_d":28},{"o_a":45,"o_d":47,"b_a":66,"b_d":26},{"o_a":46,"o_d":46,"b_a":68,"b_d":27},{"o_a":45,"o_d":47,"b_a":63,"b_d":29},{"o_a":45,"o_d":47,"b_a":67,"b_d":28},{"o_a":45,"o_d":48,"b_a":62,"b_d":31},{"o_a":45,"o_d":47,"b_a":67,"b_d":28},{"o_a":46,"o_d":48,"b_a":68,"b_d":26},{"o_a":46,"o_d":47,"b_a":66,"b_d":30},{"o_a":46,"o_d":47,"b_a":70,"b_d":26},{"o_a":46,"o_d":47,"b_a":66,"b_d":30},{"o_a":45,"o_d":47,"b_a":66,"b_d":29},{"o_a":43,"o_d":50,"b_a":65,"b_d":28},{"o_a":43,"o_d":49,"b_a":68,"b_d":26},{"o_a":45,"o_d":48,"b_a":71,"b_d":23},{"o_a":45,"o_d":47,"b_a":69,"b_d":26},{"o_a":46,"o_d":46,"b_a":69,"b_d":24},{"o_a":46,"o_d":47,"b_a":73,"b_d":21},{"o_a":46,"o_d":46,"b_a":76,"b_d":18},{"o_a":47,"o_d":45,"b_a":76,"b_d":19},{"o_a":46,"o_d":47,"b_a":73,"b_d":21},{"o_a":46,"o_d":46,"b_a":74,"b_d":20},{"o_a":48,"o_d":45,"b_a":74,"b_d":18},{"o_a":51,"o_d":42,"b_a":70,"b_d":23},{"o_a":50,"o_d":43,"b_a":77,"b_d":17},{"o_a":48,"o_d":45,"b_a":76,"b_d":17},{"o_a":47,"o_d":46,"b_a":76,"b_d":19},{"o_a":49,"o_d":44,"b_a":77,"b_d":20},{"o_a":49,"o_d":44,"b_a":77,"b_d":17},{"o_a":47,"o_d":45,"b_a":75,"b_d":20},{"o_a":50,"o_d":43,"b_a":76,"b_d":19},{"o_a":49,"o_d":43,"b_a":79,"b_d":17},{"o_a":52,"o_d":41,"b_a":79,"b_d":16},{"o_a":49,"o_d":45,"b_a":80,"b_d":14},{"o_a":48,"o_d":46,"b_a":77,"b_d":18},{"o_a":50,"o_d":43,"b_a":81,"b_d":14},{"o_a":51,"o_d":43,"b_a":82,"b_d":14},{"o_a":50,"o_d":44,"b_a":82,"b_d":14},{"o_a":49,"o_d":42,"b_a":84,"b_d":13},{"o_a":50,"o_d":43,"b_a":83,"b_d":13},{"o_a":49,"o_d":44,"b_a":84,"b_d":12},{"o_a":52,"o_d":40,"b_a":86,"b_d":11},{"o_a":53,"o_d":40,"b_a":86,"b_d":10},{"o_a":52,"o_d":40,"b_a":87,"b_d":8},{"o_a":52,"o_d":41,"b_a":87,"b_d":9},{"o_a":52,"o_d":40,"b_a":87,"b_d":9},{"o_a":51,"o_d":41,"b_a":88,"b_d":9},{"o_a":52,"o_d":41,"b_a":89,"b_d":8},{"o_a":53,"o_d":40,"b_a":87,"b_d":10},{"o_a":51,"o_d":42,"b_a":90,"b_d":6},{"o_a":52,"o_d":40,"b_a":86,"b_d":10},{"o_a":56,"o_d":37,"b_a":51,"b_d":39},{"o_a":54,"o_d":39,"b_a":55,"b_d":36},{"o_a":57,"o_d":37,"b_a":57,"b_d":34},{"o_a":58,"o_d":35,"b_a":57,"b_d":35},{"o_a":60,"o_d":32,"b_a":55,"b_d":35},{"o_a":59,"o_d":34,"b_a":56,"b_d":33},{"o_a":61,"o_d":32,"b_a":57,"b_d":35},{"o_a":63,"o_d":30,"b_a":52,"b_d":34},{"o_a":64,"o_d":28,"b_a":55,"b_d":33},{"o_a":65,"o_d":28,"b_a":55,"b_d":35},{"o_a":67,"o_d":27,"b_a":56,"b_d":36},{"o_a":64,"o_d":29,"b_a":56,"b_d":31},{"o_a":62,"o_d":29,"b_a":53,"b_d":33},{"o_a":61,"o_d":28,"b_a":62,"b_d":29},{"o_a":61,"o_d":29,"b_a":59,"b_d":30},{"o_a":62,"o_d":28,"b_a":53,"b_d":29},{"o_a":62,"o_d":27,"b_a":58,"b_d":29},{"o_a":64,"o_d":24,"b_a":63,"b_d":22},{"o_a":64,"o_d":22,"b_a":62,"b_d":21},{"o_a":65,"o_d":20,"b_a":57,"b_d":25},{"o_a":66,"o_d":17,"b_a":57,"b_d":25}]
<!DOCTYPE html>
<head>
<title>Difference Chart</title>
</head>
<meta charset="utf-8">
<style>
svg{
background-color: #eee;
margin-top: 20px;
}
path.domain {
fill: none;
}
g.tick > line {
stroke: white;
stroke-width: 1px;
}
</style>
<body>
<div id="viz"></div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script>
var width = 500,
height = 500;
xScale = d3.scale.linear().domain([0,10]).range([40,760]);
yScale = d3.scale.linear().domain([0,10]).range([60,830]);
svg = d3.select('#viz')
.append('svg')
.attr({width: width, height: height});
svg.append("g").attr("class","linechart");
linechart = d3.select("g.linechart");
d3.json("bush_obama.json", createLineChart);
function createLineChart(data) {
data.forEach(function(d) {
d.b_a = parseInt(d.b_a);
d.o_a = parseInt(d.o_a);
});
b_aExtent = d3.extent(data, function(d) {return d.b_a});
o_aExtent = d3.extent(data, function(d) {return d.o_a});
chartExtent = ([Math.min(b_aExtent[0],o_aExtent[0]) - 5, Math.max(b_aExtent[1],o_aExtent[1]) + 5]);
xScale = d3.scale.linear().domain([data.length, 0]).range([20,480]);
yScale = d3.scale.linear().domain(chartExtent).range([480,20]);
var axis = d3.svg.axis().scale(yScale)
.orient("right")
.ticks(8)
.tickSize(-460)
.tickSubdivide(true);
b_aLine = d3.svg.line().x(function(d,i) {return xScale(i)}).y(function(d) {return yScale(d.b_a)}).interpolate("linear");
o_aLine = d3.svg.line().x(function(d,i) {return xScale(i)}).y(function(d) {return yScale(d.o_a)}).interpolate("linear");
b_aDifference = d3.svg.area().x(function(d,i) {return xScale(i)})
.y(function(d) {return yScale(Math.max(d.b_a,d.o_a))})
.y0(function(d) {return yScale(d.o_a)})
.interpolate("linear");
o_aDifference = d3.svg.area().x(function(d,i) {return xScale(i)})
.y(function(d) {return yScale(Math.max(d.o_a,d.b_a))})
.y0(function(d) {return yScale(d.b_a)})
.interpolate("linear");
linechart.append("g").attr("transform", "translate(480,0)").call(axis);
linechart.append("path")
.style("fill", "green")
.style("fill-opacity", .5)
.attr("class", "difference")
.attr("d", b_aDifference(data))
linechart.append("path")
.style("fill", "red")
.style("fill-opacity", .5)
.attr("class", "difference")
.attr("d", o_aDifference(data))
linechart.append("path")
.style("fill", "none")
.style("stroke-width", "1px")
.style("stroke", "darkgreen")
.attr("class", "line")
.attr("d", b_aLine(data))
linechart.append("path")
.style("fill", "none")
.style("stroke-width", "1px")
.style("stroke", "darkred")
.attr("class", "line")
.attr("d", o_aLine(data))
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment