Created
June 18, 2017 03:17
Revisions
-
rjurney created this gist
Jun 18, 2017 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1 @@ 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1 @@ Built with [blockbuilder.org](http://blockbuilder.org) 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,217 @@ <!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>