function makeDistroChart(dataset, xGroup, yValue) { /* * dataset = the csv file * xGroup = the name of the column to group by * yValue = the column to use as the values for the chart * * */ var chart = {}; var colorFunct = d3.scale.category10(); //function () {return 'lightgrey';}; function formatAsFloat(d) { if (d % 1 !== 0) { return d3.format(".2f")(d); } else { return d3.format(".0f")(d); } } function logFormatNumber(d) { var x = Math.log(d) / Math.log(10) + 1e-6; return Math.abs(x - Math.floor(x)) < 0.6 ? formatAsFloat(d) : ""; } chart.yFormatter = formatAsFloat; chart.data = dataset; //Data management chart.xGroup = xGroup; chart.yValue = yValue; chart.groupObjs = {}; //The data organized by grouping and sorted as well as any metadata for the groups chart.objs = {mainDiv: null, chartDiv: null, g: null, xAxis: null, yAxis: null}; function updateColorFunction(colorOptions) { /* * Takes either a list of colors, a function or an object with the mapping already in place * */ if (typeof colorOptions == 'function') { return colorOptions } else if (Array.isArray(colorOptions)) { // If an array is provided, map it to the domain var colorMap = {}, cColor = 0; for (var cName in chart.groupObjs) { colorMap[cName] = colorOptions[cColor]; cColor = (cColor + 1) % colorOptions.length; } return function (group) { return colorMap[group]; } } else if (typeof colorOptions == 'object') { // if an object is provided, assume it maps to the colors return function (group) { return colorOptions[group]; } } } function updateGroupWidth(boxWidth) { // Takes the boxWidth size (as percentage of possible width) and returns the actual pixel width to use var boxSize = {left: null, right: null, middle: null}; var width = chart.xScale.rangeBand() * (boxWidth / 100); var padding = (chart.xScale.rangeBand() - width) / 2; boxSize.middle = chart.xScale.rangeBand() / 2; boxSize.left = padding; boxSize.right = boxSize.left + width; return boxSize; } function tooltipHover(name, metrics) { var tooltipString = "Group: " + name; tooltipString += "Max: " + formatAsFloat(metrics.max, 0.1); tooltipString += "Q3: " + formatAsFloat(metrics.quartile3); tooltipString += "Median: " + formatAsFloat(metrics.median); tooltipString += "Q1: " + formatAsFloat(metrics.quartile1); tooltipString += "Min: " + formatAsFloat(metrics.min); return function () { chart.objs.tooltip.transition().duration(200).style("opacity", 0.9); chart.objs.tooltip.html(tooltipString) }; } function prepareData() { /* * Takes the dataset that is an array of objects and groups the yValues by xGroups and then sorts it * Returns the groupObj * */ function calcMetrics(values) { var metrics = { //These are the original non–scaled values max: null, upperOuterFence: null, upperInnerFence: null, quartile3: null, median: null, mean: null, iqr: null, quartile1: null, lowerInnerFence: null, lowerOuterFence: null, min: null }; metrics.min = d3.min(values); metrics.quartile1 = d3.quantile(values, 0.25); metrics.median = d3.median(values); metrics.mean = d3.mean(values); metrics.quartile3 = d3.quantile(values, 0.75); metrics.max = d3.max(values); metrics.iqr = metrics.quartile3 - metrics.quartile1; //The inner fences are the closest value to the IQR without going past it (assumes sorted lists) var LIF = metrics.quartile1 - (1.5 * metrics.iqr); var UIF = metrics.quartile3 + (1.5 * metrics.iqr); for (var i = 0; i <= values.length; i++) { if (values[i] < LIF) { continue; } if (!metrics.lowerInnerFence && values[i] >= LIF) { metrics.lowerInnerFence = values[i]; continue; } if (values[i] > UIF) { metrics.upperInnerFence = values[i - 1]; break; } } metrics.lowerOuterFence = metrics.quartile1 - (3 * metrics.iqr); metrics.upperOuterFence = metrics.quartile3 + (3 * metrics.iqr); if (!metrics.lowerInnerFence) { metrics.lowerInnerFence = metrics.min; } if (!metrics.upperInnerFence) { metrics.upperInnerFence = metrics.max; } return metrics } var current_x = null; var current_y = null; var current_row; //Group the values for (current_row = 0; current_row < chart.data.length; current_row++) { current_x = chart.data[current_row][chart.xGroup]; current_y = chart.data[current_row][chart.yValue]; if (chart.groupObjs.hasOwnProperty(current_x)) { chart.groupObjs[current_x].values.push(current_y); } else { chart.groupObjs[current_x] = {}; chart.groupObjs[current_x].values = [current_y]; } } var cName; // Sort them for (cName in chart.groupObjs) { chart.groupObjs[cName].values.sort(d3.ascending); chart.groupObjs[cName].metrics = {}; chart.groupObjs[cName].metrics = calcMetrics(chart.groupObjs[cName].values); } } prepareData(); chart.update = function () { if (!chart.objs.g) { return false; } // Update chart size chart.width = parseInt(chart.objs.chartDiv.style("width"), 10) - (chart.margin.left + chart.margin.right); chart.height = parseInt(chart.objs.chartDiv.style("height"), 10) - (chart.margin.top + chart.margin.bottom); chart.xScale.rangeBands([0, chart.width]); chart.yScale.range([chart.height, 0]); //Update axes chart.objs.g.select('.x.axis').attr("transform", "translate(0," + chart.height + ")").call(chart.objs.xAxis) .selectAll("text") .attr("y", 5) .attr("x", -5) .attr("transform", "rotate(-45)") .style("text-anchor", "end"); chart.objs.g.select('.x.axis .label').attr("x", chart.width / 2); chart.objs.g.select('.y.axis').call(chart.objs.yAxis.innerTickSize(-chart.width)); chart.objs.g.select('.y.axis .label').attr("x", -chart.height / 2); chart.objs.chartDiv.select('svg').attr("width", chart.width + (chart.margin.left + chart.margin.right)).attr("height", chart.height + (chart.margin.top + chart.margin.bottom)); return chart; }; chart.bind = function (selector, chartOptions) { /* * Setup chart and connect it to the correct div * * Selector is the id to attach the chart to * chartOptions = list of chart options * scale = linear (vs log) * chartSize * – chart_width = 800 * – chart_height = 400 * margin = {top: 15, right: 60, bottom: 30, left: 50}; * constrainExtremes True/False, if true max is then the max of the lower fences * axisLabels = Labels for the chart * */ //Get base data function getBaseData() { if (chartOptions && chartOptions.margin) { chart.margin = margin; } else { chart.margin = {top: 15, right: 40, bottom: 35, left: 50}; } if (chartOptions && chartOptions.chartSize) { chart.divWidth = chartOptions.chartSize.width; chart.divHeight = chartOptions.chartSize.height; } else { chart.divWidth = 800; chart.divHeight = 400; } chart.width = chart.divWidth - chart.margin.left - chart.margin.right; chart.height = chart.divHeight - chart.margin.top - chart.margin.bottom; if (chartOptions && chartOptions.axisLabels) { chart.xAxisLable = chartOptions.axisLabels.xAxis; chart.yAxisLable = chartOptions.axisLabels.yAxis; } else { chart.xAxisLable = xGroup; chart.yAxisLable = yValue; } if (chartOptions && chartOptions.scale === 'log') { chart.yScale = d3.scale.log(); chart.yFormatter = logFormatNumber; } else { chart.yScale = d3.scale.linear(); } if (chartOptions && chartOptions.constrainExtremes === true) { var fences = []; for (var cName in chart.groupObjs) { fences.push(chart.groupObjs[cName].metrics.lowerInnerFence); fences.push(chart.groupObjs[cName].metrics.upperInnerFence); } chart.range = d3.extent(fences); } else { chart.range = d3.extent(chart.data, function (d) { return d[chart.yValue]; }); } // Take the options colors argument and update the colors function if (chartOptions && chartOptions.colors) { colorFunct = updateColorFunction(chartOptions.colors); } chart.yScale.range([chart.height, 0]).domain(chart.range).clamp(true); // Get x range chart.xScale = d3.scale.ordinal().domain(Object.keys(chart.groupObjs)).rangeBands([0, chart.width]); //Build Axes chart.objs.yAxis = d3.svg.axis() .scale(chart.yScale) .orient("left") .tickFormat(chart.yFormatter) .outerTickSize(0) .innerTickSize(-chart.width + (chart.margin.right + chart.margin.left)); chart.objs.xAxis = d3.svg.axis().scale(chart.xScale).orient("bottom").tickSize(5); } getBaseData(); chart.objs.mainDiv = d3.select(selector) .style("max-width", chart.divWidth + "px"); // Add all the divs to make it centered and responsive chart.objs.mainDiv.append("div") .attr("class", "inner-wrapper") .style("padding-bottom", (chart.divHeight / chart.divWidth) * 100 + "%") .append("div").attr("class", "outer-box") .append("div").attr("class", "inner-box"); // Capture the inner div for the chart (where the chart actually is) chart.chartSelector = selector + " .inner-box"; chart.objs.chartDiv = d3.select(chart.chartSelector); d3.select(window).on('resize.' + chart.chartSelector, chart.update); // Create the svg chart.objs.g = chart.objs.chartDiv.append("svg") .attr("class", "chart-area") .attr("width", chart.width + (chart.margin.left + chart.margin.right)) .attr("height", chart.height + (chart.margin.top + chart.margin.bottom)) .append("g") .attr("transform", "translate(" + chart.margin.left + "," + chart.margin.top + ")"); chart.objs.axes = chart.objs.g.append("g").attr("class", "axis"); // Show axis chart.objs.axes.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + chart.height + ")") .call(chart.objs.xAxis); chart.objs.axes.append("g") .attr("class", "y axis") .call(chart.objs.yAxis) .append("text") .attr("class", "label") .attr("transform", "rotate(-90)") .attr("y", -42) .attr("x", -chart.height / 2) .attr("dy", ".71em") .style("text-anchor", "middle") .text(chart.yAxisLable); //Add the tooltip div chart.objs.tooltip = chart.objs.mainDiv.append('div').attr('class', 'tooltip'); // Add hover tooltip for (var cName in chart.groupObjs) { //Add mouseover chart.groupObjs[cName].g = chart.objs.g.append("g").attr("class", "group"); chart.groupObjs[cName].g.on("mouseover", function () { chart.objs.tooltip.style("display", null).style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY - 28) + "px"); }).on("mouseout", function () { chart.objs.tooltip.style("display", "none"); }).on("mousemove", tooltipHover(cName, chart.groupObjs[cName].metrics)) } chart.update(); return chart; }; chart.renderViolinPlot = function (chartOptions) { /* * Options * - showArea True/False (default True) * - showLine True/False (default True) * - resolution, number of bins * - boxWidth (wider or not) */ chart.violinPlots = {}; chart.violinPlots.plots = {}; chart.violinPlots.violinOptions = chartOptions; var vOpts = chart.violinPlots.violinOptions; // Violin Calculations chart.violinPlots.calculateNumBins = function (cGroup) { var iqr; if (chart.boxPlots) { iqr = chart.groupObjs[cGroup].metrics.iqr } else { var quartile1 = d3.quantile(chart.groupObjs[cGroup].values, 0.25); var quartile3 = d3.quantile(chart.groupObjs[cGroup].values, 0.75); iqr = quartile3 - quartile1; } return Math.max(Math.round(2 * (iqr / Math.pow(chart.groupObjs[cGroup].values.length, 1 / 3))), 50) }; function prepareViolin() { /* * Takes the structured data and calculates the box plot numbers * */ var cName; for (cName in chart.groupObjs) { chart.groupObjs[cName].violin = {}; chart.groupObjs[cName].violin.objs = {}; chart.groupObjs[cName].violin.histogramFunct = d3.layout.histogram().frequency(1); } } prepareViolin(); chart.violinPlots.change = function (updateOptions) { /* * Same options as on renderViolin */ if (updateOptions) { for (var key in updateOptions) { vOpts[key] = updateOptions[key] } } mapObjects(true); chart.violinPlots.update() }; chart.violinPlots.update = function () { var cName, cViolinPlot; for (cName in chart.groupObjs) { cViolinPlot = chart.groupObjs[cName].violin; if (vOpts && vOpts.resolution) { cViolinPlot.histogramFunct.bins(vOpts.resolution); } else { cViolinPlot.histogramFunct.bins(chart.violinPlots.calculateNumBins(cName)); } cViolinPlot.histogramData = cViolinPlot.histogramFunct(chart.groupObjs[cName].values); // Get the box size var groupWidth = {left: null, right: null, middle: null}; if (vOpts && vOpts.violinWidth) { groupWidth = updateGroupWidth(vOpts.violinWidth) } else { groupWidth = updateGroupWidth(100) } var leftBound = chart.xScale(cName) + groupWidth.left; var rightBound = chart.xScale(cName) + groupWidth.right; var width = (rightBound - leftBound) / 2; var xV = chart.yScale.copy(); var yV = d3.scale.linear() .range([width, 0]) .domain([0, Math.max(chart.range[1], d3.max(cViolinPlot.histogramData, function (d) { return d.y; }))]) .clamp(true); var area = d3.svg.area() .interpolate('basis') .x(function (d) { return xV(d.x); }) .y0(width) .y1(function (d) { return yV(d.y); }); var line = d3.svg.line() .interpolate('basis') .x(function (d) { return xV(d.x); }) .y(function (d) { return yV(d.y); }); if (cViolinPlot.objs.left.area) { cViolinPlot.objs.left.area .datum(cViolinPlot.histogramData) .attr("d", area); } if (cViolinPlot.objs.left.line) { cViolinPlot.objs.left.line .datum(cViolinPlot.histogramData) .attr("d", line); } if (cViolinPlot.objs.right.area) { cViolinPlot.objs.right.area .datum(cViolinPlot.histogramData) .attr("d", area); } if (cViolinPlot.objs.right.line) { cViolinPlot.objs.right.line .datum(cViolinPlot.histogramData) .attr("d", line); } cViolinPlot.objs.left.g.attr("transform", "rotate(90,0,0) translate(0,-" + leftBound + ") scale(1,-1)"); cViolinPlot.objs.right.g.attr("transform", "rotate(90,0,0) translate(0,-" + rightBound + ")"); } }; function mapObjects(clear) { var cName, cViolinPlot; if (vOpts && vOpts.colors) { chart.violinPlots.color = updateColorFunction(vOpts.colors); } else { chart.violinPlots.color = colorFunct } for (cName in chart.groupObjs) { cViolinPlot = chart.groupObjs[cName].violin; if (clear) { cViolinPlot.objs.g.remove() } cViolinPlot.objs.g = chart.groupObjs[cName].g.append("g").attr("class", "violin-plot"); cViolinPlot.objs.left = {area: null, line: null, g: null}; cViolinPlot.objs.right = {area: null, line: null, g: null}; cViolinPlot.objs.left.g = cViolinPlot.objs.g.append("g"); cViolinPlot.objs.right.g = cViolinPlot.objs.g.append("g"); if (!vOpts || (vOpts && vOpts.showArea !== false)) { cViolinPlot.objs.left.area = cViolinPlot.objs.left.g.append("path") .attr("class", "area") .style("fill", chart.violinPlots.color(cName)); cViolinPlot.objs.right.area = cViolinPlot.objs.right.g.append("path") .attr("class", "area") .style("fill", chart.violinPlots.color(cName)); } if (!vOpts || (vOpts && vOpts.showLine !== false)) { cViolinPlot.objs.left.line = cViolinPlot.objs.left.g.append("path") .attr("class", "line") .attr("fill", 'none') .style("stroke", chart.violinPlots.color(cName)); cViolinPlot.objs.right.line = cViolinPlot.objs.right.g.append("path") .attr("class", "line") .attr("fill", 'none') .style("stroke", chart.violinPlots.color(cName)); } } } mapObjects(); d3.select(window).on('resize.' + chart.chartSelector + '.violinPlot', chart.violinPlots.update); //Update the divs with the proper values chart.violinPlots.update(); return chart.violinPlots; }; chart.renderBoxPlot = function (chartOptions) { chart.boxPlots = {}; chart.boxPlots.chartOptions = chartOptions; var bOpts = chart.boxPlots.chartOptions; /* * options: * showOutliers: True/False (default True) - this shouldn't affect the min/max * showWhiskers: True/False (default True) * whiskersRatio: (default standard=iqr*1.5), other options, minmax, (future?: std) * showBox: True/False (default True) * showMedian: True/False (default True) * showMean: True/False (default False) * outlierScatter: True/False (default False) (not fully implimented) * boxWidth (not implimented) what percent of the bin should the box take up */ //Create boxPlots for (var cName in chart.groupObjs) { chart.groupObjs[cName].boxPlot = {}; chart.groupObjs[cName].boxPlot.objs = {}; } function calcOutliers(obj, values, metrics) { /* * Create lists of the outliers for each content group */ var cExtremes = []; var cOutliers = []; var cOut, idx; for (idx = 0; idx <= values.length; idx++) { cOut = {value: values[idx]}; if (cOut.value < metrics.lowerInnerFence) { if (cOut.value < metrics.lowerOuterFence) { cExtremes.push(cOut); } else { cOutliers.push(cOut); } } else if (cOut.value > metrics.upperInnerFence) { if (cOut.value > metrics.upperOuterFence) { cExtremes.push(cOut); } else { cOutliers.push(cOut); } } } obj.outliers = cOutliers; obj.extremes = cExtremes; } function calcAllOutliers() { if (!bOpts || (bOpts && bOpts.showOutliers !== false)) { for (var cName in chart.groupObjs) { calcOutliers(chart.groupObjs[cName].boxPlot.objs, chart.groupObjs[cName].values, chart.groupObjs[cName].metrics); } } } calcAllOutliers(); chart.boxPlots.change = function (updateOptions) { if (updateOptions) { for (var key in updateOptions) { bOpts[key] = updateOptions[key] } } mapObjects(true); chart.boxPlots.update() }; chart.boxPlots.update = function () { var cName, cBoxPlot; for (cName in chart.groupObjs) { cBoxPlot = chart.groupObjs[cName].boxPlot; // Get the box size var groupWidth = {left: null, right: null, middle: null}; if (bOpts && bOpts.boxWidth) { groupWidth = updateGroupWidth(bOpts.boxWidth) } else { groupWidth = updateGroupWidth(30) } var leftBound = chart.xScale(cName) + groupWidth.left; var rightBound = chart.xScale(cName) + groupWidth.right; var middle = chart.xScale(cName) + groupWidth.middle; var sMetrics = {}; //temp var for scaled (plottable) metric values for (var attr in chart.groupObjs[cName].metrics) { sMetrics[attr] = null; sMetrics[attr] = chart.yScale(chart.groupObjs[cName].metrics[attr]); } //// Box if (cBoxPlot.objs.box) { cBoxPlot.objs.box .attr("x", leftBound) .attr('width', rightBound - leftBound) .attr("y", sMetrics.quartile3) .attr("rx", 1) .attr("ry", 1) .attr("height", -sMetrics.quartile3 + sMetrics.quartile1) } //// Lines if (cBoxPlot.objs.upperWhisker) { cBoxPlot.objs.upperWhisker.fence .attr("x1", leftBound) .attr("x2", rightBound) .attr('y1', sMetrics.upperInnerFence) .attr("y2", sMetrics.upperInnerFence); cBoxPlot.objs.upperWhisker.line .attr("x1", middle) .attr("x2", middle) .attr('y1', sMetrics.quartile3) .attr("y2", sMetrics.upperInnerFence); cBoxPlot.objs.lowerWhisker.fence .attr("x1", leftBound) .attr("x2", rightBound) .attr('y1', sMetrics.lowerInnerFence) .attr("y2", sMetrics.lowerInnerFence); cBoxPlot.objs.lowerWhisker.line .attr("x1", middle) .attr("x2", middle) .attr('y1', sMetrics.quartile1) .attr("y2", sMetrics.lowerInnerFence); } //// Median if (cBoxPlot.objs.median) { cBoxPlot.objs.median.line .attr("x1", leftBound) .attr("x2", rightBound) .attr('y1', sMetrics.median) .attr("y2", sMetrics.median); cBoxPlot.objs.median.circle .attr("cx", middle) .attr("cy", sMetrics.median) } //// Mean if (cBoxPlot.objs.mean) { cBoxPlot.objs.mean.line .attr("x1", leftBound) .attr("x2", rightBound) .attr('y1', sMetrics.mean) .attr("y2", sMetrics.mean); cBoxPlot.objs.mean.circle .attr("cx", middle) .attr("cy", sMetrics.mean); } //// Outliers var pt; if (cBoxPlot.objs.outliers) { for (pt in cBoxPlot.objs.outliers) { cBoxPlot.objs.outliers[pt].point .attr("cx", middle/*+scatter()*/) .attr("cy", chart.yScale(cBoxPlot.objs.outliers[pt].value)); } } if (cBoxPlot.objs.extremes) { for (pt in cBoxPlot.objs.extremes) { cBoxPlot.objs.extremes[pt].point .attr("cx", middle/*+scatter()*/) .attr("cy", chart.yScale(cBoxPlot.objs.extremes[pt].value)); } } } }; function mapObjects(clear) { // Map everything to divs var cName, cBoxPlot; if (bOpts && bOpts.colors) { chart.boxPlots.colorFunct = updateColorFunction(bOpts.colors); } else { chart.boxPlots.colorFunct = colorFunct } for (cName in chart.groupObjs) { cBoxPlot = chart.groupObjs[cName].boxPlot; if (clear) { cBoxPlot.objs.g.remove() } cBoxPlot.objs.g = chart.groupObjs[cName].g.append("g").attr("class", "box-plot"); //Plot Box (default show) if (!bOpts || (bOpts && bOpts.showBox !== false)) { cBoxPlot.objs.box = cBoxPlot.objs.g.append("rect") .attr("class", "box") .style("fill", chart.boxPlots.colorFunct(cName)); } //Plot Median (default show) if (!bOpts || (bOpts && bOpts.showMedian !== false)) { cBoxPlot.objs.median = {line: null, circle: null}; cBoxPlot.objs.median.line = cBoxPlot.objs.g.append("line") .attr("class", "median"); cBoxPlot.objs.median.circle = cBoxPlot.objs.g.append("circle") .attr("class", "median") .attr('r', 3) .style("fill", chart.boxPlots.colorFunct(cName)); } // Plot Mean (default no plot) if (bOpts && bOpts.showMean) { cBoxPlot.objs.mean = {line: null, circle: null}; cBoxPlot.objs.mean.line = cBoxPlot.objs.g.append("line") .attr("class", "mean"); cBoxPlot.objs.mean.circle = cBoxPlot.objs.g.append("circle") .attr("class", "mean") .attr('r', 3) .style("fill", chart.boxPlots.colorFunct(cName)); } //Plot Whiskers (default show) if (!bOpts || (bOpts && bOpts.showWhiskers !== false)) { cBoxPlot.objs.upperWhisker = {fence: null, line: null}; cBoxPlot.objs.lowerWhisker = {fence: null, line: null}; cBoxPlot.objs.upperWhisker.fence = cBoxPlot.objs.g.append("line") .attr("class", "upper whisker") .style("stroke", chart.boxPlots.colorFunct(cName)); cBoxPlot.objs.upperWhisker.line = cBoxPlot.objs.g.append("line") .attr("class", "upper whisker") .style("stroke", chart.boxPlots.colorFunct(cName)); cBoxPlot.objs.lowerWhisker.fence = cBoxPlot.objs.g.append("line") .attr("class", "lower whisker") .style("stroke", chart.boxPlots.colorFunct(cName)); cBoxPlot.objs.lowerWhisker.line = cBoxPlot.objs.g.append("line") .attr("class", "lower whisker") .style("stroke", chart.boxPlots.colorFunct(cName)); } // Plot outliers (default show) //var scatter = function() { // var range = chartObj.xScale.rangeBand()/3; // return Math.floor(Math.random() * range)-range/2; //} if (!bOpts || (bOpts && bOpts.showOutliers !== false)) { if (!cBoxPlot.objs.outliers) calcAllOutliers(); var pt; if (cBoxPlot.objs.outliers.length) { var outDiv = cBoxPlot.objs.g.append("g").attr("class", "boxplot outliers"); for (pt in cBoxPlot.objs.outliers) { cBoxPlot.objs.outliers[pt].point = outDiv.append("circle") .attr("class", "outlier") .attr('r', 2) .style("fill", chart.boxPlots.colorFunct(cName)); } } if (cBoxPlot.objs.extremes.length) { var extDiv = cBoxPlot.objs.g.append("g").attr("class", "boxplot extremes"); for (pt in cBoxPlot.objs.extremes) { cBoxPlot.objs.extremes[pt].point = extDiv.append("circle") .attr("class", "extreme") .attr('r', 2) .style("stroke", chart.boxPlots.colorFunct(cName)); } } } } } mapObjects(); d3.select(window).on('resize.' + chart.chartSelector + '.boxPlot', chart.boxPlots.update); //Update the divs with the proper values chart.boxPlots.update(); return chart.boxPlots; }; return chart; }