1 /*
  2     Copyright 2008-2014
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
 13 
 14     You can redistribute it and/or modify it under the terms of the
 15 
 16       * GNU Lesser General Public License as published by
 17         the Free Software Foundation, either version 3 of the License, or
 18         (at your option) any later version
 19       OR
 20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
 21 
 22     JSXGraph is distributed in the hope that it will be useful,
 23     but WITHOUT ANY WARRANTY; without even the implied warranty of
 24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 25     GNU Lesser General Public License for more details.
 26 
 27     You should have received a copy of the GNU Lesser General Public License and
 28     the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/>
 29     and <http://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 
 33 /*global JXG: true, define: true*/
 34 /*jslint nomen: true, plusplus: true*/
 35 
 36 /* depends:
 37  jxg
 38  math/math
 39  utils/type
 40  */
 41 
 42 define(['jxg', 'math/math', 'utils/type'], function (JXG, Mat, Type) {
 43 
 44     "use strict";
 45 
 46     /**
 47      * Functions for mathematical statistics. Most functions are like in the statistics package R.
 48      * @name JXG.Math.Statistics
 49      * @exports Mat.Statistics as JXG.Math.Statistics
 50      * @namespace
 51      */
 52     Mat.Statistics = {
 53         /**
 54          * Sums up all elements of the given array.
 55          * @param {Array} arr An array of numbers.
 56          * @returns {Number}
 57          * @memberof JXG.Math.Statistics
 58          */
 59         sum: function (arr) {
 60             var i,
 61                 len = arr.length,
 62                 res = 0;
 63 
 64             for (i = 0; i < len; i++) {
 65                 res += arr[i];
 66             }
 67             return res;
 68         },
 69 
 70         /**
 71          * Multiplies all elements of the given array.
 72          * @param {Array} arr An array of numbers.
 73          * @returns {Number}
 74          * @memberof JXG.Math.Statistics
 75          */
 76         prod: function (arr) {
 77             var i,
 78                 len = arr.length,
 79                 res = 1;
 80 
 81             for (i = 0; i < len; i++) {
 82                 res *= arr[i];
 83             }
 84             return res;
 85         },
 86 
 87         /**
 88          * Determines the mean value of the values given in an array.
 89          * @param {Array} arr
 90          * @returns {Number}
 91          * @memberof JXG.Math.Statistics
 92          */
 93         mean: function (arr) {
 94             if (arr.length > 0) {
 95                 return this.sum(arr) / arr.length;
 96             }
 97 
 98             return 0.0;
 99         },
100 
101         /**
102          * The median of a finite set of values is the value that divides the set
103          * into two equal sized subsets.
104          * @param {Array} arr The set of values.
105          * @returns {Number}
106          * @memberof JXG.Math.Statistics
107          */
108         median: function (arr) {
109             var tmp, len;
110 
111             if (arr.length > 0) {
112                 tmp = arr.slice(0);
113                 tmp.sort(function (a, b) {
114                     return a - b;
115                 });
116                 len = tmp.length;
117 
118                 if (len % 2 === 1) {
119                     return tmp[parseInt(len * 0.5, 10)];
120                 }
121 
122                 return (tmp[len * 0.5 - 1] + tmp[len * 0.5]) * 0.5;
123             }
124 
125             return 0.0;
126         },
127 
128         /**
129          * Bias-corrected sample variance. A variance is a measure of how far a
130          * set of numbers are spread out from each other.
131          * @param {Array} arr
132          * @returns {Number}
133          * @memberof JXG.Math.Statistics
134          */
135         variance: function (arr) {
136             var m, res, i, len = arr.length;
137 
138             if (len > 1) {
139                 m = this.mean(arr);
140                 res = 0;
141                 for (i = 0; i < len; i++) {
142                     res += (arr[i] - m) * (arr[i] - m);
143                 }
144                 return res / (arr.length - 1);
145             }
146 
147             return 0.0;
148         },
149 
150         /**
151          * Determines the <strong>s</strong>tandard <strong>d</strong>eviation which shows how much
152          * variation there is from the average value of a set of numbers.
153          * @param {Array} arr
154          * @returns {Number}
155          * @memberof JXG.Math.Statistics
156          */
157         sd: function (arr) {
158             return Math.sqrt(this.variance(arr));
159         },
160 
161         /**
162          * Weighted mean value is basically the same as {@link JXG.Math.Statistics.mean} but here the values
163          * are weighted, i.e. multiplied with another value called <em>weight</em>. The weight values are given
164          * as a second array with the same length as the value array..
165          * @throws {Error} If the dimensions of the arrays don't match.
166          * @param {Array} arr Set of alues.
167          * @param {Array} w Weight values.
168          * @returns {Number}
169          * @memberof JXG.Math.Statistics
170          */
171         weightedMean: function (arr, w) {
172             if (arr.length !== w.length) {
173                 throw new Error('JSXGraph error (Math.Statistics.weightedMean): Array dimension mismatch.');
174             }
175 
176             if (arr.length > 0) {
177                 return this.mean(this.multiply(arr, w));
178             }
179 
180             return 0.0;
181         },
182 
183         /**
184          * Extracts the maximum value from the array.
185          * @param {Array} arr
186          * @returns {Number} The highest number from the array. It returns <tt>NaN</tt> if not every element could be
187          * interpreted as a number and <tt>-Infinity</tt> if an empty array is given or no element could be interpreted
188          * as a number.
189          * @memberof JXG.Math.Statistics
190          */
191         max: function (arr) {
192             return Math.max.apply(this, arr);
193         },
194 
195         /**
196          * Extracts the minimum value from the array.
197          * @param {Array} arr
198          * @returns {Number} The lowest number from the array. It returns <tt>NaN</tt> if not every element could be
199          * interpreted as a number and <tt>Infinity</tt> if an empty array is given or no element could be interpreted
200          * as a number.
201          * @memberof JXG.Math.Statistics
202          */
203         min: function (arr) {
204             return Math.min.apply(this, arr);
205         },
206 
207         /**
208          * Determines the lowest and the highest value from the given array.
209          * @param {Array} arr
210          * @returns {Array} The minimum value as the first and the maximum value as the second value.
211          * @memberof JXG.Math.Statistics
212          */
213         range: function (arr) {
214             return [this.min(arr), this.max(arr)];
215         },
216 
217         /**
218          * Determines the absolute value of every given value.
219          * @param {Array|Number} arr
220          * @returns {Array|Number}
221          * @memberof JXG.Math.Statistics
222          */
223         abs: function (arr) {
224             var i, len, res;
225 
226             if (Type.isArray(arr)) {
227                 len = arr.length;
228                 res = [];
229 
230                 for (i = 0; i < len; i++) {
231                     res[i] = Math.abs(arr[i]);
232                 }
233             } else {
234                 res = Math.abs(arr);
235             }
236 
237             return res;
238         },
239 
240         /**
241          * Adds up two (sequences of) values. If one value is an array and the other one is a number the number
242          * is added to every element of the array. If two arrays are given and the lengths don't match the shortest
243          * length is taken.
244          * @param {Array|Number} arr1
245          * @param {Array|Number} arr2
246          * @returns {Array|Number}
247          * @memberof JXG.Math.Statistics
248          */
249         add: function (arr1, arr2) {
250             var i, len, res = [];
251 
252             arr1 = Type.evalSlider(arr1);
253             arr2 = Type.evalSlider(arr2);
254 
255             if (Type.isArray(arr1) && Type.isNumber(arr2)) {
256                 len = arr1.length;
257 
258                 for (i = 0; i < len; i++) {
259                     res[i] = arr1[i] + arr2;
260                 }
261             } else if (Type.isNumber(arr1) && Type.isArray(arr2)) {
262                 len = arr2.length;
263 
264                 for (i = 0; i < len; i++) {
265                     res[i] = arr1 + arr2[i];
266                 }
267             } else if (Type.isArray(arr1) && Type.isArray(arr2)) {
268                 len = Math.min(arr1.length, arr2.length);
269 
270                 for (i = 0; i < len; i++) {
271                     res[i] = arr1[i] + arr2[i];
272                 }
273             } else {
274                 res = arr1 + arr2;
275             }
276 
277             return res;
278         },
279 
280         /**
281          * Divides two (sequences of) values. If two arrays are given and the lengths don't match the shortest length
282          * is taken.
283          * @param {Array|Number} arr1 Dividend
284          * @param {Array|Number} arr2 Divisor
285          * @returns {Array|Number}
286          * @memberof JXG.Math.Statistics
287          */
288         div: function (arr1, arr2) {
289             var i, len, res = [];
290 
291             arr1 = Type.evalSlider(arr1);
292             arr2 = Type.evalSlider(arr2);
293 
294             if (Type.isArray(arr1) && Type.isNumber(arr2)) {
295                 len = arr1.length;
296 
297                 for (i = 0; i < len; i++) {
298                     res[i] = arr1[i] / arr2;
299                 }
300             } else if (Type.isNumber(arr1) && Type.isArray(arr2)) {
301                 len = arr2.length;
302 
303                 for (i = 0; i < len; i++) {
304                     res[i] = arr1 / arr2[i];
305                 }
306             } else if (Type.isArray(arr1) && Type.isArray(arr2)) {
307                 len = Math.min(arr1.length, arr2.length);
308 
309                 for (i = 0; i < len; i++) {
310                     res[i] = arr1[i] / arr2[i];
311                 }
312             } else {
313                 res = arr1 / arr2;
314             }
315 
316             return res;
317         },
318 
319         /**
320          * @function
321          * @deprecated Use {@link JXG.Math.Statistics.div} instead.
322          * @memberof JXG.Math.Statistics
323          */
324         divide: function () {
325             JXG.deprecated('Statistics.divide()', 'Statistics.div()');
326             Mat.Statistics.div.apply(Mat.Statistics, arguments);
327         },
328 
329         /**
330          * Divides two (sequences of) values and returns the remainder. If two arrays are given and the lengths don't
331          * match the shortest length is taken.
332          * @param {Array|Number} arr1 Dividend
333          * @param {Array|Number} arr2 Divisor
334          * @param {Boolean} [math=false] Mathematical mod or symmetric mod? Default is symmetric, the JavaScript <tt>%</tt> operator.
335          * @returns {Array|Number}
336          * @memberof JXG.Math.Statistics
337          */
338         mod: function (arr1, arr2, math) {
339             var i, len, res = [], mod = function (a, m) {
340                 return a % m;
341             };
342 
343             math = Type.def(math, false);
344 
345             if (math) {
346                 mod = Mat.mod;
347             }
348 
349             arr1 = Type.evalSlider(arr1);
350             arr2 = Type.evalSlider(arr2);
351 
352             if (Type.isArray(arr1) && Type.isNumber(arr2)) {
353                 len = arr1.length;
354 
355                 for (i = 0; i < len; i++) {
356                     res[i] = mod(arr1[i], arr2);
357                 }
358             } else if (Type.isNumber(arr1) && Type.isArray(arr2)) {
359                 len = arr2.length;
360 
361                 for (i = 0; i < len; i++) {
362                     res[i] = mod(arr1, arr2[i]);
363                 }
364             } else if (Type.isArray(arr1) && Type.isArray(arr2)) {
365                 len = Math.min(arr1.length, arr2.length);
366 
367                 for (i = 0; i < len; i++) {
368                     res[i] = mod(arr1[i], arr2[i]);
369                 }
370             } else {
371                 res = mod(arr1, arr2);
372             }
373 
374             return res;
375         },
376 
377         /**
378          * Multiplies two (sequences of) values. If one value is an array and the other one is a number the number
379          * is multiplied to every element of the array. If two arrays are given and the lengths don't match the shortest
380          * length is taken.
381          * @param {Array|Number} arr1
382          * @param {Array|Number} arr2
383          * @returns {Array|Number}
384          * @memberof JXG.Math.Statistics
385          */
386         multiply: function (arr1, arr2) {
387             var i, len, res = [];
388 
389             arr1 = Type.evalSlider(arr1);
390             arr2 = Type.evalSlider(arr2);
391 
392             if (Type.isArray(arr1) && Type.isNumber(arr2)) {
393                 len = arr1.length;
394 
395                 for (i = 0; i < len; i++) {
396                     res[i] = arr1[i] * arr2;
397                 }
398             } else if (Type.isNumber(arr1) && Type.isArray(arr2)) {
399                 len = arr2.length;
400 
401                 for (i = 0; i < len; i++) {
402                     res[i] = arr1 * arr2[i];
403                 }
404             } else if (Type.isArray(arr1) && Type.isArray(arr2)) {
405                 len = Math.min(arr1.length, arr2.length);
406 
407                 for (i = 0; i < len; i++) {
408                     res[i] = arr1[i] * arr2[i];
409                 }
410             } else {
411                 res = arr1 * arr2;
412             }
413 
414             return res;
415         },
416 
417         /**
418          * Subtracts two (sequences of) values. If two arrays are given and the lengths don't match the shortest
419          * length is taken.
420          * @param {Array|Number} arr1 Minuend
421          * @param {Array|Number} arr2 Subtrahend
422          * @returns {Array|Number}
423          * @memberof JXG.Math.Statistics
424          */
425         subtract: function (arr1, arr2) {
426             var i, len, res = [];
427 
428             arr1 = Type.evalSlider(arr1);
429             arr2 = Type.evalSlider(arr2);
430 
431             if (Type.isArray(arr1) && Type.isNumber(arr2)) {
432                 len = arr1.length;
433 
434                 for (i = 0; i < len; i++) {
435                     res[i] = arr1[i] - arr2;
436                 }
437             } else if (Type.isNumber(arr1) && Type.isArray(arr2)) {
438                 len = arr2.length;
439 
440                 for (i = 0; i < len; i++) {
441                     res[i] = arr1 - arr2[i];
442                 }
443             } else if (Type.isArray(arr1) && Type.isArray(arr2)) {
444                 len = Math.min(arr1.length, arr2.length);
445 
446                 for (i = 0; i < len; i++) {
447                     res[i] = arr1[i] - arr2[i];
448                 }
449             } else {
450                 res = arr1 - arr2;
451             }
452 
453             return res;
454         },
455 
456         /**
457          * The Theil-Sen estimator can be used to determine a more robust linear regression of a set of sample
458          * points than least squares regression in {@link JXG.Math.Numerics.regressionPolynomial}.
459          * @param {Array} coords Array of {@link JXG.Coords}.
460          * @returns {Array} The stdform of the regression line.
461          * @memberof JXG.Math.Statistics
462          */
463         TheilSenRegression: function (coords) {
464             var i, j,
465                 slopes = [],
466                 tmpslopes = [],
467                 yintercepts = [];
468 
469             for (i = 0; i < coords.length; i++) {
470                 tmpslopes.length = 0;
471 
472                 for (j = 0; j < coords.length; j++) {
473                     if (Math.abs(coords[j].usrCoords[1] - coords[i].usrCoords[1]) > Mat.eps) {
474                         tmpslopes[j] = (coords[j].usrCoords[2] - coords[i].usrCoords[2]) /
475                             (coords[j].usrCoords[1] - coords[i].usrCoords[1]);
476                     }
477                 }
478 
479                 slopes[i] = this.median(tmpslopes);
480                 yintercepts.push(coords[i].usrCoords[2] - slopes[i] * coords[i].usrCoords[1]);
481             }
482 
483             return [this.median(yintercepts), this.median(slopes), -1];
484         }
485     };
486 
487     return Mat.Statistics;
488 });
489