Built with blockbuilder.org
forked from rjurney's block: Simple Box Plot Example in d3.js 4.0
| license: apache-2.0 |
Built with blockbuilder.org
forked from rjurney's block: Simple Box Plot Example in d3.js 4.0
| <!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> | |
| // set the dimensions and margins of the graph | |
| var margin = {top: 20, right: 20, bottom: 30, left: 50}, | |
| width = 960 - margin.left - margin.right, | |
| height = 500 - margin.top - margin.bottom; | |
| var barWidth = 30; | |
| // Generate five 100 count, normal distributions with random means | |
| var groupCounts = {}; | |
| var globalCounts = []; | |
| var meanGenerator = d3.randomUniform(10); | |
| for(i=0; i <= 5; 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.schemeCategory10) | |
| .domain(Object.keys(groupCounts)); | |
| // Prepare the data for the box plots | |
| var boxPlotData = []; | |
| for (var [key, groupCount] of Object.entries(groupCounts)) { | |
| var localMin = d3.min(groupCount); | |
| var localMax = d3.max(groupCount); | |
| var obj = {}; | |
| obj["key"] = key; | |
| obj["counts"] = groupCount; | |
| obj["quartile"] = boxQuartiles(groupCount); | |
| obj["whiskers"] = [localMin, localMax]; | |
| obj["color"] = colorScale(key); | |
| boxPlotData.push(obj); | |
| } | |
| // 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([height, 0]); | |
| // append the svg obgect to the body of the page | |
| // appends a 'group' element to 'svg' | |
| // moves the 'group' element to the top left margin | |
| 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 + ")"); | |
| // append a group for the box plot elements | |
| var g = svg.append("g"); | |
| // Draw the box plot vertical lines | |
| var verticalLines = g.selectAll(".verticalLines") | |
| .data(boxPlotData) | |
| .enter() | |
| .append("line") | |
| .attr("x1", function(datum) { return xScale(datum.key); }) | |
| .attr("y1", function(datum) { return yScale(datum.whiskers[0]); }) | |
| .attr("x2", function(datum) { return xScale(datum.key); }) | |
| .attr("y2", function(datum) { return yScale(datum.whiskers[1]); }) | |
| .attr("stroke", "#000") | |
| .attr("stroke-width", 1) | |
| .attr("fill", "none"); | |
| // Draw the boxes of the box plot, filled 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[0]) - yScale(quartiles[2]); | |
| return height; | |
| }) | |
| .attr("x", function(datum) { return xScale(datum.key) - (barWidth/2); }) | |
| .attr("y", function(datum) { return yScale(datum.quartile[2]); }) | |
| .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) - barWidth/2 }, | |
| y1: function(datum) { return yScale(datum.whiskers[0]) }, | |
| x2: function(datum) { return xScale(datum.key) + barWidth/2 }, | |
| y2: function(datum) { return yScale(datum.whiskers[0]) } | |
| }, | |
| // Median line | |
| { | |
| x1: function(datum) { return xScale(datum.key) - barWidth/2 }, | |
| y1: function(datum) { return yScale(datum.quartile[1]) }, | |
| x2: function(datum) { return xScale(datum.key) + barWidth/2 }, | |
| y2: function(datum) { return yScale(datum.quartile[1]) } | |
| }, | |
| // Bottom whisker | |
| { | |
| x1: function(datum) { return xScale(datum.key) - barWidth/2 }, | |
| y1: function(datum) { return yScale(datum.whiskers[1]) }, | |
| x2: function(datum) { return xScale(datum.key) + barWidth/2 }, | |
| 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"); | |
| } | |
| // Move the left axis over 25 pixels, and the top axis over 35 pixels | |
| //var axisY = svg.append("g").attr("transform", "translate(25,0)"); | |
| //var axisX = svg.append("g").attr("transform", "translate(35,0)"); | |
| //x-axis | |
| svg.append("g") | |
| .attr("transform", "translate(0," + height + ")") | |
| .call(d3.axisBottom(xScale)); | |
| // Add the Y Axis | |
| svg.append("g") | |
| .call(d3.axisLeft(yScale)); | |
| 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> |