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