/** * Returns an array of numbers, starting at start, incrementing by * step, until stop is reached. The stop value is * exclusive. If only a single argument is specified, this value is interpeted * as the stop value, with the start value as zero. If only two * arguments are specified, the step value is implied to be one. * *
The method is modeled after the built-in range method from * Python. See the Python documentation for more details. * * @see Python range * @param {number} [start] the start value. * @param {number} stop the stop value. * @param {number} [step] the step value. * @returns {number[]} an array of numbers. */ pv.range = function(start, stop, step) { if (arguments.length == 1) { stop = start; start = 0; } if (step == undefined) step = 1; if ((stop - start) / step == Infinity) throw new Error("range must be finite"); var array = [], i = 0, j; stop -= (stop - start) * 1e-10; // floating point precision! if (step < 0) { while ((j = start + step * i++) > stop) { array.push(j); } } else { while ((j = start + step * i++) < stop) { array.push(j); } } return array; }; /** * Returns a random number in the range [start, stop) that is * a multiple of step. More specifically, the returned number is of the * form start + n * step, where n is a * nonnegative integer. If step is not specified, it defaults to 1, * returning a random integer if start is also an integer. * * @param {number} [start] the start value value. * @param {number} stop the stop value. * @param {number} [step] the step value. * @returns {number} a random number between start and stop. */ pv.random = function(start, stop, step) { if (arguments.length == 1) { stop = start; start = 0; } if (step == undefined) step = 1; return step ? (Math.floor(Math.random() * (stop - start) / step) * step + start) : (Math.random() * (stop - start) + start); }; /** * Returns the sum of the specified array. If the specified array is not an * array of numbers, an optional accessor function f can be specified * to map the elements to numbers. See {@link #normalize} for an example. * Accessor functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the sum of the specified array. */ pv.sum = function(array, f) { var o = {}; return array.reduce(f ? function(p, d, i) { o.index = i; return p + f.call(o, d); } : function(p, d) { return p + d; }, 0); }; /** * Returns the maximum value of the specified array. If the specified array is * not an array of numbers, an optional accessor function f can be * specified to map the elements to numbers. See {@link #normalize} for an * example. Accessor functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the maximum value of the specified array. */ pv.max = function(array, f) { if (f == pv.index) return array.length - 1; return Math.max.apply(null, f ? pv.map(array, f) : array); }; /** * Returns the index of the maximum value of the specified array. If the * specified array is not an array of numbers, an optional accessor function * f can be specified to map the elements to numbers. See * {@link #normalize} for an example. Accessor functions can refer to * this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the index of the maximum value of the specified array. */ pv.max.index = function(array, f) { if (!array.length) return -1; if (f == pv.index) return array.length - 1; if (!f) f = pv.identity; var maxi = 0, maxx = -Infinity, o = {}; for (var i = 0; i < array.length; i++) { o.index = i; var x = f.call(o, array[i]); if (x > maxx) { maxx = x; maxi = i; } } return maxi; } /** * Returns the minimum value of the specified array of numbers. If the specified * array is not an array of numbers, an optional accessor function f * can be specified to map the elements to numbers. See {@link #normalize} for * an example. Accessor functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the minimum value of the specified array. */ pv.min = function(array, f) { if (f == pv.index) return 0; return Math.min.apply(null, f ? pv.map(array, f) : array); }; /** * Returns the index of the minimum value of the specified array. If the * specified array is not an array of numbers, an optional accessor function * f can be specified to map the elements to numbers. See * {@link #normalize} for an example. Accessor functions can refer to * this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the index of the minimum value of the specified array. */ pv.min.index = function(array, f) { if (!array.length) return -1; if (f == pv.index) return 0; if (!f) f = pv.identity; var mini = 0, minx = Infinity, o = {}; for (var i = 0; i < array.length; i++) { o.index = i; var x = f.call(o, array[i]); if (x < minx) { minx = x; mini = i; } } return mini; } /** * Returns the arithmetic mean, or average, of the specified array. If the * specified array is not an array of numbers, an optional accessor function * f can be specified to map the elements to numbers. See * {@link #normalize} for an example. Accessor functions can refer to * this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the mean of the specified array. */ pv.mean = function(array, f) { return pv.sum(array, f) / array.length; }; /** * Returns the median of the specified array. If the specified array is not an * array of numbers, an optional accessor function f can be specified * to map the elements to numbers. See {@link #normalize} for an example. * Accessor functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the median of the specified array. */ pv.median = function(array, f) { if (f == pv.index) return (array.length - 1) / 2; array = pv.map(array, f).sort(pv.naturalOrder); if (array.length % 2) return array[Math.floor(array.length / 2)]; var i = array.length / 2; return (array[i - 1] + array[i]) / 2; }; /** * Returns the unweighted variance of the specified array. If the specified * array is not an array of numbers, an optional accessor function f * can be specified to map the elements to numbers. See {@link #normalize} for * an example. Accessor functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the variance of the specified array. */ pv.variance = function(array, f) { if (array.length < 1) return NaN; if (array.length == 1) return 0; var mean = pv.mean(array, f), sum = 0, o = {}; if (!f) f = pv.identity; for (var i = 0; i < array.length; i++) { o.index = i; var d = f.call(o, array[i]) - mean; sum += d * d; } return sum; }; /** * Returns an unbiased estimation of the standard deviation of a population, * given the specified random sample. If the specified array is not an array of * numbers, an optional accessor function f can be specified to map the * elements to numbers. See {@link #normalize} for an example. Accessor * functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the standard deviation of the specified array. */ pv.deviation = function(array, f) { return Math.sqrt(pv.variance(array, f) / (array.length - 1)); }; /** * Returns the logarithm with a given base value. * * @param {number} x the number for which to compute the logarithm. * @param {number} b the base of the logarithm. * @returns {number} the logarithm value. */ pv.log = function(x, b) { return Math.log(x) / Math.log(b); }; /** * Computes a zero-symmetric logarithm. Computes the logarithm of the absolute * value of the input, and determines the sign of the output according to the * sign of the input value. * * @param {number} x the number for which to compute the logarithm. * @param {number} b the base of the logarithm. * @returns {number} the symmetric log value. */ pv.logSymmetric = function(x, b) { return (x == 0) ? 0 : ((x < 0) ? -pv.log(-x, b) : pv.log(x, b)); }; /** * Computes a zero-symmetric logarithm, with adjustment to values between zero * and the logarithm base. This adjustment introduces distortion for values less * than the base number, but enables simultaneous plotting of log-transformed * data involving both positive and negative numbers. * * @param {number} x the number for which to compute the logarithm. * @param {number} b the base of the logarithm. * @returns {number} the adjusted, symmetric log value. */ pv.logAdjusted = function(x, b) { if (!isFinite(x)) return x; var negative = x < 0; if (x < b) x += (b - x) / b; return negative ? -pv.log(x, b) : pv.log(x, b); }; /** * Rounds an input value down according to its logarithm. The method takes the * floor of the logarithm of the value and then uses the resulting value as an * exponent for the base value. * * @param {number} x the number for which to compute the logarithm floor. * @param {number} b the base of the logarithm. * @returns {number} the rounded-by-logarithm value. */ pv.logFloor = function(x, b) { return (x > 0) ? Math.pow(b, Math.floor(pv.log(x, b))) : -Math.pow(b, -Math.floor(-pv.log(-x, b))); }; /** * Rounds an input value up according to its logarithm. The method takes the * ceiling of the logarithm of the value and then uses the resulting value as an * exponent for the base value. * * @param {number} x the number for which to compute the logarithm ceiling. * @param {number} b the base of the logarithm. * @returns {number} the rounded-by-logarithm value. */ pv.logCeil = function(x, b) { return (x > 0) ? Math.pow(b, Math.ceil(pv.log(x, b))) : -Math.pow(b, -Math.ceil(-pv.log(-x, b))); }; (function() { var radians = Math.PI / 180, degrees = 180 / Math.PI; /** Returns the number of radians corresponding to the specified degrees. */ pv.radians = function(degrees) { return radians * degrees; }; /** Returns the number of degrees corresponding to the specified radians. */ pv.degrees = function(radians) { return degrees * radians; }; })();