Skip to content

Commit 5b71b47

Browse files
committedOct 20, 2016
doing cheaper, probabilistic checks outside the scattergl conversion loop
1 parent 3c79068 commit 5b71b47

File tree

3 files changed

+103
-65
lines changed

3 files changed

+103
-65
lines changed
 

Diff for: ‎src/plots/cartesian/axis_autotype.js

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
10+
'use strict';
11+
12+
var isNumeric = require('fast-isnumeric');
13+
14+
var Lib = require('../../lib');
15+
var cleanDatum = require('./clean_datum');
16+
17+
module.exports = function autoType(array) {
18+
if(moreDates(array)) return 'date';
19+
if(category(array)) return 'category';
20+
if(linearOK(array)) return 'linear';
21+
else return '-';
22+
};
23+
24+
// is there at least one number in array? If not, we should leave
25+
// ax.type empty so it can be autoset later
26+
function linearOK(array) {
27+
if(!array) return false;
28+
29+
for(var i = 0; i < array.length; i++) {
30+
if(isNumeric(array[i])) return true;
31+
}
32+
33+
return false;
34+
}
35+
36+
// does the array a have mostly dates rather than numbers?
37+
// note: some values can be neither (such as blanks, text)
38+
// 2- or 4-digit integers can be both, so require twice as many
39+
// dates as non-dates, to exclude cases with mostly 2 & 4 digit
40+
// numbers and a few dates
41+
function moreDates(a) {
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;
47+
48+
for(var i = 0; i < a.length; i += inc) {
49+
ai = a[Math.round(i)];
50+
if(Lib.isDateTime(ai)) dcnt += 1;
51+
if(isNumeric(ai)) ncnt += 1;
52+
}
53+
54+
return (dcnt > ncnt * 2);
55+
}
56+
57+
// are the (x,y)-values in td.data mostly text?
58+
// require twice as many categories as numbers
59+
function category(a) {
60+
// test at most 1000 points
61+
var inc = Math.max(1, (a.length - 1) / 1000),
62+
curvenums = 0,
63+
curvecats = 0,
64+
ai;
65+
66+
for(var i = 0; i < a.length; i += inc) {
67+
ai = cleanDatum(a[Math.round(i)]);
68+
if(isNumeric(ai)) curvenums++;
69+
else if(typeof ai === 'string' && ai !== '' && ai !== 'None') curvecats++;
70+
}
71+
72+
return curvecats > curvenums * 2;
73+
}

Diff for: ‎src/plots/cartesian/axis_defaults.js

+1-60
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@ var handleTickLabelDefaults = require('./tick_label_defaults');
2323
var handleCategoryOrderDefaults = require('./category_order_defaults');
2424
var setConvert = require('./set_convert');
2525
var orderedCategories = require('./ordered_categories');
26-
var cleanDatum = require('./clean_datum');
2726
var axisIds = require('./axis_ids');
28-
27+
var autoType = require('./axis_autotype');
2928

3029
/**
3130
* options: object containing:
@@ -207,13 +206,6 @@ function isBoxWithoutPositionCoords(trace, axLetter) {
207206
);
208207
}
209208

210-
function autoType(array) {
211-
if(moreDates(array)) return 'date';
212-
if(category(array)) return 'category';
213-
if(linearOK(array)) return 'linear';
214-
else return '-';
215-
}
216-
217209
function getFirstNonEmptyTrace(data, id, axLetter) {
218210
for(var i = 0; i < data.length; i++) {
219211
var trace = data[i];
@@ -228,54 +220,3 @@ function getFirstNonEmptyTrace(data, id, axLetter) {
228220
}
229221
}
230222
}
231-
232-
// is there at least one number in array? If not, we should leave
233-
// ax.type empty so it can be autoset later
234-
function linearOK(array) {
235-
if(!array) return false;
236-
237-
for(var i = 0; i < array.length; i++) {
238-
if(isNumeric(array[i])) return true;
239-
}
240-
241-
return false;
242-
}
243-
244-
// does the array a have mostly dates rather than numbers?
245-
// note: some values can be neither (such as blanks, text)
246-
// 2- or 4-digit integers can be both, so require twice as many
247-
// dates as non-dates, to exclude cases with mostly 2 & 4 digit
248-
// numbers and a few dates
249-
function moreDates(a) {
250-
var dcnt = 0,
251-
ncnt = 0,
252-
// test at most 1000 points, evenly spaced
253-
inc = Math.max(1, (a.length - 1) / 1000),
254-
ai;
255-
256-
for(var i = 0; i < a.length; i += inc) {
257-
ai = a[Math.round(i)];
258-
if(Lib.isDateTime(ai)) dcnt += 1;
259-
if(isNumeric(ai)) ncnt += 1;
260-
}
261-
262-
return (dcnt > ncnt * 2);
263-
}
264-
265-
// are the (x,y)-values in td.data mostly text?
266-
// require twice as many categories as numbers
267-
function category(a) {
268-
// test at most 1000 points
269-
var inc = Math.max(1, (a.length - 1) / 1000),
270-
curvenums = 0,
271-
curvecats = 0,
272-
ai;
273-
274-
for(var i = 0; i < a.length; i += inc) {
275-
ai = cleanDatum(a[Math.round(i)]);
276-
if(isNumeric(ai)) curvenums++;
277-
else if(typeof ai === 'string' && ai !== '' && ai !== 'None') curvecats++;
278-
}
279-
280-
return curvecats > curvenums * 2;
281-
}

Diff for: ‎src/traces/scattergl/convert.js

+29-5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var isNumeric = require('fast-isnumeric');
1717

1818
var Lib = require('../../lib');
1919
var Axes = require('../../plots/cartesian/axes');
20+
var autoType = require('../../plots/cartesian/axis_autotype');
2021
var ErrorBars = require('../../components/errorbars');
2122
var str2RGBArray = require('../../lib/str2rgbarray');
2223
var truncate = require('../../lib/float32_truncate');
@@ -261,6 +262,29 @@ proto.update = function(options) {
261262
this.color = getTraceColor(options, {});
262263
};
263264

265+
// We'd ideally know that all values are of fast types; sampling gives no certainty but faster
266+
// (for the future, typed arrays can guarantee it, and Date values can be done with
267+
// representing the epoch milliseconds in a typed array;
268+
// also, perhaps the Python / R interfaces take care of String->Date conversions
269+
// such that there's no need to check for string dates in plotly.js)
270+
// Patterned from axis_defaults.js:moreDates
271+
// Code DRYing is not done to preserve the most direct compilation possible for speed;
272+
// also, there are quite a few differences
273+
function allFastTypesLikely(a) {
274+
var len = a.length,
275+
inc = Math.max(0, (len - 1) / Math.min(Math.max(len, 1), 1000)),
276+
ai;
277+
278+
for(var i = 0; i < len; i += inc) {
279+
ai = a[Math.floor(i)];
280+
if(!isNumeric(ai) && !(ai instanceof Date)) {
281+
return false;
282+
}
283+
}
284+
285+
return true;
286+
}
287+
264288
proto.updateFast = function(options) {
265289
var x = this.xData = this.pickXData = options.x;
266290
var y = this.yData = this.pickYData = options.y;
@@ -272,18 +296,18 @@ proto.updateFast = function(options) {
272296
pId = 0,
273297
ptr = 0;
274298

275-
var xx, yy, fastType;
299+
var xx, yy;
300+
301+
var fastType = allFastTypesLikely(x);
302+
var isDateTime = !fastType && autoType(x) === 'date';
276303

277304
// TODO add 'very fast' mode that bypasses this loop
278305
// TODO bypass this on modebar +/- zoom
279306
for(var i = 0; i < len; ++i) {
280307
xx = x[i];
281308
yy = y[i];
282309

283-
// check for isNaN is faster but doesn't skip over nulls
284-
fastType = isNumeric(xx) || xx instanceof Date;
285-
286-
if(isNumeric(yy) && (fastType || Lib.isDateTime(xx))) {
310+
if(isNumeric(yy) && (fastType || isDateTime)) {
287311

288312
if(!fastType) {
289313
xx = Lib.dateTime2ms(xx);

0 commit comments

Comments
 (0)
Please sign in to comment.