Skip to content

Commit

Permalink
Merge pull request #3362 from plotly/multicat-fixes
Browse files Browse the repository at this point in the history
Multicategory sorting fixes
  • Loading branch information
etpinard committed Dec 21, 2018
2 parents 874ac4c + 96edd72 commit 997e720
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 44 deletions.
88 changes: 55 additions & 33 deletions src/plots/cartesian/set_convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var cleanNumber = Lib.cleanNumber;
var ms2DateTime = Lib.ms2DateTime;
var dateTime2ms = Lib.dateTime2ms;
var ensureNumber = Lib.ensureNumber;
var isArrayOrTypedArray = Lib.isArrayOrTypedArray;

var numConstants = require('../../constants/numerical');
var FP_SAFE = numConstants.FP_SAFE;
Expand Down Expand Up @@ -148,40 +149,11 @@ module.exports = function setConvert(ax, fullLayout) {

function setMultiCategoryIndex(arrayIn, len) {
var arrayOut = new Array(len);
var i;

// [ [arrayIn[0][i], arrayIn[1][i]], for i .. len ]
var tmp = new Array(len);
// [ [cnt, {$cat: index}], for j .. arrayIn.length ]
var seen = [[0, {}], [0, {}]];

if(Lib.isArrayOrTypedArray(arrayIn[0]) && Lib.isArrayOrTypedArray(arrayIn[1])) {
for(i = 0; i < len; i++) {
var v0 = arrayIn[0][i];
var v1 = arrayIn[1][i];
if(isValidCategory(v0) && isValidCategory(v1)) {
tmp[i] = [v0, v1];
if(!(v0 in seen[0][1])) {
seen[0][1][v0] = seen[0][0]++;
}
if(!(v1 in seen[1][1])) {
seen[1][1][v1] = seen[1][0]++;
}
}
}

tmp.sort(function(a, b) {
var ind0 = seen[0][1];
var d = ind0[a[0]] - ind0[b[0]];
if(d) return d;

var ind1 = seen[1][1];
return ind1[a[1]] - ind1[b[1]];
});
}

for(i = 0; i < len; i++) {
arrayOut[i] = setCategoryIndex(tmp[i]);
for(var i = 0; i < len; i++) {
var v0 = (arrayIn[0] || [])[i];
var v1 = (arrayIn[1] || [])[i];
arrayOut[i] = getCategoryIndex([v0, v1]);
}

return arrayOut;
Expand Down Expand Up @@ -330,6 +302,56 @@ module.exports = function setConvert(ax, fullLayout) {
if(Array.isArray(v) || (typeof v === 'string' && v !== '')) return v;
return ensureNumber(v);
};

ax.setupMultiCategory = function(fullData) {
var traceIndices = ax._traceIndices;
var i, j;

// [ [cnt, {$cat: index}], for 1,2 ]
var seen = ax._multicatSeen = [[0, {}], [0, {}]];
// [ [arrayIn[0][i], arrayIn[1][i]], for i .. N ]
var list = ax._multicatList = [];

for(i = 0; i < traceIndices.length; i++) {
var trace = fullData[traceIndices[i]];

if(axLetter in trace) {
var arrayIn = trace[axLetter];
var len = trace._length || Lib.minRowLength(arrayIn);

if(isArrayOrTypedArray(arrayIn[0]) && isArrayOrTypedArray(arrayIn[1])) {
for(j = 0; j < len; j++) {
var v0 = arrayIn[0][j];
var v1 = arrayIn[1][j];

if(isValidCategory(v0) && isValidCategory(v1)) {
list.push([v0, v1]);

if(!(v0 in seen[0][1])) {
seen[0][1][v0] = seen[0][0]++;
}
if(!(v1 in seen[1][1])) {
seen[1][1][v1] = seen[1][0]++;
}
}
}
}
}
}

list.sort(function(a, b) {
var ind0 = seen[0][1];
var d = ind0[a[0]] - ind0[b[0]];
if(d) return d;

var ind1 = seen[1][1];
return ind1[a[1]] - ind1[b[1]];
});

for(i = 0; i < list.length; i++) {
setCategoryIndex(list[i]);
}
};
}

// find the range value at the specified (linear) fraction of the axis
Expand Down
18 changes: 11 additions & 7 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
Expand Up @@ -2524,9 +2524,9 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts)
};

plots.doCalcdata = function(gd, traces) {
var axList = axisIDs.list(gd),
fullData = gd._fullData,
fullLayout = gd._fullLayout;
var axList = axisIDs.list(gd);
var fullData = gd._fullData;
var fullLayout = gd._fullLayout;

var trace, _module, i, j;

Expand Down Expand Up @@ -2579,7 +2579,7 @@ plots.doCalcdata = function(gd, traces) {
);
}

clearAxesCalc(axList);
setupAxisCategories(axList, fullData);

var hasCalcTransform = false;

Expand Down Expand Up @@ -2617,7 +2617,7 @@ plots.doCalcdata = function(gd, traces) {
}

// clear stuff that should recomputed in 'regular' loop
if(hasCalcTransform) clearAxesCalc(axList);
if(hasCalcTransform) setupAxisCategories(axList, fullData);

function calci(i, isContainer) {
trace = fullData[i];
Expand Down Expand Up @@ -2675,9 +2675,13 @@ plots.doCalcdata = function(gd, traces) {
Registry.getComponentMethod('errorbars', 'calc')(gd);
};

function clearAxesCalc(axList) {
function setupAxisCategories(axList, fullData) {
for(var i = 0; i < axList.length; i++) {
axList[i].clearCalc();
var ax = axList[i];
ax.clearCalc();
if(ax.type === 'multicategory') {
ax.setupMultiCategory(fullData);
}
}
}

Expand Down
Binary file added test/image/baselines/multicategory-sorting.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 modified test/image/baselines/multicategory-y.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 modified test/image/baselines/multicategory2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 52 additions & 0 deletions test/image/mocks/multicategory-sorting.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"data": [{
"x": [[1, 2, 3, 4, 5 ,6],
[1, 2, 1, 2, 1 ,2]],
"y": [1, 2, 3, 4, 5 ,6]
},

{
"x": [[1, 2, 3, 4, 5, 6],
[1, 2, 1, 2, 1, 2]],
"y": [1, 2, 3, 4, 5, 6],
"xaxis": "x2",
"yaxis": "y2"
},
{
"x": [[4, 5, 6, 7, 8, 9],
[1, 2, 1, 2, 1, 2]],
"y": [1, 2, 3, 4, 5, 6],
"xaxis": "x2",
"yaxis": "y2"
},

{
"x": [[1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 9],
[1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2]],
"y": [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6],
"xaxis": "x3",
"yaxis": "y3"
},

{
"x": [[1, 2, 3, 4, 5, 6],
[1, 2, 1, 2, null, 2]],
"y": [1, 2, 3, 4, 5, 6],
"xaxis": "x4",
"yaxis": "y4"
},
{
"x": [[4, 5, 6, 7, 8, 9],
[1, null, 1, 2, 1, 2]],
"y": [1, 2, 3, 4, 5, 6],
"xaxis": "x4",
"yaxis": "y4"
}],
"layout": {
"grid": {"rows": 2, "columns": 2, "pattern": "independent", "xgap": 0.1, "ygap": 0.15},
"width": 700,
"height": 400,
"margin": {"l": 20, "b": 40, "t": 20, "r": 20},
"showlegend": false
}
}
10 changes: 6 additions & 4 deletions test/jasmine/tests/axes_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2723,6 +2723,8 @@ describe('Test axes', function() {
ax = {type: axType};
Axes.setConvert(ax);
ax._categories = [];
ax._traceIndices = [0];
if(axType === 'multicategory') ax.setupMultiCategory([trace]);
return ax.makeCalcdata(trace, axLetter);
}

Expand Down Expand Up @@ -2856,7 +2858,7 @@ describe('Test axes', function() {
x: [['1', '2', '1', '2'], ['a', 'a', 'b', 'b']]
}, 'x', 'multicategory');

expect(out).toEqual([0, 1, 2, 3]);
expect(out).toEqual([0, 2, 1, 3]);
expect(ax._categories).toEqual([['1', 'a'], ['1', 'b'], ['2', 'a'], ['2', 'b']]);
expect(ax._categoriesMap).toEqual({'1,a': 0, '1,b': 1, '2,a': 2, '2,b': 3});
});
Expand All @@ -2866,7 +2868,7 @@ describe('Test axes', function() {
x: [['1', '2', null, '2'], ['a', 'a', 'b', 'b']]
}, 'x', 'multicategory');

expect(out).toEqual([0, 1, 2, BADNUM]);
expect(out).toEqual([0, 1, BADNUM, 2]);
expect(ax._categories).toEqual([['1', 'a'], ['2', 'a'], ['2', 'b']]);
expect(ax._categoriesMap).toEqual({'1,a': 0, '2,a': 1, '2,b': 2});
});
Expand All @@ -2876,7 +2878,7 @@ describe('Test axes', function() {
x: [['1', '2', '1', '2'], ['a', 'a', null, 'b']]
}, 'x', 'multicategory');

expect(out).toEqual([0, 1, 2, BADNUM]);
expect(out).toEqual([0, 1, BADNUM, 2]);
expect(ax._categories).toEqual([['1', 'a'], ['2', 'a'], ['2', 'b']]);
expect(ax._categoriesMap).toEqual({'1,a': 0, '2,a': 1, '2,b': 2});
});
Expand Down Expand Up @@ -2919,7 +2921,7 @@ describe('Test axes', function() {
]
}, 'x', 'multicategory');

expect(out).toEqual([0, 1, 2, 3]);
expect(out).toEqual([0, 2, 1, 3]);
expect(ax._categories).toEqual([[1, 10], [1, 20], [2, 10], [2, 20]]);
expect(ax._categoriesMap).toEqual({'1,10': 0, '1,20': 1, '2,10': 2, '2,20': 3});
});
Expand Down

0 comments on commit 997e720

Please sign in to comment.