Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4e09ad3

Browse filesBrowse files
authored
Merge pull request plotly#5240 from plotly/disable-convertnumeric-in-autotype
Implement strict autotypenumbers
2 parents 96d7511 + 6fdf559 commit 4e09ad3
Copy full SHA for 4e09ad3
Expand file treeCollapse file tree

20 files changed

+570
-41
lines changed

‎src/plots/cartesian/axes.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/axes.js
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,9 @@ var getDataConversions = axes.getDataConversions = function(gd, trace, target, t
216216
// setup the data-to-calc method.
217217
if(Array.isArray(d2cTarget)) {
218218
ax = {
219-
type: autoType(targetArray),
219+
type: autoType(targetArray, undefined, {
220+
autotypenumbers: gd._fullLayout.autotypenumbers
221+
}),
220222
_categories: []
221223
};
222224
axes.setConvert(ax);

‎src/plots/cartesian/axis_autotype.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/axis_autotype.js
+67-31Lines changed: 67 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,49 @@ var isNumeric = require('fast-isnumeric');
1414
var Lib = require('../../lib');
1515
var BADNUM = require('../../constants/numerical').BADNUM;
1616

17+
var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
18+
var isDateTime = Lib.isDateTime;
19+
var cleanNumber = Lib.cleanNumber;
20+
var round = Math.round;
21+
1722
module.exports = function autoType(array, calendar, opts) {
18-
opts = opts || {};
23+
var a = array;
24+
25+
var noMultiCategory = opts.noMultiCategory;
26+
if(isArrayOrTypedArray(a) && !a.length) return '-';
27+
if(!noMultiCategory && multiCategory(a)) return 'multicategory';
28+
if(noMultiCategory && Array.isArray(a[0])) { // no need to flat typed arrays here
29+
var b = [];
30+
for(var i = 0; i < a.length; i++) {
31+
if(isArrayOrTypedArray(a[i])) {
32+
for(var j = 0; j < a[i].length; j++) {
33+
b.push(a[i][j]);
34+
}
35+
}
36+
}
37+
a = b;
38+
}
39+
40+
if(moreDates(a, calendar)) return 'date';
1941

20-
if(!opts.noMultiCategory && multiCategory(array)) return 'multicategory';
21-
if(moreDates(array, calendar)) return 'date';
22-
if(category(array)) return 'category';
23-
if(linearOK(array)) return 'linear';
24-
else return '-';
42+
var convertNumeric = opts.autotypenumbers !== 'strict'; // compare against strict, just in case autotypenumbers was not provided in opts
43+
if(category(a, convertNumeric)) return 'category';
44+
if(linearOK(a, convertNumeric)) return 'linear';
45+
46+
return '-';
2547
};
2648

49+
function hasTypeNumber(v, convertNumeric) {
50+
return convertNumeric ? isNumeric(v) : typeof v === 'number';
51+
}
52+
2753
// is there at least one number in array? If not, we should leave
2854
// ax.type empty so it can be autoset later
29-
function linearOK(array) {
30-
if(!array) return false;
55+
function linearOK(a, convertNumeric) {
56+
var len = a.length;
3157

32-
for(var i = 0; i < array.length; i++) {
33-
if(isNumeric(array[i])) return true;
58+
for(var i = 0; i < len; i++) {
59+
if(hasTypeNumber(a[i], convertNumeric)) return true;
3460
}
3561

3662
return false;
@@ -43,51 +69,61 @@ function linearOK(array) {
4369
// numbers and a few dates
4470
// as with categories, consider DISTINCT values only.
4571
function moreDates(a, calendar) {
46-
// test at most 1000 points, evenly spaced
47-
var inc = Math.max(1, (a.length - 1) / 1000);
48-
var dcnt = 0;
49-
var ncnt = 0;
72+
var len = a.length;
73+
74+
var inc = getIncrement(len);
75+
var dats = 0;
76+
var nums = 0;
5077
var seen = {};
5178

52-
for(var i = 0; i < a.length; i += inc) {
53-
var ai = a[Math.round(i)];
79+
for(var f = 0; f < len; f += inc) {
80+
var i = round(f);
81+
var ai = a[i];
5482
var stri = String(ai);
5583
if(seen[stri]) continue;
5684
seen[stri] = 1;
5785

58-
if(Lib.isDateTime(ai, calendar)) dcnt += 1;
59-
if(isNumeric(ai)) ncnt += 1;
86+
if(isDateTime(ai, calendar)) dats++;
87+
if(isNumeric(ai)) nums++;
6088
}
6189

62-
return (dcnt > ncnt * 2);
90+
return dats > nums * 2;
91+
}
92+
93+
// return increment to test at most 1000 points, evenly spaced
94+
function getIncrement(len) {
95+
return Math.max(1, (len - 1) / 1000);
6396
}
6497

6598
// are the (x,y)-values in gd.data mostly text?
6699
// require twice as many DISTINCT categories as distinct numbers
67-
function category(a) {
68-
// test at most 1000 points
69-
var inc = Math.max(1, (a.length - 1) / 1000);
70-
var curvenums = 0;
71-
var curvecats = 0;
100+
function category(a, convertNumeric) {
101+
var len = a.length;
102+
103+
var inc = getIncrement(len);
104+
var nums = 0;
105+
var cats = 0;
72106
var seen = {};
73107

74-
for(var i = 0; i < a.length; i += inc) {
75-
var ai = a[Math.round(i)];
108+
for(var f = 0; f < len; f += inc) {
109+
var i = round(f);
110+
var ai = a[i];
76111
var stri = String(ai);
77112
if(seen[stri]) continue;
78113
seen[stri] = 1;
79114

80-
if(typeof ai === 'boolean') curvecats++;
81-
else if(Lib.cleanNumber(ai) !== BADNUM) curvenums++;
82-
else if(typeof ai === 'string') curvecats++;
115+
var t = typeof ai;
116+
if(t === 'boolean') cats++;
117+
else if(convertNumeric ? cleanNumber(ai) !== BADNUM : t === 'number') nums++;
118+
else if(t === 'string') cats++;
83119
}
84120

85-
return curvecats > curvenums * 2;
121+
return cats > nums * 2;
86122
}
87123

88124
// very-loose requirements for multicategory,
89125
// trace modules that should never auto-type to multicategory
90126
// should be declared with 'noMultiCategory'
91127
function multiCategory(a) {
92-
return Lib.isArrayOrTypedArray(a[0]) && Lib.isArrayOrTypedArray(a[1]);
128+
return isArrayOrTypedArray(a[0]) && isArrayOrTypedArray(a[1]);
93129
}

‎src/plots/cartesian/layout_attributes.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/layout_attributes.js
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,19 @@ module.exports = {
102102
'the axis in question.'
103103
].join(' ')
104104
},
105+
autotypenumbers: {
106+
valType: 'enumerated',
107+
values: ['convert types', 'strict'],
108+
dflt: 'convert types',
109+
role: 'info',
110+
editType: 'calc',
111+
description: [
112+
'Using *strict* a numeric string in trace data is not converted to a number.',
113+
'Using *convert types* a numeric string in trace data may be',
114+
'treated as a number during automatic axis `type` detection.',
115+
'Defaults to layout.autotypenumbers.'
116+
].join(' ')
117+
},
105118
autorange: {
106119
valType: 'enumerated',
107120
values: [true, false, 'reversed'],

‎src/plots/cartesian/layout_defaults.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/layout_defaults.js
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ function appendList(cont, k, item) {
3838
}
3939

4040
module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
41+
var autotypenumbersDflt = layoutOut.autotypenumbers;
42+
4143
var ax2traces = {};
4244
var xaMayHide = {};
4345
var yaMayHide = {};
@@ -246,6 +248,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
246248
automargin: true,
247249
visibleDflt: visibleDflt,
248250
reverseDflt: reverseDflt,
251+
autotypenumbersDflt: autotypenumbersDflt,
249252
splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
250253
};
251254

@@ -310,6 +313,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
310313
automargin: true,
311314
visibleDflt: false,
312315
reverseDflt: false,
316+
autotypenumbersDflt: autotypenumbersDflt,
313317
splomStash: ((layoutOut._splomAxes || {})[axLetter] || {})[axId]
314318
};
315319

‎src/plots/cartesian/type_defaults.js

Copy file name to clipboardExpand all lines: src/plots/cartesian/type_defaults.js
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ var autoType = require('./axis_autotype');
1616
* name: axis object name (ie 'xaxis') if one should be stored
1717
*/
1818
module.exports = function handleTypeDefaults(containerIn, containerOut, coerce, options) {
19+
coerce('autotypenumbers', options.autotypenumbersDflt);
1920
var axType = coerce('type', (options.splomStash || {}).type);
2021

2122
if(axType === '-') {
@@ -68,6 +69,8 @@ function setAutoType(ax, data) {
6869
opts.noMultiCategory = true;
6970
}
7071

72+
opts.autotypenumbers = ax.autotypenumbers;
73+
7174
// check all boxes on this x axis to see
7275
// if they're dates, numbers, or categories
7376
if(isBoxWithoutPositionCoords(d0, axLetter)) {

‎src/plots/gl3d/layout/axis_attributes.js

Copy file name to clipboardExpand all lines: src/plots/gl3d/layout/axis_attributes.js
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ module.exports = overrideAll({
7878
type: extendFlat({}, axesAttrs.type, {
7979
values: ['-', 'linear', 'log', 'date', 'category']
8080
}),
81+
autotypenumbers: axesAttrs.autotypenumbers,
8182
autorange: axesAttrs.autorange,
8283
rangemode: axesAttrs.rangemode,
8384
range: extendFlat({}, axesAttrs.range, {

‎src/plots/gl3d/layout/defaults.js

Copy file name to clipboardExpand all lines: src/plots/gl3d/layout/defaults.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
4040
font: layoutOut.font,
4141
fullData: fullData,
4242
getDfltFromLayout: getDfltFromLayout,
43+
autotypenumbersDflt: layoutOut.autotypenumbers,
4344
paper_bgcolor: layoutOut.paper_bgcolor,
4445
calendar: layoutOut.calendar
4546
});
@@ -109,6 +110,7 @@ function handleGl3dDefaults(sceneLayoutIn, sceneLayoutOut, coerce, opts) {
109110
data: fullGl3dData,
110111
bgColor: bgColorCombined,
111112
calendar: opts.calendar,
113+
autotypenumbersDflt: opts.autotypenumbersDflt,
112114
fullLayout: opts.fullLayout
113115
});
114116

‎src/plots/layout_attributes.js

Copy file name to clipboardExpand all lines: src/plots/layout_attributes.js
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,19 @@ module.exports = {
292292
'Sets the background color of the plotting area in-between x and y axes.'
293293
].join(' ')
294294
},
295+
autotypenumbers: {
296+
valType: 'enumerated',
297+
values: ['convert types', 'strict'],
298+
dflt: 'convert types',
299+
role: 'info',
300+
editType: 'calc',
301+
description: [
302+
'Using *strict* a numeric string in trace data is not converted to a number.',
303+
'Using *convert types* a numeric string in trace data may be',
304+
'treated as a number during automatic axis `type` detection.',
305+
'This is the default value; however it could be overridden for individual axes.'
306+
].join(' ')
307+
},
295308
separators: {
296309
valType: 'string',
297310
role: 'style',

‎src/plots/plots.js

Copy file name to clipboardExpand all lines: src/plots/plots.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,6 +1476,8 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) {
14761476
layoutOut._dataTemplate = template.data;
14771477
}
14781478

1479+
coerce('autotypenumbers');
1480+
14791481
var globalFont = Lib.coerceFont(coerce, 'font');
14801482

14811483
coerce('title.text', layoutOut._dfltTitle.plot);

‎src/plots/polar/layout_attributes.js

Copy file name to clipboardExpand all lines: src/plots/polar/layout_attributes.js
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ var radialAxisAttrs = {
6161
type: extendFlat({}, axesAttrs.type, {
6262
values: ['-', 'linear', 'log', 'date', 'category']
6363
}),
64+
autotypenumbers: axesAttrs.autotypenumbers,
6465

6566
autorange: extendFlat({}, axesAttrs.autorange, {editType: 'plot'}),
6667
rangemode: {
@@ -179,6 +180,7 @@ var angularAxisAttrs = {
179180
'If *category, use `period` to set the number of integer coordinates around polar axis.'
180181
].join(' ')
181182
},
183+
autotypenumbers: axesAttrs.autotypenumbers,
182184

183185
categoryorder: axesAttrs.categoryorder,
184186
categoryarray: axesAttrs.categoryarray,

‎src/plots/polar/layout_defaults.js

Copy file name to clipboardExpand all lines: src/plots/polar/layout_defaults.js
+8-3Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function handleDefaults(contIn, contOut, coerce, opts) {
5757
axOut._traceIndices = subplotData.map(function(t) { return t._expandedIndex; });
5858

5959
var dataAttr = constants.axisName2dataArray[axName];
60-
var axType = handleAxisTypeDefaults(axIn, axOut, coerceAxis, subplotData, dataAttr);
60+
var axType = handleAxisTypeDefaults(axIn, axOut, coerceAxis, subplotData, dataAttr, opts);
6161

6262
handleCategoryOrderDefaults(axIn, axOut, coerceAxis, {
6363
axData: subplotData,
@@ -187,7 +187,8 @@ function handleDefaults(contIn, contOut, coerce, opts) {
187187
}
188188
}
189189

190-
function handleAxisTypeDefaults(axIn, axOut, coerce, subplotData, dataAttr) {
190+
function handleAxisTypeDefaults(axIn, axOut, coerce, subplotData, dataAttr, options) {
191+
var autotypenumbers = coerce('autotypenumbers', options.autotypenumbersDflt);
191192
var axType = coerce('type');
192193

193194
if(axType === '-') {
@@ -201,7 +202,10 @@ function handleAxisTypeDefaults(axIn, axOut, coerce, subplotData, dataAttr) {
201202
}
202203

203204
if(trace && trace[dataAttr]) {
204-
axOut.type = autoType(trace[dataAttr], 'gregorian');
205+
axOut.type = autoType(trace[dataAttr], 'gregorian', {
206+
noMultiCategory: true,
207+
autotypenumbers: autotypenumbers
208+
});
205209
}
206210

207211
if(axOut.type === '-') {
@@ -224,6 +228,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) {
224228
attributes: layoutAttributes,
225229
handleDefaults: handleDefaults,
226230
font: layoutOut.font,
231+
autotypenumbersDflt: layoutOut.autotypenumbers,
227232
paper_bgcolor: layoutOut.paper_bgcolor,
228233
fullData: fullData,
229234
layoutOut: layoutOut

‎src/traces/box/defaults.js

Copy file name to clipboardExpand all lines: src/traces/box/defaults.js
+7-2Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ function handleSampleDefaults(traceIn, traceOut, coerce, layout) {
109109
var yLen = yDims && Lib.minRowLength(y);
110110
var xLen = xDims && Lib.minRowLength(x);
111111

112+
var calendar = layout.calendar;
113+
var opts = {
114+
autotypenumbers: layout.autotypenumbers
115+
};
116+
112117
var defaultOrientation, len;
113118
if(traceOut._hasPreCompStats) {
114119
switch(String(xDims) + String(yDims)) {
@@ -160,7 +165,7 @@ function handleSampleDefaults(traceIn, traceOut, coerce, layout) {
160165
var hasCategories = false;
161166
var i;
162167
for(i = 0; i < x.length; i++) {
163-
if(autoType(x[i]) === 'category') {
168+
if(autoType(x[i], calendar, opts) === 'category') {
164169
hasCategories = true;
165170
break;
166171
}
@@ -171,7 +176,7 @@ function handleSampleDefaults(traceIn, traceOut, coerce, layout) {
171176
len = Math.min(sLen, xLen, y.length);
172177
} else {
173178
for(i = 0; i < y.length; i++) {
174-
if(autoType(y[i]) === 'category') {
179+
if(autoType(y[i], calendar, opts) === 'category') {
175180
hasCategories = true;
176181
break;
177182
}

‎src/traces/carpet/ab_defaults.js

Copy file name to clipboardExpand all lines: src/traces/carpet/ab_defaults.js
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ function mimickAxisDefaults(traceIn, traceOut, fullLayout, dfltColor) {
4747
calendar: traceOut.calendar,
4848
dfltColor: dfltColor,
4949
bgColor: fullLayout.paper_bgcolor,
50+
autotypenumbersDflt: fullLayout.autotypenumbers,
5051
fullLayout: fullLayout
5152
};
5253

‎src/traces/carpet/axis_attributes.js

Copy file name to clipboardExpand all lines: src/traces/carpet/axis_attributes.js
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ module.exports = {
8888
'the axis in question.'
8989
].join(' ')
9090
},
91+
autotypenumbers: axesAttrs.autotypenumbers,
9192
autorange: {
9293
valType: 'enumerated',
9394
values: [true, false, 'reversed'],

‎src/traces/carpet/axis_defaults.js

Copy file name to clipboardExpand all lines: src/traces/carpet/axis_defaults.js
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, options)
5151
}
5252

5353
// now figure out type and do some more initialization
54+
coerce('autotypenumbers', options.autotypenumbersDflt);
5455
var axType = coerce('type');
5556
if(axType === '-') {
5657
if(options.data) setAutoType(containerOut, options.data);
@@ -219,5 +220,7 @@ function setAutoType(ax, data) {
219220
var calAttr = axLetter + 'calendar';
220221
var calendar = ax[calAttr];
221222

222-
ax.type = autoType(data, calendar);
223+
ax.type = autoType(data, calendar, {
224+
autotypenumbers: ax.autotypenumbers
225+
});
223226
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.