plotly.js
Version:
The open source javascript graphing library that powers plotly
130 lines (103 loc) • 3.65 kB
JavaScript
/**
* Copyright 2012-2020, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
var isNumeric = require('fast-isnumeric');
var Lib = require('../../lib');
var BADNUM = require('../../constants/numerical').BADNUM;
var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
var isDateTime = Lib.isDateTime;
var cleanNumber = Lib.cleanNumber;
var round = Math.round;
module.exports = function autoType(array, calendar, opts) {
var a = array;
var noMultiCategory = opts.noMultiCategory;
if(isArrayOrTypedArray(a) && !a.length) return '-';
if(!noMultiCategory && multiCategory(a)) return 'multicategory';
if(noMultiCategory && Array.isArray(a[0])) { // no need to flat typed arrays here
var b = [];
for(var i = 0; i < a.length; i++) {
if(isArrayOrTypedArray(a[i])) {
for(var j = 0; j < a[i].length; j++) {
b.push(a[i][j]);
}
}
}
a = b;
}
if(moreDates(a, calendar)) return 'date';
var convertNumeric = opts.autotypenumbers !== 'strict'; // compare against strict, just in case autotypenumbers was not provided in opts
if(category(a, convertNumeric)) return 'category';
if(linearOK(a, convertNumeric)) return 'linear';
return '-';
};
function hasTypeNumber(v, convertNumeric) {
return convertNumeric ? isNumeric(v) : typeof v === 'number';
}
// is there at least one number in array? If not, we should leave
// ax.type empty so it can be autoset later
function linearOK(a, convertNumeric) {
var len = a.length;
for(var i = 0; i < len; i++) {
if(hasTypeNumber(a[i], convertNumeric)) return true;
}
return false;
}
// does the array a have mostly dates rather than numbers?
// note: some values can be neither (such as blanks, text)
// 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 len = a.length;
var inc = getIncrement(len);
var dats = 0;
var nums = 0;
var seen = {};
for(var f = 0; f < len; f += inc) {
var i = round(f);
var ai = a[i];
var stri = String(ai);
if(seen[stri]) continue;
seen[stri] = 1;
if(isDateTime(ai, calendar)) dats++;
if(isNumeric(ai)) nums++;
}
return dats > nums * 2;
}
// return increment to test at most 1000 points, evenly spaced
function getIncrement(len) {
return Math.max(1, (len - 1) / 1000);
}
// are the (x,y)-values in gd.data mostly text?
// require twice as many DISTINCT categories as distinct numbers
function category(a, convertNumeric) {
var len = a.length;
var inc = getIncrement(len);
var nums = 0;
var cats = 0;
var seen = {};
for(var f = 0; f < len; f += inc) {
var i = round(f);
var ai = a[i];
var stri = String(ai);
if(seen[stri]) continue;
seen[stri] = 1;
var t = typeof ai;
if(t === 'boolean') cats++;
else if(convertNumeric ? cleanNumber(ai) !== BADNUM : t === 'number') nums++;
else if(t === 'string') cats++;
}
return cats > nums * 2;
}
// very-loose requirements for multicategory,
// trace modules that should never auto-type to multicategory
// should be declared with 'noMultiCategory'
function multiCategory(a) {
return isArrayOrTypedArray(a[0]) && isArrayOrTypedArray(a[1]);
}