diff --git a/src/traces/box/attributes.js b/src/traces/box/attributes.js index a8144de00d0..8b31df30dc9 100644 --- a/src/traces/box/attributes.js +++ b/src/traces/box/attributes.js @@ -100,7 +100,12 @@ module.exports = { role: 'style', editType: 'calc', description: [ - 'Determines whether or not notches should be drawn.' + 'Determines whether or not notches are drawn.', + 'Notches displays a confidence interval around the median.', + 'We compute the confidence interval as median +/- 1.57 / IQR * sqrt(N),', + 'where IQR is the interquartile range and N is the sample size.', + 'If two boxes\' notches do not overlap there is 95% confidence their medians differ.', + 'See https://sites.google.com/site/davidsstatistics/home/notched-box-plots for more info.' ].join(' ') }, notchwidth: { diff --git a/src/traces/box/calc.js b/src/traces/box/calc.js index 092d5795fdd..992c2dc64d7 100644 --- a/src/traces/box/calc.js +++ b/src/traces/box/calc.js @@ -69,6 +69,9 @@ module.exports = function calc(gd, trace) { Lib.identity : function(pt) { return (pt.v < cdi.lf || pt.v > cdi.uf); }; + var minLowerNotch = Infinity; + var maxUpperNotch = -Infinity; + // build calcdata trace items, one item per distinct position for(i = 0; i < pLen; i++) { if(ptsPerBin[i].length > 0) { @@ -123,6 +126,8 @@ module.exports = function calc(gd, trace) { var mci = 1.57 * iqr / Math.sqrt(bvLen); cdi.ln = cdi.med - mci; cdi.un = cdi.med + mci; + minLowerNotch = Math.min(minLowerNotch, cdi.ln); + maxUpperNotch = Math.max(maxUpperNotch, cdi.un); cdi.pts2 = pts.filter(ptFilterFn); @@ -131,8 +136,11 @@ module.exports = function calc(gd, trace) { } calcSelection(cd, trace); - var extremes = Axes.findExtremes(valAxis, val, {padded: true}); - trace._extremes[valAxis._id] = extremes; + + trace._extremes[valAxis._id] = Axes.findExtremes(valAxis, + trace.notched ? val.concat([minLowerNotch, maxUpperNotch]) : val, + {padded: true} + ); if(cd.length > 0) { cd[0].t = { diff --git a/test/image/baselines/box_notched-inverted-end.png b/test/image/baselines/box_notched-inverted-end.png new file mode 100644 index 00000000000..2e86923f0ca Binary files /dev/null and b/test/image/baselines/box_notched-inverted-end.png differ diff --git a/test/image/mocks/box_notched-inverted-end.json b/test/image/mocks/box_notched-inverted-end.json new file mode 100644 index 00000000000..e8297b640c9 --- /dev/null +++ b/test/image/mocks/box_notched-inverted-end.json @@ -0,0 +1,36 @@ +{ + "data": [ + { + "x": [113.35, 29.54, 21.7, 113.35, 29.54, 21.7], + "name": "[113.35,29.54,21.7,113.35,29.54,21.7]", + "type": "box", + "notched": true + }, + { + "x": [39.4, 88.63, 39.4, 88.63], + "name": "[39.4,88.63,39.4,88.63]", + "type": "box", + "notched": true + } + ], + "layout": { + "title": { + "text": "Samples where the confidence interval
median ± 1.57 / IQR * sqrt(N)
go beyond the fences", + "x": 0.02 + }, + "xaxis": { + "showline": true, + "mirror": true, + "zeroline": false + }, + "yaxis": { + "showline": true, + "mirror": true, + "zeroline": false + }, + "showlegend": false, + "margin": {"l": 220, "b": 20, "r": 20}, + "width": 600, + "height": 300 + } +}