diff --git a/src/plots/cartesian/axis_autotype.js b/src/plots/cartesian/axis_autotype.js index e5a399784b3..c7b90565985 100644 --- a/src/plots/cartesian/axis_autotype.js +++ b/src/plots/cartesian/axis_autotype.js @@ -38,15 +38,20 @@ function linearOK(array) { // 2- or 4-digit integers can be both, so require twice as many // dates as non-dates, to exclude cases with mostly 2 & 4 digit // numbers and a few dates +// as with categories, consider DISTINCT values only. function moreDates(a, calendar) { - var dcnt = 0, - ncnt = 0, - // test at most 1000 points, evenly spaced - inc = Math.max(1, (a.length - 1) / 1000), - ai; + // test at most 1000 points, evenly spaced + var inc = Math.max(1, (a.length - 1) / 1000); + var dcnt = 0; + var ncnt = 0; + var seen = {}; for(var i = 0; i < a.length; i += inc) { - ai = a[Math.round(i)]; + var ai = a[Math.round(i)]; + var stri = String(ai); + if(seen[stri]) continue; + seen[stri] = 1; + if(Lib.isDateTime(ai, calendar)) dcnt += 1; if(isNumeric(ai)) ncnt += 1; } @@ -55,18 +60,23 @@ function moreDates(a, calendar) { } // are the (x,y)-values in gd.data mostly text? -// require twice as many categories as numbers +// require twice as many DISTINCT categories as distinct numbers function category(a) { // test at most 1000 points - var inc = Math.max(1, (a.length - 1) / 1000), - curvenums = 0, - curvecats = 0, - ai; + var inc = Math.max(1, (a.length - 1) / 1000); + var curvenums = 0; + var curvecats = 0; + var seen = {}; for(var i = 0; i < a.length; i += inc) { - ai = a[Math.round(i)]; - if(Lib.cleanNumber(ai) !== BADNUM) curvenums++; - else if(typeof ai === 'string' && ai !== '' && ai !== 'None') curvecats++; + var ai = a[Math.round(i)]; + var stri = String(ai); + if(seen[stri]) continue; + seen[stri] = 1; + + if(typeof ai === 'boolean') curvecats++; + else if(Lib.cleanNumber(ai) !== BADNUM) curvenums++; + else if(typeof ai === 'string') curvecats++; } return curvecats > curvenums * 2; diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index bfc15effcc0..4962bd75908 100644 --- a/test/jasmine/tests/axes_test.js +++ b/test/jasmine/tests/axes_test.js @@ -189,6 +189,80 @@ describe('Test axes', function() { fullData = []; }); + describe('autotype', function() { + function supplyWithTrace(trace) { + var fullTrace = Lib.extendDeep( + {type: 'scatter', xaxis: 'x', yaxis: 'y'}, + trace + ); + layoutIn = {xaxis: {}, yaxis: {}}; + supplyLayoutDefaults(layoutIn, layoutOut, [fullTrace]); + } + + function checkTypes(xType, yType, msg) { + expect(layoutOut.xaxis.type).toBe(xType, msg); + expect(layoutOut.yaxis.type).toBe(yType, msg); + } + + it('treats booleans as categories', function() { + supplyWithTrace({x: [0, 1, 2], y: [true, false, true]}); + checkTypes('linear', 'category'); + }); + + it('sees a single "None" or "" as a category', function() { + supplyWithTrace({x: ['None'], y: ['']}); + checkTypes('category', 'category'); + }); + + it('lets a single number beat up to two distinct categories', function() { + supplyWithTrace({ + x: ['2.1', 'N/A', '', 'N/A', '', 'N/A', 'N/A', '', '', ''], + y: [0, 'None', true, 'None', 'None', 'None', 'None', 'None'] + }); + checkTypes('linear', 'linear'); + }); + + it('turns back to category with >2 per distinct number', function() { + supplyWithTrace({ + x: [4, 4, 4, 4, 4, 4, 4, 4, 'Yes', 'No', 'Maybe'], + y: [1, 2, 1, 2, 1, 2, true, false, '', 'None', 'nan'] + }); + checkTypes('category', 'category'); + }); + + it('works with world calendars', function() { + // these are only valid dates in chinese + var intercalary = ['1995-08i-01', '1995-08i-29', '1984-10i-15']; + supplyWithTrace({ + x: intercalary, xcalendar: 'chinese', + y: intercalary, ycalendar: 'gregorian' + }); + checkTypes('date', 'category'); + }); + + it('requires >twice as many distinct dates as numbers', function() { + supplyWithTrace({ + x: ['2000-01-01', '2000-01-02', '2000-01-03', 1, 1.2], + y: ['2000-01', '2000-02', '2000-03', '2000-04', 1.1] + }); + checkTypes('linear', 'date'); + + supplyWithTrace({ + x: ['2000-01-01', '2000-01-02', '2000-01-03', 1, 1], + y: ['2000-01', '2000-01', '2000-01', '2000-01', 1.1] + }); + checkTypes('date', 'linear'); + }); + + it('counts ambiguous dates as both dates and numbers', function() { + supplyWithTrace({ + x: ['2000', '2000-01', '2000-02'], // 3 dates, 1 number + y: ['2000', '2001', '2000-01'] // 3 dates, 2 numbers + }); + checkTypes('date', 'linear'); + }); + }); + it('should set undefined linewidth/linecolor if linewidth, linecolor or showline is not supplied', function() { layoutIn = { xaxis: {},