From bb64307746fc956782efb2b2078cdc27c7c39bfb Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 7 Sep 2016 19:51:15 -0400 Subject: [PATCH 1/3] fallbacks for jitter algo with no IQR and lots of duplicate points --- src/traces/box/plot.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/traces/box/plot.js b/src/traces/box/plot.js index 81b47d715c3..e923fa3faf5 100644 --- a/src/traces/box/plot.js +++ b/src/traces/box/plot.js @@ -132,7 +132,9 @@ module.exports = function plot(gd, plotinfo, cdbox) { .data(function(d) { var pts = (trace.boxpoints === 'all') ? d.val : d.val.filter(function(v) { return (v < d.lf || v > d.uf); }), - spreadLimit = (d.q3 - d.q1) * JITTERSPREAD, + // normally use IQR, but if this is 0 or too small, use max-min + typicalSpread = Math.max((d.max - d.min) / 10, d.q3 - d.q1), + spreadLimit = typicalSpread * JITTERSPREAD, jitterFactors = [], maxJitterFactor = 0, i, @@ -155,7 +157,7 @@ module.exports = function plot(gd, plotinfo, cdbox) { else pmin = Math.max(pmin, d.uf); } - jitterFactor = Math.sqrt(spreadLimit * (i1 - i0) / (pmax - pmin)) || 0; + jitterFactor = Math.sqrt(spreadLimit * (i1 - i0) / (pmax - pmin + 1e-9)) || 0; jitterFactor = Lib.constrain(Math.abs(jitterFactor), 0, 1); jitterFactors.push(jitterFactor); From c8c5a030ce66dfbba73b3442020df79c98a67c88 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 7 Sep 2016 20:11:04 -0400 Subject: [PATCH 2/3] boxplots: handle jitter in edge case of no data range at all --- src/traces/box/plot.js | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/traces/box/plot.js b/src/traces/box/plot.js index e923fa3faf5..5d251686f9c 100644 --- a/src/traces/box/plot.js +++ b/src/traces/box/plot.js @@ -134,6 +134,7 @@ module.exports = function plot(gd, plotinfo, cdbox) { d.val.filter(function(v) { return (v < d.lf || v > d.uf); }), // normally use IQR, but if this is 0 or too small, use max-min typicalSpread = Math.max((d.max - d.min) / 10, d.q3 - d.q1), + minSpread = typicalSpread * 1e-9, spreadLimit = typicalSpread * JITTERSPREAD, jitterFactors = [], maxJitterFactor = 0, @@ -146,22 +147,32 @@ module.exports = function plot(gd, plotinfo, cdbox) { // dynamic jitter if(trace.jitter) { - for(i = 0; i < pts.length; i++) { - i0 = Math.max(0, i - JITTERCOUNT); - pmin = pts[i0]; - i1 = Math.min(pts.length - 1, i + JITTERCOUNT); - pmax = pts[i1]; - - if(trace.boxpoints !== 'all') { - if(pts[i] < d.lf) pmax = Math.min(pmax, d.lf); - else pmin = Math.max(pmin, d.uf); + if(typicalSpread === 0) { + // edge case of no spread at all: fall back to max jitter + maxJitterFactor = 1; + jitterFactors = new Array(pts.length); + for(i = 0; i < pts.length; i++) { + jitterFactors[i] = 1; + } + } + else { + for(i = 0; i < pts.length; i++) { + i0 = Math.max(0, i - JITTERCOUNT); + pmin = pts[i0]; + i1 = Math.min(pts.length - 1, i + JITTERCOUNT); + pmax = pts[i1]; + + if(trace.boxpoints !== 'all') { + if(pts[i] < d.lf) pmax = Math.min(pmax, d.lf); + else pmin = Math.max(pmin, d.uf); + } + + jitterFactor = Math.sqrt(spreadLimit * (i1 - i0) / (pmax - pmin + minSpread)) || 0; + jitterFactor = Lib.constrain(Math.abs(jitterFactor), 0, 1); + + jitterFactors.push(jitterFactor); + maxJitterFactor = Math.max(jitterFactor, maxJitterFactor); } - - jitterFactor = Math.sqrt(spreadLimit * (i1 - i0) / (pmax - pmin + 1e-9)) || 0; - jitterFactor = Lib.constrain(Math.abs(jitterFactor), 0, 1); - - jitterFactors.push(jitterFactor); - maxJitterFactor = Math.max(jitterFactor, maxJitterFactor); } newJitter = trace.jitter * 2 / maxJitterFactor; } From ae7bcab5994bd37236acf0550fd406b1282947fd Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 7 Sep 2016 23:09:24 -0400 Subject: [PATCH 3/3] box plot jitter edge cases test image --- .../baselines/box_plot_jitter_edge_cases.png | Bin 0 -> 26778 bytes .../mocks/box_plot_jitter_edge_cases.json | 32 ++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 test/image/baselines/box_plot_jitter_edge_cases.png create mode 100644 test/image/mocks/box_plot_jitter_edge_cases.json diff --git a/test/image/baselines/box_plot_jitter_edge_cases.png b/test/image/baselines/box_plot_jitter_edge_cases.png new file mode 100644 index 0000000000000000000000000000000000000000..1853a20a16c238471ba9b663ce286cb0b632583f GIT binary patch literal 26778 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