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 });