stats.js 5.8 KB

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