Skip to content

Commit

Permalink
Merge pull request #2305 from krassowski/notched_box_plot
Browse files Browse the repository at this point in the history
 Implement notched box plots, closes #2286
  • Loading branch information
alexcjohnson authored Jan 30, 2018
2 parents 7de0c7f + 8dde2d2 commit 28f7ea2
Show file tree
Hide file tree
Showing 8 changed files with 623 additions and 4 deletions.
21 changes: 21 additions & 0 deletions src/traces/box/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,27 @@ module.exports = {
'For example, with 1, the whiskers are as wide as the box(es).'
].join(' ')
},
notched: {
valType: 'boolean',
role: 'style',
editType: 'calcIfAutorange',
description: [
'Determines whether or not notches should be drawn.'
].join(' ')
},
notchwidth: {
valType: 'number',
min: 0,
max: 0.5,
dflt: 0.25,
role: 'style',
editType: 'calcIfAutorange',
description: [
'Sets the width of the notches relative to',
'the box\' width.',
'For example, with 0, the notches are as wide as the box(es).'
].join(' ')
},
boxpoints: {
valType: 'enumerated',
values: ['all', 'outliers', 'suspectedoutliers', false],
Expand Down
7 changes: 7 additions & 0 deletions src/traces/box/calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ module.exports = function calc(gd, trace) {
cdi.lo = 4 * cdi.q1 - 3 * cdi.q3;
cdi.uo = 4 * cdi.q3 - 3 * cdi.q1;


// lower and upper notches ~95% Confidence Intervals for median
var iqr = cdi.q3 - cdi.q1;
var mci = 1.57 * iqr / Math.sqrt(bvLen);
cdi.ln = cdi.med - mci;
cdi.un = cdi.med + mci;

cd.push(cdi);
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/traces/box/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
coerce('whiskerwidth');
coerce('boxmean');

var notched = coerce('notched', traceIn.notchwidth !== undefined);
if(notched) coerce('notchwidth');

handlePointsDefaults(traceIn, traceOut, coerce, {prefix: 'box'});
}

Expand Down
24 changes: 20 additions & 4 deletions src/traces/box/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ function plotBoxAndWhiskers(sel, axes, trace, t) {
var wdPos = t.wdPos || 0;
var bPosPxOffset = t.bPosPxOffset || 0;
var whiskerWidth = trace.whiskerwidth || 0;
var notched = trace.notched || false;
var nw = notched ? 1 - 2 * trace.notchwidth : 1;

// to support for one-sided box
var bdPos0;
Expand All @@ -125,6 +127,8 @@ function plotBoxAndWhiskers(sel, axes, trace, t) {
var pos1 = posAxis.c2p(pos + bPos + bdPos1, true) + bPosPxOffset;
var posw0 = posAxis.c2p(pos + bPos - wdPos, true) + bPosPxOffset;
var posw1 = posAxis.c2p(pos + bPos + wdPos, true) + bPosPxOffset;
var posm0 = posAxis.c2p(pos + bPos - bdPos0 * nw, true) + bPosPxOffset;
var posm1 = posAxis.c2p(pos + bPos + bdPos1 * nw, true) + bPosPxOffset;
var q1 = valAxis.c2p(d.q1, true);
var q3 = valAxis.c2p(d.q3, true);
// make sure median isn't identical to either of the
Expand All @@ -135,18 +139,30 @@ function plotBoxAndWhiskers(sel, axes, trace, t) {
);
var lf = valAxis.c2p(trace.boxpoints === false ? d.min : d.lf, true);
var uf = valAxis.c2p(trace.boxpoints === false ? d.max : d.uf, true);
var ln = valAxis.c2p(d.ln, true);
var un = valAxis.c2p(d.un, true);

if(trace.orientation === 'h') {
d3.select(this).attr('d',
'M' + m + ',' + pos0 + 'V' + pos1 + // median line
'M' + q1 + ',' + pos0 + 'V' + pos1 + 'H' + q3 + 'V' + pos0 + 'Z' + // box
'M' + m + ',' + posm0 + 'V' + posm1 + // median line
'M' + q1 + ',' + pos0 + 'V' + pos1 + // left edge
(notched ? 'H' + ln + 'L' + m + ',' + posm1 + 'L' + un + ',' + pos1 : '') + // top notched edge
'H' + q3 + // end of the top edge
'V' + pos0 + // right edge
(notched ? 'H' + un + 'L' + m + ',' + posm0 + 'L' + ln + ',' + pos0 : '') + // bottom notched edge
'Z' + // end of the box
'M' + q1 + ',' + posc + 'H' + lf + 'M' + q3 + ',' + posc + 'H' + uf + // whiskers
((whiskerWidth === 0) ? '' : // whisker caps
'M' + lf + ',' + posw0 + 'V' + posw1 + 'M' + uf + ',' + posw0 + 'V' + posw1));
} else {
d3.select(this).attr('d',
'M' + pos0 + ',' + m + 'H' + pos1 + // median line
'M' + pos0 + ',' + q1 + 'H' + pos1 + 'V' + q3 + 'H' + pos0 + 'Z' + // box
'M' + posm0 + ',' + m + 'H' + posm1 + // median line
'M' + pos0 + ',' + q1 + 'H' + pos1 + // top of the box
(notched ? 'V' + ln + 'L' + posm1 + ',' + m + 'L' + pos1 + ',' + un : '') + // notched right edge
'V' + q3 + // end of the right edge
'H' + pos0 + // bottom of the box
(notched ? 'V' + un + 'L' + posm0 + ',' + m + 'L' + pos0 + ',' + ln : '') + // notched left edge
'Z' + // end of the box
'M' + posc + ',' + q1 + 'V' + lf + 'M' + posc + ',' + q3 + 'V' + uf + // whiskers
((whiskerWidth === 0) ? '' : // whisker caps
'M' + posw0 + ',' + lf + 'H' + posw1 + 'M' + posw0 + ',' + uf + 'H' + posw1));
Expand Down
Binary file added test/image/baselines/box_horz_notched.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/baselines/box_notched.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
290 changes: 290 additions & 0 deletions test/image/mocks/box_horz_notched.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
{
"data":[
{
"x":[
90,
88,
55,
88,
72,
100,
88,
25,
92,
100,
82,
82,
90,
68,
85,
82,
40,
100,
92,
82,
55,
62,
85,
100,
75,
88,
78,
80,
92,
100,
88,
72,
95,
80,
90,
72,
100,
100,
75,
82,
60,
90,
85,
90,
38,
78,
82,
100,
90,
80,
80,
100,
70,
100,
82,
62,
92,
100,
80,
100,
88,
85
],
"line":{
"color":"#1c9099"
},
"type":"box",
"name":"Notched = True",
"orientation": "h",
"notched":true
},
{
"x":[
70,
65,
85,
75,
72,
75,
90,
88,
85,
80,
92,
85,
85,
75,
72,
80,
42,
80,
95,
90,
62,
65,
65,
82,
68,
48,
57,
95,
70,
100,
80,
95,
78,
80,
80,
85,
90,
100,
52,
85,
72,
70,
45,
75,
85,
95,
65,
70,
85,
70,
85,
35,
90,
95,
95,
65,
62,
48,
60,
85,
85,
90,
70,
68
],
"line":{
"color":"#1c9099"
},
"type":"box",
"name":"Notch width = 0.1",
"notched":true,
"orientation": "h",
"notchwidth":0.1
},
{
"x":[
95,
75,
70,
72,
52,
70,
82,
90,
95,
80,
68,
88,
82,
52,
80,
78,
57,
88,
88,
100,
50,
65,
78,
92,
65,
50,
60,
88,
100,
50,
90,
70,
60,
72,
75,
95,
100,
45,
68,
72,
45,
60,
78,
85,
92,
45,
68,
70,
85,
82,
62,
75,
100,
80,
65,
52,
48,
57,
100,
72,
100,
80,
65
],
"line":{
"color":"#1c9099"
},
"type":"box",
"name":"Notch width = 0",
"notched":true,
"orientation": "h",
"notchwidth":0
},
{
"x":[
86,
64,
86.2,
91.1,
86,
85.3,
85.30000000000001,
63.7,
47.400000000000006,
89.2,
91.3,
91.6,
55.099999999999994,
64.1,
76.2,
91.4,
68.9,
60.8,
64.5,
87.7,
21,
97.6,
90.9,
86.6,
100,
33.6,
82.7,
63.599999999999994,
55.5,
80.7,
85,
92.30000000000001,
48.9,
85
],
"line":{
"color":"#1c9099"
},
"type":"box",
"name":"Outside of hinges",
"orientation": "h",
"notched":true
}
],
"layout":{
"showlegend":false,
"xaxis":{
"title":"Grade [%]",
"type":"linear"
},
"title":"Based on Fig 4.4a: Course Grade Distributions",
"yaxis":{
"type":"category"
},
"height":598,
"width":1080,
"margin":{
"l":110
},
"autosize":true
}
}
Loading

0 comments on commit 28f7ea2

Please sign in to comment.