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