1 /** 2 * Returns an array of numbers, starting at <tt>start</tt>, incrementing by 3 * <tt>step</tt>, until <tt>stop</tt> is reached. The stop value is 4 * exclusive. If only a single argument is specified, this value is interpeted 5 * as the <i>stop</i> value, with the <i>start</i> value as zero. If only two 6 * arguments are specified, the step value is implied to be one. 7 * 8 * <p>The method is modeled after the built-in <tt>range</tt> method from 9 * Python. See the Python documentation for more details. 10 * 11 * @see <a href="http://docs.python.org/library/functions.html#range">Python range</a> 12 * @param {number} [start] the start value. 13 * @param {number} stop the stop value. 14 * @param {number} [step] the step value. 15 * @returns {number[]} an array of numbers. 16 */ 17 pv.range = function(start, stop, step) { 18 if (arguments.length == 1) { 19 stop = start; 20 start = 0; 21 } 22 if (step == undefined) step = 1; 23 if ((stop - start) / step == Infinity) throw new Error("range must be finite"); 24 var array = [], i = 0, j; 25 if (step < 0) { 26 while ((j = start + step * i++) > stop) { 27 array.push(j); 28 } 29 } else { 30 while ((j = start + step * i++) < stop) { 31 array.push(j); 32 } 33 } 34 return array; 35 }; 36 37 /** 38 * Returns a random number in the range [<tt>start</tt>, <tt>stop</tt>) that is 39 * a multiple of <tt>step</tt>. More specifically, the returned number is of the 40 * form <tt>start</tt> + <i>n</i> * <tt>step</tt>, where <i>n</i> is a 41 * nonnegative integer. If <tt>step</tt> is not specified, it defaults to 1, 42 * returning a random integer if <tt>start</tt> is also an integer. 43 * 44 * @param {number} [start] the start value value. 45 * @param {number} stop the stop value. 46 * @param {number} [step] the step value. 47 * @returns {number} a random number between <i>start</i> and <i>stop</i>. 48 */ 49 pv.random = function(start, stop, step) { 50 if (arguments.length == 1) { 51 stop = start; 52 start = 0; 53 } 54 if (step == undefined) step = 1; 55 return step 56 ? (Math.floor(Math.random() * (stop - start) / step) * step + start) 57 : (Math.random() * (stop - start) + start); 58 }; 59 60 /** 61 * Returns the sum of the specified array. If the specified array is not an 62 * array of numbers, an optional accessor function <tt>f</tt> can be specified 63 * to map the elements to numbers. See {@link #normalize} for an example. 64 * Accessor functions can refer to <tt>this.index</tt>. 65 * 66 * @param {array} array an array of objects, or numbers. 67 * @param {function} [f] an optional accessor function. 68 * @returns {number} the sum of the specified array. 69 */ 70 pv.sum = function(array, f) { 71 var o = {}; 72 return array.reduce(f 73 ? function(p, d, i) { o.index = i; return p + f.call(o, d); } 74 : function(p, d) { return p + d; }, 0); 75 }; 76 77 /** 78 * Returns the maximum value of the specified array. If the specified array is 79 * not an array of numbers, an optional accessor function <tt>f</tt> can be 80 * specified to map the elements to numbers. See {@link #normalize} for an 81 * example. Accessor functions can refer to <tt>this.index</tt>. 82 * 83 * @param {array} array an array of objects, or numbers. 84 * @param {function} [f] an optional accessor function. 85 * @returns {number} the maximum value of the specified array. 86 */ 87 pv.max = function(array, f) { 88 if (f == pv.index) return array.length - 1; 89 return Math.max.apply(null, f ? pv.map(array, f) : array); 90 }; 91 92 /** 93 * Returns the index of the maximum value of the specified array. If the 94 * specified array is not an array of numbers, an optional accessor function 95 * <tt>f</tt> can be specified to map the elements to numbers. See 96 * {@link #normalize} for an example. Accessor functions can refer to 97 * <tt>this.index</tt>. 98 * 99 * @param {array} array an array of objects, or numbers. 100 * @param {function} [f] an optional accessor function. 101 * @returns {number} the index of the maximum value of the specified array. 102 */ 103 pv.max.index = function(array, f) { 104 if (!array.length) return -1; 105 if (f == pv.index) return array.length - 1; 106 if (!f) f = pv.identity; 107 var maxi = 0, maxx = -Infinity, o = {}; 108 for (var i = 0; i < array.length; i++) { 109 o.index = i; 110 var x = f.call(o, array[i]); 111 if (x > maxx) { 112 maxx = x; 113 maxi = i; 114 } 115 } 116 return maxi; 117 } 118 119 /** 120 * Returns the minimum value of the specified array of numbers. If the specified 121 * array is not an array of numbers, an optional accessor function <tt>f</tt> 122 * can be specified to map the elements to numbers. See {@link #normalize} for 123 * an example. Accessor functions can refer to <tt>this.index</tt>. 124 * 125 * @param {array} array an array of objects, or numbers. 126 * @param {function} [f] an optional accessor function. 127 * @returns {number} the minimum value of the specified array. 128 */ 129 pv.min = function(array, f) { 130 if (f == pv.index) return 0; 131 return Math.min.apply(null, f ? pv.map(array, f) : array); 132 }; 133 134 /** 135 * Returns the index of the minimum value of the specified array. If the 136 * specified array is not an array of numbers, an optional accessor function 137 * <tt>f</tt> can be specified to map the elements to numbers. See 138 * {@link #normalize} for an example. Accessor functions can refer to 139 * <tt>this.index</tt>. 140 * 141 * @param {array} array an array of objects, or numbers. 142 * @param {function} [f] an optional accessor function. 143 * @returns {number} the index of the minimum value of the specified array. 144 */ 145 pv.min.index = function(array, f) { 146 if (!array.length) return -1; 147 if (f == pv.index) return 0; 148 if (!f) f = pv.identity; 149 var mini = 0, minx = Infinity, o = {}; 150 for (var i = 0; i < array.length; i++) { 151 o.index = i; 152 var x = f.call(o, array[i]); 153 if (x < minx) { 154 minx = x; 155 mini = i; 156 } 157 } 158 return mini; 159 } 160 161 /** 162 * Returns the arithmetic mean, or average, of the specified array. If the 163 * specified array is not an array of numbers, an optional accessor function 164 * <tt>f</tt> can be specified to map the elements to numbers. See 165 * {@link #normalize} for an example. Accessor functions can refer to 166 * <tt>this.index</tt>. 167 * 168 * @param {array} array an array of objects, or numbers. 169 * @param {function} [f] an optional accessor function. 170 * @returns {number} the mean of the specified array. 171 */ 172 pv.mean = function(array, f) { 173 return pv.sum(array, f) / array.length; 174 }; 175 176 /** 177 * Returns the median of the specified array. If the specified array is not an 178 * array of numbers, an optional accessor function <tt>f</tt> can be specified 179 * to map the elements to numbers. See {@link #normalize} for an example. 180 * Accessor functions can refer to <tt>this.index</tt>. 181 * 182 * @param {array} array an array of objects, or numbers. 183 * @param {function} [f] an optional accessor function. 184 * @returns {number} the median of the specified array. 185 */ 186 pv.median = function(array, f) { 187 if (f == pv.index) return (array.length - 1) / 2; 188 array = pv.map(array, f).sort(pv.naturalOrder); 189 if (array.length % 2) return array[Math.floor(array.length / 2)]; 190 var i = array.length / 2; 191 return (array[i - 1] + array[i]) / 2; 192 }; 193 194 /** 195 * Returns the unweighted variance of the specified array. If the specified 196 * array is not an array of numbers, an optional accessor function <tt>f</tt> 197 * can be specified to map the elements to numbers. See {@link #normalize} for 198 * an example. Accessor functions can refer to <tt>this.index</tt>. 199 * 200 * @param {array} array an array of objects, or numbers. 201 * @param {function} [f] an optional accessor function. 202 * @returns {number} the variance of the specified array. 203 */ 204 pv.variance = function(array, f) { 205 if (array.length < 1) return NaN; 206 if (array.length == 1) return 0; 207 var mean = pv.mean(array, f), sum = 0, o = {}; 208 if (!f) f = pv.identity; 209 for (var i = 0; i < array.length; i++) { 210 o.index = i; 211 var d = f.call(o, array[i]) - mean; 212 sum += d * d; 213 } 214 return sum; 215 }; 216 217 /** 218 * Returns an unbiased estimation of the standard deviation of a population, 219 * given the specified random sample. If the specified array is not an array of 220 * numbers, an optional accessor function <tt>f</tt> can be specified to map the 221 * elements to numbers. See {@link #normalize} for an example. Accessor 222 * functions can refer to <tt>this.index</tt>. 223 * 224 * @param {array} array an array of objects, or numbers. 225 * @param {function} [f] an optional accessor function. 226 * @returns {number} the standard deviation of the specified array. 227 */ 228 pv.deviation = function(array, f) { 229 return Math.sqrt(pv.variance(array, f) / (array.length - 1)); 230 }; 231 232 /** 233 * Returns the logarithm with a given base value. 234 * 235 * @param {number} x the number for which to compute the logarithm. 236 * @param {number} b the base of the logarithm. 237 * @returns {number} the logarithm value. 238 */ 239 pv.log = function(x, b) { 240 return Math.log(x) / Math.log(b); 241 }; 242 243 /** 244 * Computes a zero-symmetric logarithm. Computes the logarithm of the absolute 245 * value of the input, and determines the sign of the output according to the 246 * sign of the input value. 247 * 248 * @param {number} x the number for which to compute the logarithm. 249 * @param {number} b the base of the logarithm. 250 * @returns {number} the symmetric log value. 251 */ 252 pv.logSymmetric = function(x, b) { 253 return (x == 0) ? 0 : ((x < 0) ? -pv.log(-x, b) : pv.log(x, b)); 254 }; 255 256 /** 257 * Computes a zero-symmetric logarithm, with adjustment to values between zero 258 * and the logarithm base. This adjustment introduces distortion for values less 259 * than the base number, but enables simultaneous plotting of log-transformed 260 * data involving both positive and negative numbers. 261 * 262 * @param {number} x the number for which to compute the logarithm. 263 * @param {number} b the base of the logarithm. 264 * @returns {number} the adjusted, symmetric log value. 265 */ 266 pv.logAdjusted = function(x, b) { 267 if (!isFinite(x)) return x; 268 var negative = x < 0; 269 if (x < b) x += (b - x) / b; 270 return negative ? -pv.log(x, b) : pv.log(x, b); 271 }; 272 273 /** 274 * Rounds an input value down according to its logarithm. The method takes the 275 * floor of the logarithm of the value and then uses the resulting value as an 276 * exponent for the base value. 277 * 278 * @param {number} x the number for which to compute the logarithm floor. 279 * @param {number} b the base of the logarithm. 280 * @returns {number} the rounded-by-logarithm value. 281 */ 282 pv.logFloor = function(x, b) { 283 return (x > 0) 284 ? Math.pow(b, Math.floor(pv.log(x, b))) 285 : -Math.pow(b, -Math.floor(-pv.log(-x, b))); 286 }; 287 288 /** 289 * Rounds an input value up according to its logarithm. The method takes the 290 * ceiling of the logarithm of the value and then uses the resulting value as an 291 * exponent for the base value. 292 * 293 * @param {number} x the number for which to compute the logarithm ceiling. 294 * @param {number} b the base of the logarithm. 295 * @returns {number} the rounded-by-logarithm value. 296 */ 297 pv.logCeil = function(x, b) { 298 return (x > 0) 299 ? Math.pow(b, Math.ceil(pv.log(x, b))) 300 : -Math.pow(b, -Math.ceil(-pv.log(-x, b))); 301 }; 302 303 (function() { 304 var radians = Math.PI / 180, 305 degrees = 180 / Math.PI; 306 307 /** Returns the number of radians corresponding to the specified degrees. */ 308 pv.radians = function(degrees) { return radians * degrees; }; 309 310 /** Returns the number of degrees corresponding to the specified radians. */ 311 pv.degrees = function(radians) { return degrees * radians; }; 312 })(); 313