Built with blockbuilder.org
Created
June 18, 2017 03:17
-
-
Save rjurney/e04ceddae2e8f85cf3afe4681dac1d74 to your computer and use it in GitHub Desktop.
Simple Box Plot Example in d3.js v4.0
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
license: mit |
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> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<style> | |
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
</style> | |
</head> | |
<body> | |
<script> | |
var width = 900; | |
var height = 400; | |
var barWidth = 30; | |
var margin = {top: 20, right: 10, bottom: 20, left: 10}; | |
var width = width - margin.left - margin.right, | |
height = height - margin.top - margin.bottom; | |
var totalWidth = width + margin.left + margin.right; | |
var totalheight = height + margin.top + margin.bottom; | |
// Generate five 100 count, normal distributions with random means | |
var groupCounts = {}; | |
var globalCounts = []; | |
var meanGenerator = d3.randomUniform(10); | |
for(i=0; i<7; i++) { | |
var randomMean = meanGenerator(); | |
var generator = d3.randomNormal(randomMean); | |
var key = i.toString(); | |
groupCounts[key] = []; | |
for(j=0; j<100; j++) { | |
var entry = generator(); | |
groupCounts[key].push(entry); | |
globalCounts.push(entry); | |
} | |
} | |
// Sort group counts so quantile methods work | |
for(var key in groupCounts) { | |
var groupCount = groupCounts[key]; | |
groupCounts[key] = groupCount.sort(sortNumber); | |
} | |
// Setup a color scale for filling each box | |
var colorScale = d3.scaleOrdinal(d3.schemeCategory20) | |
.domain(Object.keys(groupCounts)); | |
// Prepare the data for the box plots | |
var boxPlotData = []; | |
for (var [key, groupCount] of Object.entries(groupCounts)) { | |
var record = {}; | |
var localMin = d3.min(groupCount); | |
var localMax = d3.max(groupCount); | |
record["key"] = key; | |
record["counts"] = groupCount; | |
record["quartile"] = boxQuartiles(groupCount); | |
record["whiskers"] = [localMin, localMax]; | |
record["color"] = colorScale(key); | |
boxPlotData.push(record); | |
} | |
// Compute an ordinal xScale for the keys in boxPlotData | |
var xScale = d3.scalePoint() | |
.domain(Object.keys(groupCounts)) | |
.rangeRound([0, width]) | |
.padding([0.5]); | |
// Compute a global y scale based on the global counts | |
var min = d3.min(globalCounts); | |
var max = d3.max(globalCounts); | |
var yScale = d3.scaleLinear() | |
.domain([min, max]) | |
.range([0, height]); | |
// Setup the svg and group we will draw the box plot in | |
var svg = d3.select("body").append("svg") | |
.attr("width", totalWidth) | |
.attr("height", totalheight) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
// Move the left axis over 25 pixels, and the top axis over 35 pixels | |
var axisG = svg.append("g").attr("transform", "translate(25,0)"); | |
var axisTopG = svg.append("g").attr("transform", "translate(35,0)"); | |
// Setup the group the box plot elements will render in | |
var g = svg.append("g") | |
.attr("transform", "translate(20,5)"); | |
// Draw the box plot vertical lines | |
var verticalLines = g.selectAll(".verticalLines") | |
.data(boxPlotData) | |
.enter() | |
.append("line") | |
.attr("x1", function(datum) { | |
return xScale(datum.key) + barWidth/2; | |
} | |
) | |
.attr("y1", function(datum) { | |
var whisker = datum.whiskers[0]; | |
return yScale(whisker); | |
} | |
) | |
.attr("x2", function(datum) { | |
return xScale(datum.key) + barWidth/2; | |
} | |
) | |
.attr("y2", function(datum) { | |
var whisker = datum.whiskers[1]; | |
return yScale(whisker); | |
} | |
) | |
.attr("stroke", "#000") | |
.attr("stroke-width", 1) | |
.attr("fill", "none"); | |
// Draw the boxes of the box plot, filled in white and on top of vertical lines | |
var rects = g.selectAll("rect") | |
.data(boxPlotData) | |
.enter() | |
.append("rect") | |
.attr("width", barWidth) | |
.attr("height", function(datum) { | |
var quartiles = datum.quartile; | |
var height = yScale(quartiles[2]) - yScale(quartiles[0]); | |
return height; | |
} | |
) | |
.attr("x", function(datum) { | |
return xScale(datum.key); | |
} | |
) | |
.attr("y", function(datum) { | |
return yScale(datum.quartile[0]); | |
} | |
) | |
.attr("fill", function(datum) { | |
return datum.color; | |
} | |
) | |
.attr("stroke", "#000") | |
.attr("stroke-width", 1); | |
// Now render all the horizontal lines at once - the whiskers and the median | |
var horizontalLineConfigs = [ | |
// Top whisker | |
{ | |
x1: function(datum) { return xScale(datum.key) }, | |
y1: function(datum) { return yScale(datum.whiskers[0]) }, | |
x2: function(datum) { return xScale(datum.key) + barWidth }, | |
y2: function(datum) { return yScale(datum.whiskers[0]) } | |
}, | |
// Median line | |
{ | |
x1: function(datum) { return xScale(datum.key) }, | |
y1: function(datum) { return yScale(datum.quartile[1]) }, | |
x2: function(datum) { return xScale(datum.key) + barWidth }, | |
y2: function(datum) { return yScale(datum.quartile[1]) } | |
}, | |
// Bottom whisker | |
{ | |
x1: function(datum) { return xScale(datum.key) }, | |
y1: function(datum) { return yScale(datum.whiskers[1]) }, | |
x2: function(datum) { return xScale(datum.key) + barWidth }, | |
y2: function(datum) { return yScale(datum.whiskers[1]) } | |
} | |
]; | |
for(var i=0; i < horizontalLineConfigs.length; i++) { | |
var lineConfig = horizontalLineConfigs[i]; | |
// Draw the whiskers at the min for this series | |
var horizontalLine = g.selectAll(".whiskers") | |
.data(boxPlotData) | |
.enter() | |
.append("line") | |
.attr("x1", lineConfig.x1) | |
.attr("y1", lineConfig.y1) | |
.attr("x2", lineConfig.x2) | |
.attr("y2", lineConfig.y2) | |
.attr("stroke", "#000") | |
.attr("stroke-width", 1) | |
.attr("fill", "none"); | |
} | |
// Setup a scale on the left | |
var axisLeft = d3.axisLeft(yScale); | |
axisG.append("g") | |
.call(axisLeft); | |
// Setup a series axis on the top | |
var axisTop = d3.axisTop(xScale); | |
axisTopG.append("g") | |
.call(axisTop); | |
function boxQuartiles(d) { | |
return [ | |
d3.quantile(d, .25), | |
d3.quantile(d, .5), | |
d3.quantile(d, .75) | |
]; | |
} | |
// Perform a numeric sort on an array | |
function sortNumber(a,b) { | |
return a - b; | |
} | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment