Skip to content

Commit ccfcd7d

Browse files
authored
Merge pull request #3626 from plotly/pr-violin-3622
`violin`: honor `bandwidth` attribute even if values in a trace are identical
2 parents 5270abb + 4698df0 commit ccfcd7d

File tree

6 files changed

+60
-22
lines changed

6 files changed

+60
-22
lines changed

src/traces/violin/calc.js

+36-20
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,32 @@ module.exports = function calc(gd, trace) {
3737
var bandwidth = cdi.bandwidth = calcBandwidth(trace, cdi, vals);
3838
var span = cdi.span = calcSpan(trace, cdi, valAxis, bandwidth);
3939

40-
// step that well covers the bandwidth and is multiple of span distance
41-
var dist = span[1] - span[0];
42-
var n = Math.ceil(dist / (bandwidth / 3));
43-
var step = dist / n;
44-
45-
if(!isFinite(step) || !isFinite(n)) {
46-
Lib.error('Something went wrong with computing the violin span');
47-
cd[0].t.empty = true;
48-
return cd;
49-
}
50-
51-
var kde = helpers.makeKDE(cdi, trace, vals);
52-
cdi.density = new Array(n);
53-
54-
for(var k = 0, t = span[0]; t < (span[1] + step / 2); k++, t += step) {
55-
var v = kde(t);
56-
cdi.density[k] = {v: v, t: t};
57-
maxKDE = Math.max(maxKDE, v);
40+
if(cdi.min === cdi.max && bandwidth === 0) {
41+
// if span is zero and bandwidth is zero, we want a violin with zero width
42+
span = cdi.span = [cdi.min, cdi.max];
43+
cdi.density = [{v: 1, t: span[0]}];
44+
cdi.bandwidth = bandwidth;
45+
maxKDE = Math.max(maxKDE, 1);
46+
} else {
47+
// step that well covers the bandwidth and is multiple of span distance
48+
var dist = span[1] - span[0];
49+
var n = Math.ceil(dist / (bandwidth / 3));
50+
var step = dist / n;
51+
52+
if(!isFinite(step) || !isFinite(n)) {
53+
Lib.error('Something went wrong with computing the violin span');
54+
cd[0].t.empty = true;
55+
return cd;
56+
}
57+
58+
var kde = helpers.makeKDE(cdi, trace, vals);
59+
cdi.density = new Array(n);
60+
61+
for(var k = 0, t = span[0]; t < (span[1] + step / 2); k++, t += step) {
62+
var v = kde(t);
63+
cdi.density[k] = {v: v, t: t};
64+
maxKDE = Math.max(maxKDE, v);
65+
}
5866
}
5967

6068
maxCount = Math.max(maxCount, vals.length);
@@ -100,8 +108,16 @@ function silvermanRule(len, ssd, iqr) {
100108
function calcBandwidth(trace, cdi, vals) {
101109
var span = cdi.max - cdi.min;
102110

103-
// plot single-value violin with bandwidth of 1
104-
if(!span) return 1;
111+
// If span is zero
112+
if(!span) {
113+
if(trace.bandwidth) {
114+
return trace.bandwidth;
115+
} else {
116+
// if span is zero and no bandwidth is specified
117+
// it returns zero bandwidth which is a special case
118+
return 0;
119+
}
120+
}
105121

106122
// Limit how small the bandwidth can be.
107123
//
Loading
-30.8 KB
Loading
23.1 KB
Loading
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"data": [{
3+
"type": "violin",
4+
"y": [0, 0, 0, 0],
5+
"points": "all",
6+
"pointpos": 0,
7+
"meanline": {
8+
"visible": true
9+
}
10+
}, {
11+
"type": "violin",
12+
"y": [0, 0, 0, 0],
13+
"points": "all",
14+
"bandwidth": 0.25,
15+
"pointpos": 0
16+
}, {
17+
"type": "violin",
18+
"y": [0, 1, 1.5, 2],
19+
"points": "all",
20+
"pointpos": 0
21+
}]
22+
}

test/jasmine/tests/violin_test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ describe('Test violin calc:', function() {
280280
});
281281

282282
expect(cd.length).toBe(6, '# of violins');
283-
expect(cd.every(function(d) { return d.bandwidth; })).toBe(true, 'bandwidth');
283+
expect(cd.every(function(d) { return d.bandwidth; })).toBe(false, 'bandwidth');
284284
});
285285

286286
it('handle multi-value / single-but-unique-value case', function() {
@@ -289,7 +289,7 @@ describe('Test violin calc:', function() {
289289
});
290290

291291
expect(cd.length).toBe(1, '# of violins');
292-
expect(cd[0].bandwidth).toBe(1, 'bandwidth');
292+
expect(cd[0].bandwidth).toBe(0, 'bandwidth');
293293
});
294294
});
295295

0 commit comments

Comments
 (0)