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