Skip to content

Commit 48209a0

Browse files
authored
Merge pull request #3070 from plotly/bool-cats
Axis autotype update
2 parents b02221f + 8d446be commit 48209a0

File tree

2 files changed

+98
-14
lines changed

2 files changed

+98
-14
lines changed

src/plots/cartesian/axis_autotype.js

+24-14
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,20 @@ function linearOK(array) {
3838
// 2- or 4-digit integers can be both, so require twice as many
3939
// dates as non-dates, to exclude cases with mostly 2 & 4 digit
4040
// numbers and a few dates
41+
// as with categories, consider DISTINCT values only.
4142
function moreDates(a, calendar) {
42-
var dcnt = 0,
43-
ncnt = 0,
44-
// test at most 1000 points, evenly spaced
45-
inc = Math.max(1, (a.length - 1) / 1000),
46-
ai;
43+
// test at most 1000 points, evenly spaced
44+
var inc = Math.max(1, (a.length - 1) / 1000);
45+
var dcnt = 0;
46+
var ncnt = 0;
47+
var seen = {};
4748

4849
for(var i = 0; i < a.length; i += inc) {
49-
ai = a[Math.round(i)];
50+
var ai = a[Math.round(i)];
51+
var stri = String(ai);
52+
if(seen[stri]) continue;
53+
seen[stri] = 1;
54+
5055
if(Lib.isDateTime(ai, calendar)) dcnt += 1;
5156
if(isNumeric(ai)) ncnt += 1;
5257
}
@@ -55,18 +60,23 @@ function moreDates(a, calendar) {
5560
}
5661

5762
// are the (x,y)-values in gd.data mostly text?
58-
// require twice as many categories as numbers
63+
// require twice as many DISTINCT categories as distinct numbers
5964
function category(a) {
6065
// test at most 1000 points
61-
var inc = Math.max(1, (a.length - 1) / 1000),
62-
curvenums = 0,
63-
curvecats = 0,
64-
ai;
66+
var inc = Math.max(1, (a.length - 1) / 1000);
67+
var curvenums = 0;
68+
var curvecats = 0;
69+
var seen = {};
6570

6671
for(var i = 0; i < a.length; i += inc) {
67-
ai = a[Math.round(i)];
68-
if(Lib.cleanNumber(ai) !== BADNUM) curvenums++;
69-
else if(typeof ai === 'string' && ai !== '' && ai !== 'None') curvecats++;
72+
var ai = a[Math.round(i)];
73+
var stri = String(ai);
74+
if(seen[stri]) continue;
75+
seen[stri] = 1;
76+
77+
if(typeof ai === 'boolean') curvecats++;
78+
else if(Lib.cleanNumber(ai) !== BADNUM) curvenums++;
79+
else if(typeof ai === 'string') curvecats++;
7080
}
7181

7282
return curvecats > curvenums * 2;

test/jasmine/tests/axes_test.js

+74
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,80 @@ describe('Test axes', function() {
189189
fullData = [];
190190
});
191191

192+
describe('autotype', function() {
193+
function supplyWithTrace(trace) {
194+
var fullTrace = Lib.extendDeep(
195+
{type: 'scatter', xaxis: 'x', yaxis: 'y'},
196+
trace
197+
);
198+
layoutIn = {xaxis: {}, yaxis: {}};
199+
supplyLayoutDefaults(layoutIn, layoutOut, [fullTrace]);
200+
}
201+
202+
function checkTypes(xType, yType, msg) {
203+
expect(layoutOut.xaxis.type).toBe(xType, msg);
204+
expect(layoutOut.yaxis.type).toBe(yType, msg);
205+
}
206+
207+
it('treats booleans as categories', function() {
208+
supplyWithTrace({x: [0, 1, 2], y: [true, false, true]});
209+
checkTypes('linear', 'category');
210+
});
211+
212+
it('sees a single "None" or "" as a category', function() {
213+
supplyWithTrace({x: ['None'], y: ['']});
214+
checkTypes('category', 'category');
215+
});
216+
217+
it('lets a single number beat up to two distinct categories', function() {
218+
supplyWithTrace({
219+
x: ['2.1', 'N/A', '', 'N/A', '', 'N/A', 'N/A', '', '', ''],
220+
y: [0, 'None', true, 'None', 'None', 'None', 'None', 'None']
221+
});
222+
checkTypes('linear', 'linear');
223+
});
224+
225+
it('turns back to category with >2 per distinct number', function() {
226+
supplyWithTrace({
227+
x: [4, 4, 4, 4, 4, 4, 4, 4, 'Yes', 'No', 'Maybe'],
228+
y: [1, 2, 1, 2, 1, 2, true, false, '', 'None', 'nan']
229+
});
230+
checkTypes('category', 'category');
231+
});
232+
233+
it('works with world calendars', function() {
234+
// these are only valid dates in chinese
235+
var intercalary = ['1995-08i-01', '1995-08i-29', '1984-10i-15'];
236+
supplyWithTrace({
237+
x: intercalary, xcalendar: 'chinese',
238+
y: intercalary, ycalendar: 'gregorian'
239+
});
240+
checkTypes('date', 'category');
241+
});
242+
243+
it('requires >twice as many distinct dates as numbers', function() {
244+
supplyWithTrace({
245+
x: ['2000-01-01', '2000-01-02', '2000-01-03', 1, 1.2],
246+
y: ['2000-01', '2000-02', '2000-03', '2000-04', 1.1]
247+
});
248+
checkTypes('linear', 'date');
249+
250+
supplyWithTrace({
251+
x: ['2000-01-01', '2000-01-02', '2000-01-03', 1, 1],
252+
y: ['2000-01', '2000-01', '2000-01', '2000-01', 1.1]
253+
});
254+
checkTypes('date', 'linear');
255+
});
256+
257+
it('counts ambiguous dates as both dates and numbers', function() {
258+
supplyWithTrace({
259+
x: ['2000', '2000-01', '2000-02'], // 3 dates, 1 number
260+
y: ['2000', '2001', '2000-01'] // 3 dates, 2 numbers
261+
});
262+
checkTypes('date', 'linear');
263+
});
264+
});
265+
192266
it('should set undefined linewidth/linecolor if linewidth, linecolor or showline is not supplied', function() {
193267
layoutIn = {
194268
xaxis: {},

0 commit comments

Comments
 (0)