/src/data/Numbers.js

https://github.com/bigfix/protovis · JavaScript · 312 lines · 126 code · 18 blank · 168 comment · 41 complexity · ca0933f898f6ba37aa6072bcd2bdf6da MD5 · raw file

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