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