UNPKG

doevisualizations

Version:

Data Visualization Library based on RequireJS and D3.js (v4+)

372 lines (321 loc) 15.1 kB
define(['jquery', 'underscore', 'jqueryuiwidget', 'handlebars', 'd3' ], function($, _, ui, Handlebars, d3) { $.widget("doe.doeboxplot", { // Options to be used as defaults options: {}, _privateData: {}, _template: function() { var arrHTML = []; arrHTML.push('<div class="doeboxplot">'); arrHTML.push('<div class="title">'); arrHTML.push('</div>'); arrHTML.push('<div class="subTitle">'); arrHTML.push('</div>'); arrHTML.push('</div>'); return arrHTML.join(''); }, _create: function() { this._fetchAndRender(); }, _fetchAndRender: function() { this._compileTemplate(); }, _compileTemplate: function() { var compiled = Handlebars.compile(this._template()); this.element.html(compiled({})); this._compileTooltip(); this._buildVisualization(); this._bindEvents(); this._generateRawTable(); }, _generateRawTable: function() { var arrHTML = []; var data = this.options["Data"]; if (data.length > 0) { arrHTML.push('<div class ="doehide">'); arrHTML.push('<table>'); arrHTML.push('<thead>'); arrHTML.push('<tr>'); for (p in data[1]) { arrHTML.push('<th>'); arrHTML.push(p); arrHTML.push('</th>'); } arrHTML.push('</tr>'); for (var i = 0; i < data.length; i++) { arrHTML.push('<tr>'); for (prop in data[i]) { arrHTML.push('<td>'); arrHTML.push(data[i][prop]); arrHTML.push('</td>'); } arrHTML.push('</tr>'); } arrHTML.push('</thead>'); arrHTML.push('</table>'); arrHTML.push('</div>'); } this.element.append(arrHTML.join('')); }, _compileTooltip: function() { this._privateData.tooltipCompiled = Handlebars.compile(this.options.TooltipTemplate); }, _bindEvents: function() { }, _buildVisualization: function() { var groupeddata = _.groupBy(this.options.Data, $.proxy(function(item) { return item[this.options["Groupings"][0]]; }, this)); var globalCounts = []; for (var k in groupeddata) { for (var i = 0; i < k.length; i++) { globalCounts.push(groupeddata[k][i][this.options["YFields"][0]]); } } console.log(groupeddata); console.log(globalCounts); this._setupD3Element(groupeddata, globalCounts); }, _setupD3Element: function(groupCounts, globalCounts) { this.element.find('div.title').html(this.options.ChartTitle); this.element.find('div.subTitle').html(this.options.ChartSubTitle); var width = 590; var height = 300; 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; //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); // } // } //console.log(groupCounts); //console.log(globalCounts); // Sort group counts so quantile methods work for (var key in groupCounts) { var groupCount = groupCounts[key]; groupCounts[key] = groupCount.sort($.proxy(this._sortNumber, this)); } // Setup a color scale for filling each box // var colorScale = d3.scaleOrdinal(d3.schemeCategory20) // .domain(Object.keys(groupCounts)); var colorScale = d3.scaleOrdinal(this.options.ColorPalette) .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, $.proxy(function(d) { return d[this.options.YFields[0]]; }, this)); var localMax = d3.max(groupCount, $.proxy(function(d) { return d[this.options.YFields[0]]; }, this)); var mappeddata = _.map(groupCount, $.proxy(function(item) { return item[this.options.YFields[0]]; }, this)); record["key"] = key; record[this.options.XFields[0]] = key; record["counts"] = groupCount; record["quartile"] = this._boxQuartiles(mappeddata); record["whiskers"] = [localMin, localMax]; record["color"] = colorScale(key); boxPlotData.push(record); } console.log(boxPlotData); var tooltipcontainer = d3.select(this.element.find('div.doeboxplot').get(0)).append("div") .attr("class", "doeboxplottooltip") .style("opacity", 0); // 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(this.element.find('div.doeboxplot').get(0)).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]); }).on("mouseover", $.proxy(function(d, i) { d3.select(this.element.find(".doeboxplottooltip").get(0)).transition() .duration(200) .style("opacity", .9); d3.select(this.element.find(".doeboxplottooltip").get(0)).html(this._privateData.tooltipCompiled(d)) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }, this)) .on("mouseout", $.proxy(function(d, i) { d3.select(this.element.find(".doeboxplottooltip").get(0)).transition() .duration(500) .style("opacity", 0); }, this)) .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); }, _sortNumber: function(a, b) { return a[this.options.YFields[0]] - b[this.options.YFields[0]]; }, _boxQuartiles: function(d) { return [ d3.quantile(d, .25), d3.quantile(d, .5), d3.quantile(d, .75) ]; }, destroy: function() { }, _setOption: function(key, value) { this._super(key, value); }, _setOptions: function(options) { this._super(options); } }); });