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
 28   * Most functions are R-like:
 29   * For example prod(a1,a2) computes an array c such that
 30   * for (i=0;i<a1.length;i++) c[i] = a1[i]*a2[i];
 31   *
 32  **/
 33 JXG.Math.Statistics = {};
 34 
 35 JXG.Math.Statistics.sum = function(arr) {
 36     var i, len, res = 0;
 37     
 38     for(i=0, len=arr.length; i<len; i++) { 
 39         res += arr[i];
 40     } 
 41     return res;
 42 };
 43 
 44 JXG.Math.Statistics.prod = function(arr) {
 45     var i, len, res = 1;
 46     
 47     for(i=0, len=arr.length; i<len; i++) { 
 48         res *= arr[i];
 49     } 
 50     return res;
 51 };
 52 
 53 JXG.Math.Statistics.mean = function(arr) {
 54     if (arr.length>0) {
 55         return this.sum(arr)/arr.length;
 56     } else {
 57         return 0.0;
 58     }
 59 };
 60 
 61 JXG.Math.Statistics.median = function(arr) {
 62     var tmp, len;
 63     
 64     if (arr.length>0) {
 65         tmp = arr.slice(0,arr.length);
 66         tmp.sort(function(a,b){return a-b;});
 67         len = tmp.length;
 68         if (len%2==1) {
 69             return tmp[parseInt(len*0.5)];
 70         } else{
 71             return (tmp[len*0.5-1]+tmp[len*0.5])*0.5;
 72         }
 73     } else {
 74         return 0.0;
 75     }
 76 };
 77 
 78 /**
 79  * bias-corrected sample variance
 80  */
 81 JXG.Math.Statistics.variance = function(arr) {
 82     var m, res, i, len;
 83     
 84     if (arr.length>1) {
 85         m = this.mean(arr);
 86         res = 0;
 87         for(i=0, len=arr.length; i<len; i++) { 
 88             res += (arr[i]-m)*(arr[i]-m);
 89         } 
 90         return res/(arr.length-1);
 91     } else {
 92         return 0.0;
 93     }
 94 };
 95 
 96 JXG.Math.Statistics.sd = function(arr) {
 97     return Math.sqrt(this.variance(arr));
 98 };
 99 
100 JXG.Math.Statistics.weightedMean = function(arr,w) {
101     if (arr.length!=w.length) { return; }
102     if (arr.length>0) {
103         return this.mean(this.multiply(arr,w));
104     } else {
105         return 0.0;
106     }
107 };
108 
109 JXG.Math.Statistics.max = function(arr) {
110     var res, i, len;
111     
112     if (arr.length==0) { return NaN; }
113     res = arr[0];
114     for(i=1, len=arr.length; i<len; i++) { 
115         res = (arr[i]>res)?(arr[i]):res;
116     } 
117     return res;
118 };
119 
120 JXG.Math.Statistics.min = function(arr) {
121     var res, i, len;
122 
123     if (arr.length==0) { return NaN; }
124     res = arr[0];
125     for(i=1, len=arr.length; i<len; i++) { 
126         res = (arr[i]<res)?(arr[i]):res;
127     } 
128     return res;
129 };
130 
131 /**
132  * R-style functions
133  */
134 JXG.Math.Statistics.range = function(arr) {
135     return [this.min(arr),this.max(arr)];
136 };
137 
138 JXG.Math.Statistics.diff = function(arr) { // ?????
139     return arr;
140 };
141 
142 JXG.Math.Statistics.min = function(arr) {
143     var res, i, len;
144     
145     if (arr.length==0) { return NaN; }
146     res = arr[0];
147     for(i=1, len=arr.length; i<len; i++) { 
148         res = (arr[i]<res)?(arr[i]):res;
149     } 
150     return res;
151 };
152 
153 JXG.Math.Statistics.abs = function(arr) {  // This can be generalized with Prototype.js and should be done for all Math. methods
154     var i, len, res = [];
155     if (typeof JXG.isArray(arr1)) {
156         for (i=0, len=arr.length;i<len;i++) { res[i] = Math.abs(arr[i]); }
157     } else if (typeof arr=='number') {
158         return Math.abs(arr);
159     } else {
160         res = null;
161     }
162     return res;
163 };
164 
165 JXG.Math.Statistics.add = function(arr1,arr2) {
166     var i, len, res = [];
167     
168     if (typeof JXG.isArray(arr1) && typeof arr2=='number') {
169         for (i=0, len=arr1.length;i<len;i++) { res[i] = arr1[i]+arr2; }
170     } else if (typeof arr1=='number' && typeof JXG.isArray(arr2)) {
171         for (i=0, len=arr2.length;i<len;i++) { res[i] = arr1+arr2[i]; }
172     } else if (typeof JXG.isArray(arr1) && typeof JXG.isArray(arr2)) {
173         for (i=0, len=Math.min(arr1.length,arr2.length);i<len;i++) { res[i] = arr1[i]+arr2[i]; }
174     } else if (typeof arr1=='number' && typeof arr2=='number') {
175         res = arr1+arr2;
176     } else {
177         res = null;
178     }
179     return res;
180 };
181 
182 JXG.Math.Statistics.divide = function(arr1,arr2) {
183     var i, len, res = [];
184     
185     if (typeof JXG.isArray(arr1) && typeof arr2=='number') {
186         for (i=0, len=arr1.length;i<len;i++) { res[i] = arr1[i]/arr2; }
187     } else if (typeof arr1=='number' && typeof JXG.isArray(arr2)) {
188         for (i=0, len=arr2.length;i<len;i++) { res[i] = arr1/arr2[i]; }
189     } else if (typeof JXG.isArray(arr1) && typeof JXG.isArray(arr2)) {
190         for (i=0, len=Math.min(arr1.length,arr2.length);i<len;i++) { res[i] = arr1[i]/arr2[i]; }
191     } else if (typeof arr1=='number' && typeof arr2=='number') {
192         res = arr1/arr2;
193     } else {
194         res = null;
195     }
196     return res;
197 };
198 
199 JXG.Math.Statistics.mod = function(arr1,arr2) {
200     var i, len, res = [];
201     
202     if (typeof JXG.isArray(arr1) && typeof arr2=='number') {
203         for (i=0, len=arr1.length;i<len;i++) { res[i] = arr1[i]%arr2; }
204     } else if (typeof arr1=='number' && typeof JXG.isArray(arr2)) {
205         for (i=0, len=arr2.length;i<len;i++) { res[i] = arr1%arr2[i]; }
206     } else if (typeof JXG.isArray(arr1) && typeof JXG.isArray(arr2)) {
207         for (i=0, len=Math.min(arr1.length,arr2.length);i<len;i++) { res[i] = arr1[i]%arr2[i]; }
208     } else if (typeof arr1=='number' && typeof arr2=='number') {
209         res = arr1%arr2; 
210     } else {
211         res = null;
212     }
213     return res;
214 };
215 
216 JXG.Math.Statistics.multiply = function(arr1,arr2) {
217     var i, len, res = [];
218     
219     if (typeof JXG.isArray(arr1) && typeof arr2=='number') {
220         for (i=0, len=arr1.length;i<len;i++) { res[i] = arr1[i]*arr2; }
221     } else if (typeof arr1=='number' && typeof JXG.isArray(arr2)) {
222         for (i=0, len=arr2.length;i<len;i++) { res[i] = arr1*arr2[i]; }
223     } else if (typeof JXG.isArray(arr1) && typeof JXG.isArray(arr2)) {
224         for (i=0, len=Math.min(arr1.length,arr2.length);i<len;i++) { res[i] = arr1[i]*arr2[i]; }
225     } else if (typeof arr1=='number' && typeof arr2=='number') {
226         res = arr1*arr2;
227     } else {
228         res = null;
229     }
230     return res;
231 };
232 
233 JXG.Math.Statistics.subtract = function(arr1,arr2) {
234     var i, len, res = [];
235     
236     if (typeof JXG.isArray(arr1) && typeof arr2=='number') {
237         for (i=0, len=arr1.length;i<len;i++) { res[i] = arr1[i]-arr2; }
238     } else if (typeof arr1=='number' && typeof JXG.isArray(arr2)) {
239         for (i=0, len=arr2.length;i<len;i++) { res[i] = arr1-arr2[i]; }
240     } else if (typeof JXG.isArray(arr1) && typeof JXG.isArray(arr2)) {
241         for (i=0, len=Math.min(arr1.length,arr2.length);i<len;i++) { res[i] = arr1[i]-arr2[i]; }
242     } else if (typeof arr1=='number' && typeof arr2=='number') {
243         res = arr1-arr2; 
244     } else {
245         res = null;
246     }
247     return res;
248 };
249