1 /*
  2     Copyright 2008,2009
  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: you can redistribute it and/or modify
 13     it under the terms of the GNU Lesser General Public License as published by
 14     the Free Software Foundation, either version 3 of the License, or
 15     (at your option) any later version.
 16 
 17     JSXGraph is distributed in the hope that it will be useful,
 18     but WITHOUT ANY WARRANTY; without even the implied warranty of
 19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20     GNU Lesser General Public License for more details.
 21 
 22     You should have received a copy of the GNU Lesser General Public License
 23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 24 */
 25 
 26 /**
 27  * Functions for mathematical statistics. Most functions are like in the statistics package R.
 28  * @namespace
 29  */
 30 JXG.Math.Statistics = {
 31 
 32     /**
 33      * Sums up all elements of the given array.
 34      * @param {Array} arr An array of numbers.
 35      * @returns {Number}
 36      */
 37     sum: function (arr) {
 38         var i, len = arr.length, res = 0;
 39 
 40         for (i = 0; i<len; i++) {
 41             res += arr[i];
 42         }
 43         return res;
 44     },
 45 
 46     /**
 47      * Multiplies all elements of the given array.
 48      * @param {Array} arr An array of numbers.
 49      * @returns {Number}
 50      */
 51     prod: function (arr) {
 52         var i, len = arr.length, res = 1;
 53 
 54         for (i = 0; i < len; i++) {
 55             res *= arr[i];
 56         }
 57         return res;
 58     },
 59 
 60     /**
 61      * Determines the mean value of the values given in an array.
 62      * @param {Array} arr
 63      * @returns {Number}
 64      */
 65     mean: function (arr) {
 66         if (arr.length > 0) {
 67             return this.sum(arr)/arr.length;
 68         } else {
 69             return 0.0;
 70         }
 71     },
 72 
 73     /**
 74      * The median of a finite set of values is the value that divides the set
 75      * into two equal sized subsets.
 76      * @param {Array} arr The set of values.
 77      * @returns {Number}
 78      */
 79     median: function (arr) {
 80         var tmp, len;
 81 
 82         if (arr.length > 0) {
 83             tmp = arr.slice(0);
 84             tmp.sort(function(a, b) {
 85                 return a - b;
 86             });
 87             len = tmp.length;
 88             if (len % 2 == 1) {
 89                 return tmp[parseInt(len*0.5)];
 90             } else{
 91                 return (tmp[len*0.5-1]+tmp[len*0.5])*0.5;
 92             }
 93         } else {
 94             return 0.0;
 95         }
 96     },
 97 
 98     /**
 99      * Bias-corrected sample variance. A variance is a measure of how far a
100      * set of numbers are spread out from each other.
101      * @param {Array} arr
102      * @returns {Number}
103      */
104     variance: function (arr) {
105         var m, res, i, len = arr.length;
106 
107         if (len > 1) {
108             m = this.mean(arr);
109             res = 0;
110             for(i = 0; i < len; i++) {
111                 res += (arr[i] - m) * (arr[i] - m);
112             }
113             return res/(arr.length - 1);
114         } else {
115             return 0.0;
116         }
117     },
118 
119     /**
120      * Determines the <strong>s</strong>tandard <strong>d</strong>eviation which shows how much
121      * variation there is from the average value of a set of numbers.
122      * @param {Array} arr
123      * @returns {Number}
124      */
125     sd: function (arr) {
126         return Math.sqrt(this.variance(arr));
127     },
128 
129     /**
130      * Weighted mean value is basically the same as {@link JXG.Math.Statistics#mean} but here the values
131      * are weighted, i.e. multiplied with another value called <em>weight</em>. The weight values are given
132      * as a second array with the same length as the value array..
133      * @throws {Error} If the dimensions of the arrays don't match.
134      * @param {Array} arr Set of alues.
135      * @param {Array} w Weight values.
136      * @returns {Number}
137      */
138     weightedMean: function (arr, w) {
139         if (arr.length != w.length) {
140             throw new Error('JSXGraph error (Math.Statistics.weightedMean): Array dimension mismatch.');
141         }
142 
143         if (arr.length > 0) {
144             return this.mean(this.multiply(arr, w));
145         } else {
146             return 0.0;
147         }
148     },
149 
150     /**
151      * Extracts the maximum value from the array.
152      * @param {Array} arr
153      * @returns {Number} The highest number from the array. It returns <tt>NaN</tt> if not every element could be
154      * interpreted as a number and <tt>-Infinity</tt> if an empty array is given or no element could be interpreted
155      * as a number.
156      */
157     max: function (arr) {
158         return Math.max.apply(this, arr);
159     },
160 
161     /**
162      * Extracts the minimum value from the array.
163      * @param {Array} arr
164      * @returns {Number} The lowest number from the array. It returns <tt>NaN</tt> if not every element could be
165      * interpreted as a number and <tt>Infinity</tt> if an empty array is given or no element could be interpreted
166      * as a number.
167      */
168     min: function (arr) {
169         return Math.min.apply(this, arr);
170     },
171 
172     /**
173      * Determines the lowest and the highest value from the given array.
174      * @param {Array} arr
175      * @returns {Array} The minimum value as the first and the maximum value as the second value.
176      */
177     range: function (arr) {
178         return [this.min(arr), this.max(arr)];
179     },
180 
181     /**
182      * Determines the absolute value of every given value.
183      * @param {Array|Number} arr
184      * @returns {Array|Number}
185      */
186     abs: function (arr) {
187         var i, len, res;
188 
189         if (JXG.isArray(arr)) {
190             len = arr.length;
191             res = [];
192 
193             for (i = 0; i < len; i++) {
194                 res[i] = Math.abs(arr[i]);
195             }
196         } else {
197             res = Math.abs(arr);
198         }
199 
200         return res;
201     },
202 
203     /**
204      * Adds up two (sequences of) values. If one value is an array and the other one is a number the number
205      * is added to every element of the array. If two arrays are given and the lengths don't match the shortest
206      * length is taken.
207      * @param {Array|Number} arr1
208      * @param {Array|Number} arr2
209      * @returns {Array|Number}
210      */
211     add: function (arr1, arr2) {
212         var i, len, res = [];
213 
214         if (JXG.isArray(arr1) && JXG.isNumber(arr2)) {
215             len = arr1.length;
216 
217             for (i = 0; i < len; i++) {
218                 res[i] = arr1[i] + arr2;
219             }
220         } else if (JXG.isNumber(arr1) && JXG.isArray(arr2)) {
221             len = arr2.length;
222 
223             for (i = 0; i < len; i++) {
224                 res[i] = arr1 + arr2[i];
225             }
226         } else if (JXG.isArray(arr1) && JXG.isArray(arr2)) {
227             len = Math.min(arr1.length, arr2.length);
228 
229             for (i = 0; i < len; i++) {
230                 res[i] = arr1[i] + arr2[i];
231             }
232         } else {
233             res = arr1 + arr2;
234         }
235         
236         return res;
237     },
238 
239     /**
240      * Divides two (sequences of) values. If two arrays are given and the lengths don't match the shortest length
241      * is taken.
242      * @param {Array|Number} arr1 Dividend
243      * @param {Array|Number} arr2 Divisor
244      * @returns {Array|Number}
245      */
246     div: function (arr1, arr2) {
247         var i, len, res = [];
248 
249         if (JXG.isArray(arr1) && JXG.isNumber(arr2)) {
250             len = arr1.length;
251 
252             for (i = 0; i < len; i++) {
253                 res[i] = arr1[i] / arr2;
254             }
255         } else if (JXG.isNumber(arr1) && JXG.isArray(arr2)) {
256             len = arr2.length;
257 
258             for (i = 0; i < len; i++) {
259                 res[i] = arr1 / arr2[i];
260             }
261         } else if (JXG.isArray(arr1) && JXG.isArray(arr2)) {
262             len = Math.min(arr1.length, arr2.length);
263 
264             for (i = 0; i < len; i++) {
265                 res[i] = arr1[i] / arr2[i];
266             }
267         } else {
268             res = arr1 / arr2;
269         }
270 
271         return res;
272     },
273 
274     /**
275      * @function
276      * @deprecated Use {@link JXG.Math.Statistics#div} instead.
277      */
278     divide: JXG.shortcut(JXG.Math.Statistics, 'div'),
279 
280     /**
281      * Divides two (sequences of) values and returns the remainder. If two arrays are given and the lengths don't
282      * match the shortest length is taken.
283      * @param {Array|Number} arr1 Dividend
284      * @param {Array|Number} arr2 Divisor
285      * @param {Boolean} [math=false] Mathematical mod or symmetric mod? Default is symmetric, the JavaScript <tt>%</tt> operator.
286      * @returns {Array|Number}
287      */
288     mod: function (arr1, arr2, math) {
289         var i, len, res = [], mod = function (a, m) {
290             return a % m;
291         };
292 
293         math = JXG.def(math, false);
294 
295         if (math) {
296             mod = JXG.Math.mod;
297         }
298 
299         if (JXG.isArray(arr1) && JXG.isNumber(arr2)) {
300             len = arr1.length;
301 
302             for (i = 0; i < len; i++) {
303                 res[i] = mod(arr1[i], arr2);
304             }
305         } else if (JXG.isNumber(arr1) && JXG.isArray(arr2)) {
306             len = arr2.length;
307 
308             for (i = 0; i < len; i++) {
309                 res[i] = mod(arr1, arr2[i]);
310             }
311         } else if (JXG.isArray(arr1) && JXG.isArray(arr2)) {
312             len = Math.min(arr1.length, arr2.length);
313 
314             for (i = 0; i < len; i++) {
315                 res[i] = mod(arr1[i], arr2[i]);
316             }
317         } else {
318             res = mod(arr1, arr2);
319         }
320 
321         return res;
322     },
323 
324     /**
325      * Multiplies two (sequences of) values. If one value is an array and the other one is a number the number
326      * is multiplied to every element of the array. If two arrays are given and the lengths don't match the shortest
327      * length is taken.
328      * @param {Array|Number} arr1
329      * @param {Array|Number} arr2
330      * @returns {Array|Number}
331      */
332     multiply: function (arr1, arr2) {
333         var i, len, res = [];
334 
335         if (JXG.isArray(arr1) && JXG.isNumber(arr2)) {
336             len = arr1.length;
337 
338             for (i = 0; i < len; i++) {
339                 res[i] = arr1[i] * arr2;
340             }
341         } else if (JXG.isNumber(arr1) && JXG.isArray(arr2)) {
342             len = arr2.length;
343 
344             for (i = 0; i < len; i++) {
345                 res[i] = arr1 * arr2[i];
346             }
347         } else if (JXG.isArray(arr1) && JXG.isArray(arr2)) {
348             len = Math.min(arr1.length, arr2.length);
349 
350             for (i = 0; i < len; i++) {
351                 res[i] = arr1[i] * arr2[i];
352             }
353         } else {
354             res = arr1 * arr2;
355         }
356 
357         return res;
358     },
359 
360     /**
361      * Subtracts two (sequences of) values. If two arrays are given and the lengths don't match the shortest
362      * length is taken.
363      * @param {Array|Number} arr1 Minuend
364      * @param {Array|Number} arr2 Subtrahend
365      * @returns {Array|Number}
366      */
367     subtract: function (arr1, arr2) {
368         var i, len, res = [];
369 
370         if (JXG.isArray(arr1) && JXG.isNumber(arr2)) {
371             len = arr1.length;
372 
373             for (i = 0; i < len; i++) {
374                 res[i] = arr1[i] - arr2;
375             }
376         } else if (JXG.isNumber(arr1) && JXG.isArray(arr2)) {
377             len = arr2.length;
378 
379             for (i = 0; i < len; i++) {
380                 res[i] = arr1 - arr2[i];
381             }
382         } else if (JXG.isArray(arr1) && JXG.isArray(arr2)) {
383             len = Math.min(arr1.length, arr2.length);
384 
385             for (i = 0; i < len; i++) {
386                 res[i] = arr1[i] - arr2[i];
387             }
388         } else {
389             res = arr1 - arr2;
390         }
391 
392         return res;
393     }
394 };