      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