Skip to content

Commit 359fbbc

Browse files
committed
add bingroup attrs (!)
1 parent 6093d34 commit 359fbbc

File tree

4 files changed

+317
-0
lines changed

4 files changed

+317
-0
lines changed

Diff for: src/traces/histogram/attributes.js

+15
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,21 @@ module.exports = {
193193
].join(' ')
194194
},
195195

196+
bingroup: {
197+
valType: 'string',
198+
role: 'info',
199+
dflt: '',
200+
editType: 'calc',
201+
description: [
202+
'Set a group of histogram traces which will have compatible bin settings.',
203+
'Note that traces on the same subplot and with the same *orientation*',
204+
'under `barmode` *stack*, *relative* and *group* are forced into the same bingroup,',
205+
'Using `bingroup`, traces under `barmode` *overlay* and on different axes',
206+
'(of the same axis type) can have compatible bin settings.',
207+
'Note that histogram and histogram2d* trace can share the same `bingroup`'
208+
].join(' ')
209+
},
210+
196211
hovertemplate: hovertemplateAttrs({}, {
197212
keys: constants.eventDataKeys
198213
}),

Diff for: src/traces/histogram2d/attributes.js

+24
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,30 @@ module.exports = extendFlat(
4444
autobinx: histogramAttrs.autobinx,
4545
autobiny: histogramAttrs.autobiny,
4646

47+
bingroup: extendFlat({}, histogramAttrs.bingroup, {
48+
description: [
49+
'Set the `xbingroup` and `ybingroup` default prefix',
50+
'For example, setting a `bingroup` of *1* on two histogram2d traces',
51+
'will make them their x-bins and y-bins match separately.'
52+
].join(' ')
53+
}),
54+
xbingroup: extendFlat({}, histogramAttrs.bingroup, {
55+
description: [
56+
'Set a group of histogram traces which will have compatible x-bin settings.',
57+
'Using `xbingroup`, histogram2d and histogram2dcontour traces ',
58+
'(on axes of the same axis type) can have compatible x-bin settings.',
59+
'Note that the same `xbingroup` value can be used to set (1D) histogram `bingroup`'
60+
].join(' ')
61+
}),
62+
ybingroup: extendFlat({}, histogramAttrs.bingroup, {
63+
description: [
64+
'Set a group of histogram traces which will have compatible y-bin settings.',
65+
'Using `ybingroup`, histogram2d and histogram2dcontour traces ',
66+
'(on axes of the same axis type) can have compatible y-bin settings.',
67+
'Note that the same `ybingroup` value can be used to set (1D) histogram `bingroup`'
68+
].join(' ')
69+
}),
70+
4771
xgap: heatmapAttrs.xgap,
4872
ygap: heatmapAttrs.ygap,
4973
zsmooth: heatmapAttrs.zsmooth,

Diff for: src/traces/histogram2dcontour/attributes.js

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ module.exports = extendFlat({
2929
autobinx: histogram2dAttrs.autobinx,
3030
autobiny: histogram2dAttrs.autobiny,
3131

32+
bingroup: histogram2dAttrs.bingroup,
33+
xbingroup: histogram2dAttrs.xbingroup,
34+
ybingroup: histogram2dAttrs.ybingroup,
35+
3236
autocontour: contourAttrs.autocontour,
3337
ncontours: contourAttrs.ncontours,
3438
contours: contourAttrs.contours,

Diff for: test/jasmine/tests/histogram_test.js

+274
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,280 @@ describe('Test histogram', function() {
186186
});
187187
});
188188

189+
describe('cross-trace bingroup logic:', function() {
190+
var gd;
191+
192+
beforeEach(function() {
193+
spyOn(Lib, 'warn');
194+
});
195+
196+
function _assert(msg, exp, warnMsg) {
197+
var allBinOpts = gd._fullLayout._histogramBinOpts;
198+
var groups = Object.keys(allBinOpts);
199+
200+
expect(groups.length).toBe(exp.length, 'same # of bin groups| ' + msg);
201+
202+
var eGroups = exp.map(function(expi) { return expi[0]; });
203+
expect(groups).toEqual(eGroups, 'same bin groups| ' + msg);
204+
205+
exp.forEach(function(expi) {
206+
var k = expi[0];
207+
var binOpts = allBinOpts[k];
208+
209+
if(!binOpts) {
210+
return fail('bingroup ' + k + ' does NOT exist| ' + msg);
211+
}
212+
213+
var traces = binOpts.traces || [];
214+
215+
if(!traces.length) {
216+
return fail('traces list for bingroup ' + k + ' is empty| ' + msg);
217+
}
218+
219+
expect(traces.length).toBe(expi[1].length, 'same # of tracked traces|' + msg);
220+
221+
traces.forEach(function(t, i) {
222+
expect(t.index)
223+
.toBe(expi[1][i], 'tracks same traces[' + i + ']|' + msg);
224+
expect(t['_' + binOpts.dirs[i] + 'bingroup'])
225+
.toBe(k, '_(x|y)bingroup key in trace' + i + '| ' + msg);
226+
});
227+
});
228+
229+
if(warnMsg) {
230+
expect(Lib.warn).toHaveBeenCalledWith(warnMsg);
231+
} else {
232+
expect(Lib.warn).toHaveBeenCalledTimes(0);
233+
}
234+
}
235+
236+
it('should group traces w/ same axes and w/ same orientation', function() {
237+
var barModes = ['group', 'stack'];
238+
239+
barModes.forEach(function(mode) {
240+
gd = {
241+
data: [
242+
{type: 'histogram', y: [1]},
243+
{type: 'histogram', y: [2]},
244+
245+
{type: 'histogram', y: [1], xaxis: 'x2'},
246+
{type: 'histogram', y: [3], xaxis: 'x2'},
247+
248+
{type: 'histogram', y: [3]},
249+
{type: 'histogram', y: [2], xaxis: 'x2'},
250+
251+
{type: 'histogram', x: [1]},
252+
{uid: 'solo', type: 'histogram', x: [2], yaxis: 'y2'},
253+
{type: 'histogram', x: [2]}
254+
],
255+
layout: {barmode: mode}
256+
};
257+
supplyAllDefaults(gd);
258+
_assert('under barmode:' + mode, [
259+
['xyy', [0, 1, 4]],
260+
['x2yy', [2, 3, 5]],
261+
['xyx', [6, 8]],
262+
['solo__x', [7]]
263+
]);
264+
});
265+
});
266+
267+
it('should group traces on matching axes and w/ same orientation', function() {
268+
var barModes = ['group', 'stack'];
269+
270+
barModes.forEach(function(mode) {
271+
gd = {
272+
data: [
273+
{type: 'histogram', y: [1]},
274+
{type: 'histogram', y: [2], xaxis: 'x2'},
275+
{type: 'histogram', x: [1], yaxis: 'y2'},
276+
{type: 'histogram', x: [2], yaxis: 'y2'},
277+
],
278+
layout: {
279+
barmode: mode,
280+
xaxis2: {matches: 'x'},
281+
yaxis2: {matches: 'x'}
282+
}
283+
};
284+
supplyAllDefaults(gd);
285+
_assert('under barmode:' + mode, [
286+
['g0yy', [0, 1]],
287+
['g0g0x', [2, 3]]
288+
]);
289+
});
290+
});
291+
292+
it('should not group traces by default under barmode:overlay ', function() {
293+
gd = {
294+
data: [
295+
{uid: 'a', type: 'histogram', y: [1]},
296+
{uid: 'b', type: 'histogram', y: [2]},
297+
298+
{uid: 'c', type: 'histogram', y: [1], xaxis: 'x2'},
299+
{uid: 'd', type: 'histogram', y: [3], xaxis: 'x2'},
300+
301+
{uid: 'e', type: 'histogram', y: [3]},
302+
{uid: 'f', type: 'histogram', y: [2], xaxis: 'x2'},
303+
304+
{uid: 'g', type: 'histogram', x: [1]},
305+
{uid: 'h', type: 'histogram', x: [2], yaxis: 'y2'},
306+
{uid: 'i', type: 'histogram', x: [2]}
307+
],
308+
layout: {barmode: 'overlay'}
309+
};
310+
supplyAllDefaults(gd);
311+
_assert('', [
312+
['a__y', [0]], ['b__y', [1]], ['c__y', [2]],
313+
['d__y', [3]], ['e__y', [4]], ['f__y', [5]],
314+
['g__x', [6]], ['h__x', [7]], ['i__x', [8]]
315+
]);
316+
});
317+
318+
it('should not group histogram2d* traces by default', function() {
319+
gd = {
320+
data: [
321+
{uid: 'a', type: 'histogram2d', x: [1], y: [1]},
322+
{uid: 'b', type: 'histogram2d', x: [2], y: [2]},
323+
{uid: 'c', type: 'histogram2dcontour', x: [1], y: [1], xaxis: 'x2', yaxis: 'y2'},
324+
{uid: 'd', type: 'histogram2dcontour', x: [2], y: [2], xaxis: 'x2', yaxis: 'y2'},
325+
],
326+
layout: {}
327+
};
328+
supplyAllDefaults(gd);
329+
_assert('N.B. one bingroup for x, one bingroup for y for each trace', [
330+
['a__x', [0]], ['a__y', [0]],
331+
['b__x', [1]], ['b__y', [1]],
332+
['c__x', [2]], ['c__y', [2]],
333+
['d__x', [3]], ['d__y', [3]]
334+
]);
335+
});
336+
337+
it('should be able to group traces by *bingroup* under barmode:overlay ', function() {
338+
gd = {
339+
data: [
340+
{bingroup: '1', type: 'histogram', y: [1]},
341+
{uid: 'b', type: 'histogram', y: [2]},
342+
{bingroup: '2', type: 'histogram', y: [1], xaxis: 'x2'},
343+
{bingroup: '1', type: 'histogram', y: [3], xaxis: 'x2'},
344+
{bingroup: '2', type: 'histogram', y: [3]},
345+
{uid: 'f', type: 'histogram', y: [2], xaxis: 'x2'},
346+
{bingroup: '3', type: 'histogram', x: [1]},
347+
{bingroup: '1', type: 'histogram', x: [2], yaxis: 'y2'},
348+
{bingroup: '3', type: 'histogram', x: [2]}
349+
],
350+
layout: {barmode: 'overlay'}
351+
};
352+
supplyAllDefaults(gd);
353+
_assert('', [
354+
['1', [0, 3, 7]],
355+
['2', [2, 4]],
356+
['3', [6, 8]],
357+
['b__y', [1]],
358+
['f__y', [5]]
359+
]);
360+
});
361+
362+
it('should be able to group histogram2d traces by *bingroup*', function() {
363+
gd = {
364+
data: [
365+
{uid: 'a', type: 'histogram2d', x: [1], y: [1]},
366+
{uid: 'b', type: 'histogram2d', x: [1], y: [1]},
367+
{bingroup: '1', type: 'histogram2d', x: [1], y: [1]},
368+
{bingroup: '1', type: 'histogram2d', x: [1], y: [1]},
369+
{uid: 'e', type: 'histogram2d', x: [1], y: [1]},
370+
]
371+
};
372+
supplyAllDefaults(gd);
373+
_assert('', [
374+
['a__x', [0]], ['a__y', [0]],
375+
['b__x', [1]], ['b__y', [1]],
376+
['1__x', [2, 3]], ['1__y', [2, 3]],
377+
['e__x', [4]], ['e__y', [4]]
378+
]);
379+
});
380+
381+
// TODO figure out what to do in this case!
382+
it('should be able to group histogram and histogram2d* traces together', function() {
383+
gd = {
384+
data: [
385+
{bingroup: '1', type: 'histogram', y: [1]},
386+
{bingroup: '1', type: 'histogram', y: [3], xaxis: 'x2'},
387+
{bingroup: '1', type: 'histogram2d', x: [1], y: [3]},
388+
{bingroup: '1', type: 'histogram2dcontour', x: [1], y: [3]}
389+
],
390+
layout: {barmode: 'overlay'}
391+
};
392+
supplyAllDefaults(gd);
393+
_assert('N.B. histogram2d* indices show up twice, once for x-bins, once for y-bins', [
394+
['1', [0, 1]],
395+
['1__x', [2, 3]],
396+
['1__y', [2, 3]]
397+
]);
398+
});
399+
400+
it('should not group traces across axes of different types', function() {
401+
gd = {
402+
data: [
403+
{uid: 'a', bingroup: '1', type: 'histogram', y: [1]},
404+
{uid: 'b', bingroup: '1', type: 'histogram', y: ['cats'], yaxis: 'y2'},
405+
],
406+
layout: {barmode: 'overlay'}
407+
};
408+
supplyAllDefaults(gd);
409+
410+
_assert('', [
411+
['1', [0]],
412+
['b__y', [1]]
413+
],
414+
'Attempted to group the bins of trace 1 set on a type:category axis ' +
415+
'with bins on type:linear axis.'
416+
);
417+
});
418+
419+
it('should force traces that "have to match" to have same bingroup (stack case)', function() {
420+
gd = {
421+
data: [
422+
// these 3 traces "have to match"
423+
{bingroup: '1', type: 'histogram', y: [1]},
424+
{type: 'histogram', y: [1]},
425+
{bingroup: '2', type: 'histogram', y: [2]}
426+
],
427+
layout: {barmode: 'stack'}
428+
};
429+
supplyAllDefaults(gd);
430+
431+
_assert('used first valid bingroup to identify bin opts', [
432+
['1', [0, 1, 2]]
433+
],
434+
'Trace 2 must match within bingroup 1.' +
435+
' Ignoring its bingroup: 2 setting.'
436+
);
437+
});
438+
439+
it('traces that "have to match" can be grouped with traces that do not have to match using *bingroup*', function() {
440+
gd = {
441+
data: [
442+
// these 2 traces "have to match"
443+
{bingroup: '1', type: 'histogram', y: [1]},
444+
{type: 'histogram', y: [1]},
445+
// this one does not have to match with the above two,
446+
// (it's on another subplot), but it can be grouped
447+
{bingroup: '1', type: 'histogram', y: [2], xaxis: 'x2', yaxis: 'y2'},
448+
// this one does not have to match either
449+
// (it's a histogram2d* traces), but it can be grouped
450+
// TODO should this be just "bingroup"???
451+
{xbingroup: '1', ybingroup: '1', type: 'histogram2d', x: [3], y: [3]}
452+
],
453+
layout: {}
454+
};
455+
supplyAllDefaults(gd);
456+
457+
_assert('', [
458+
// N.B ordering in *binOpts.traces* does not matter
459+
['1', [0, 1, 3, 3, 2]]
460+
]);
461+
});
462+
});
189463

190464
describe('calc', function() {
191465
function _calc(opts, extraTraces, layout, prependExtras) {

0 commit comments

Comments
 (0)