stats.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.math.stats"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.math.stats"] = true;
  8. dojo.provide("dojox.math.stats");
  9. dojo.getObject("math.stats", true, dojox);
  10. (function(){
  11. var st = dojox.math.stats;
  12. dojo.mixin(st, {
  13. sd: function(/* Number[] */a){
  14. // summary:
  15. // Returns the standard deviation of the passed arguments.
  16. return Math.sqrt(st.variance(a)); // Number
  17. },
  18. variance: function(/* Number[] */a){
  19. // summary:
  20. // Find the variance in the passed array of numbers.
  21. var mean=0, squares=0;
  22. dojo.forEach(a, function(item){
  23. mean+=item;
  24. squares+=Math.pow(item,2);
  25. });
  26. return (squares/a.length)-Math.pow(mean/a.length, 2); // Number
  27. },
  28. bestFit: function(/* Object[] || Number[] */a, /* String? */xProp, /* String? */yProp){
  29. // summary:
  30. // Calculate the slope and intercept in a linear fashion. An array
  31. // of objects is expected; optionally you can pass in the property
  32. // names for "x" and "y", else x/y is used as the default. If you
  33. // pass an array of numbers, it will be mapped to a set of {x,y} objects
  34. // where x = the array index.
  35. xProp = xProp || "x", yProp = yProp || "y";
  36. if(a[0] !== undefined && typeof(a[0]) == "number"){
  37. // this is an array of numbers, so use the index as x.
  38. a = dojo.map(a, function(item, idx){
  39. return { x: idx, y: item };
  40. });
  41. }
  42. var sx = 0, sy = 0, sxx = 0, syy = 0, sxy = 0, stt = 0, sts = 0, n = a.length, t;
  43. for(var i=0; i<n; i++){
  44. sx += a[i][xProp];
  45. sy += a[i][yProp];
  46. sxx += Math.pow(a[i][xProp], 2);
  47. syy += Math.pow(a[i][yProp], 2);
  48. sxy += a[i][xProp] * a[i][yProp];
  49. }
  50. // we use the following because it's more efficient and accurate for determining the slope.
  51. for(i=0; i<n; i++){
  52. t = a[i][xProp] - sx/n;
  53. stt += t*t;
  54. sts += t*a[i][yProp];
  55. }
  56. var slope = sts/(stt||1); // prevent divide by zero.
  57. // get Pearson's R
  58. var d = Math.sqrt((sxx - Math.pow(sx,2)/n) * (syy - Math.pow(sy,2)/n));
  59. if(d === 0){
  60. throw new Error("dojox.math.stats.bestFit: the denominator for Pearson's R is 0.");
  61. }
  62. var r = (sxy-(sx*sy/n)) / d;
  63. var r2 = Math.pow(r, 2);
  64. if(slope < 0){
  65. r = -r;
  66. }
  67. // to use: y = slope*x + intercept;
  68. return { // Object
  69. slope: slope,
  70. intercept: (sy - sx*slope)/(n||1),
  71. r: r,
  72. r2: r2
  73. };
  74. },
  75. forecast: function(/* Object[] || Number[] */a, /* Number */x, /* String? */xProp, /* String? */yProp){
  76. // summary:
  77. // Using the bestFit algorithm above, find y for the given x.
  78. var fit = st.bestFit(a, xProp, yProp);
  79. return (fit.slope * x) + fit.intercept; // Number
  80. },
  81. mean: function(/* Number[] */a){
  82. // summary:
  83. // Returns the mean value in the passed array.
  84. var t=0;
  85. dojo.forEach(a, function(v){
  86. t += v;
  87. });
  88. return t / Math.max(a.length, 1); // Number
  89. },
  90. min: function(/* Number[] */a){
  91. // summary:
  92. // Returns the min value in the passed array.
  93. return Math.min.apply(null, a); // Number
  94. },
  95. max: function(/* Number[] */a){
  96. // summary:
  97. // Returns the max value in the passed array.
  98. return Math.max.apply(null, a); // Number
  99. },
  100. median: function(/* Number[] */a){
  101. // summary:
  102. // Returns the value closest to the middle from a sorted version of the passed array.
  103. var t = a.slice(0).sort(function(a, b){ return a - b; });
  104. return (t[Math.floor(a.length/2)] + t[Math.ceil(a.length/2)])/2; // Number
  105. },
  106. mode: function(/* Number[] */a){
  107. // summary:
  108. // Returns the mode from the passed array (number that appears the most often).
  109. // This is not the most efficient method, since it requires a double scan, but
  110. // is ensures accuracy.
  111. var o = {}, r = 0, m = Number.MIN_VALUE;
  112. dojo.forEach(a, function(v){
  113. (o[v]!==undefined)?o[v]++:o[v]=1;
  114. });
  115. // we did the lookup map because we need the number that appears the most.
  116. for(var p in o){
  117. if(m < o[p]){
  118. m = o[p], r = p;
  119. }
  120. }
  121. return r; // Number
  122. },
  123. sum: function(/* Number[] */a){
  124. // summary:
  125. // Return the sum of all the numbers in the passed array. Does
  126. // not check to make sure values within a are NaN (should simply
  127. // return NaN).
  128. var sum = 0;
  129. dojo.forEach(a, function(n){
  130. sum += n;
  131. });
  132. return sum; // Number
  133. },
  134. approxLin: function(a, pos){
  135. // summary:
  136. // Returns a linearly approximated value from an array using
  137. // a normalized float position value.
  138. // a: Number[]:
  139. // a sorted numeric array to be used for the approximation.
  140. // pos: Number:
  141. // a position number from 0 to 1. If outside of this range it
  142. // will be clamped.
  143. // returns: Number
  144. var p = pos * (a.length - 1), t = Math.ceil(p), f = t - 1;
  145. if(f < 0){ return a[0]; }
  146. if(t >= a.length){ return a[a.length - 1]; }
  147. return a[f] * (t - p) + a[t] * (p - f); // Number
  148. },
  149. summary: function(a, alreadySorted){
  150. // summary:
  151. // Returns a non-parametric collection of summary statistics:
  152. // the classic five-number summary extended to the Bowley's
  153. // seven-figure summary.
  154. // a: Number[]:
  155. // a numeric array to be appraised.
  156. // alreadySorted: Boolean?:
  157. // a Boolean flag to indicated that the array is already sorted.
  158. // This is an optional flag purely to improve the performance.
  159. // If skipped, the array will be assumed unsorted.
  160. // returns: Object
  161. if(!alreadySorted){
  162. a = a.slice(0); // copy the array
  163. a.sort(function(a, b){ return a - b; }); // sort it properly
  164. }
  165. var l = st.approxLin,
  166. result = {
  167. // the five-number summary
  168. min: a[0], // minimum
  169. p25: l(a, 0.25), // lower quartile
  170. med: l(a, 0.5), // median
  171. p75: l(a, 0.75), // upper quartile
  172. max: a[a.length - 1], // maximum
  173. // extended to the Bowley's seven-figure summary
  174. p10: l(a, 0.1), // first decile
  175. p90: l(a, 0.9) // last decile
  176. };
  177. return result; // Object
  178. }
  179. });
  180. })();
  181. }