/** * Simple Javascript eXtension - 1.0, Mathematics Module * (c) 2011 - 2015 Steve L. Nyemba, steve@the-phi.com * License GPL version 3.0 * * dependencies: * utils.js implementation of design patterns and utilities * * This file contains an enhancement of utilities integrated into the jx.math.* built-in package of javascript * Because we implement math and numerical functions it is to be understood that most of the functions will have common preconditions * i.e lxi.constructor == Array && isNumber(lxi) unless specified otherwise * jx.math.max * jx.math.min * jx.math.sum * jx.math.prod * jx.math.freq * jx.math.avg * jx.math.mean computes the mean/average of a list of observations (arthmetic mean included too) * jx.math.sd computes the standard deviation of a list of observations * jx.math.var computes the variance of a list of observations * jx.math.diff computes the absolute difference of values in an array * jx.math.fibonacci comptutes the fibonacci value of a given number * jx.math.factorial computes the factorial of a given number */ if(!jx){ var jx = {} ; } jx.math = {} jx.math.sqrt = Math.sqrt; jx.math.PHI = (1+jx.math.sqrt(5))/2 ;//1.61803399 ; /** * @param lxi list of observatins xi */ jx.math.max = function(lxi){ sortNumber= function(a,b) { return a - b; } index = lxi.length -1 ; max = jx.utils.cast(lxi,Number).sort(sortNumber)[index] ; // perhaps need to cast return max ; } /** * finds the minimum of a list of observation lxi (vector of values) * @param lxi list/vector of values/observations */ jx.math.min = function(lxi){ sortNumber = function(a,b){ return a- b; } min = jx.utils.cast(lxi,Number).sort(sortNumber)[0] ; return min ; } /** * @pre : values.constructor == Array * @param lxi list of observed values to be summed */ jx.math.sum = function(lxi){ return eval(lxi.join('+')); } ; /** * This function will compute the frequency of a vector i.e providing relative values */ jx.math.freq = function(lxi){ var N = jx.math.sum(lxi) ; return jx.utils.patterns.visitor(lxi,function(xi){ return xi/N ; }); } /** * This function will perform the product of a vector * @pre lxi.constructor == Array && isNumber(lxi) */ jx.math.prod = function(lxi){ return eval(lxi.join('*')) ; } /** * @pre : lni != null && lxi.length == lni.length * @param lxi list of observed values * @param lni list of the number of times observations of index i have been made */ jx.math.avg = function(lxi,lni){ N = lxi.length ; if(lni == null){ return jx.math.sum(lxi)/N ; }else{ values = [] for(var i=0; i < lxi.length; i++){ values[i] = Number(lxi[i])*Number(lni[i]) ; } return Number(jx.math.sum(values)/N) ; } }; /** * This function will repete a value a given number of times * @pre times > 0 */ jx.math.rep = function(value,times){ var lvalues = [] for(var i=0; i < times; i++){ lvalues.push(value) ; } return lvalues; } jx.math.mean = jx.math.avg ; jx.math.pow = Math.pow jx.math.sd = function(lxi,lni){ N = lxi.length ; mean = jx.math.mean(lxi,lni) ; sqr = [] ; for(var i=0; i < lxi.length ;i++){ sqr[i] = jx.math.pow((Number(lxi[i])-mean),2 ) ; } total = jx.math.sum(sqr); return jx.math.sqrt(total/(N-1)) ; } ; /** * This function computes the correlation between two vectors * @pre x1.length == x2.length */ jx.math.cor = function(x1,x2){ return jx.math.cov(x1,x2) / (jx.math.sd(x1)*jx.math.sd(x2)) } /** * This function will compute the covariance of 2 vectors * @pre x1.length == x2.length */ jx.math.cov = function(x1,x2){ var u1 = jx.math.mean(x1) ; var u2 = jx.math.mean(x2) ; var N = x1.length ; var value = 0 for(var i in x1){ value += (x1[i] - u1) * (x2[i] - u2) } return value / (N - 1) } /** * Computes the factorial of a given value */ jx.math.factorial = function(value){ r =value; for(var i =value-1; i > 0; i--){ r *= i ; } return r; } ; /** * Computes the fibonacci value of a given number using the golden ratio */ jx.math.fibonacci = function(value){ r = (jx.math.pow(jx.math.PHI,value)/jx.math.sqrt(5)) + 0.5 ; return jx.math.floor(r) ; } ; /** * computes the absolute difference of values in a list of observations */ jx.math.diff = function(lxi){ var r = [] ; var x,y; for(var i=0; i < lxi.length-1; i++){ x = lxi[i] ; y = lxi[i+1] ; r.push(y-x) } return r ; }; /** * This section implements a few handlers based on sets */ jx.math.sets = {} ; /** * This function will perform a unique operation of values/objects * @param list list/vector of values or objects * @param equals operator to be used, only provide this for complex objects */ jx.math.sets.unique = jx.utils.unique ; /** * This function will perform the union of 2 sets (objects, or values) * @param list1 list/vector of values or objects * @param list2 list/vector of values or objects * @param equals operator to be used to evaluate equality (use this for complex objects) */ jx.math.sets.union = function(list1,list2,equals){ runion = [] ; runion = list1.concat(list2) ; runion = jx.math.sets.unique(runion,equals) return runion; } /** * This function will normalize values within a vector * By definition normalization is (x - u) / sd (assuming population parameters are known) */ jx.math.normalize = function(lvalues){ mean = jx.math.mean(lvalues) ; sd = jx.math.sd(lvalues) ; return jx.utils.patterns.visitor(lvalues,function(x){ return ((x - mean) / sd) }) } /** * This function will scale a feature vector over it's range */ jx.math.scale = function(lvalues,percent){ max = jx.math.max(lvalues) ; min = jx.math.min(lvalues) ; return jx.utils.patterns.visitor(lvalues,function(x){ var value = (x - min ) / max ; if(percent == true){ return (100*value).toFixed(2) }else{ return value ; } }) } /** * This is a lightweight map reduce infrastructure */ jx.mr = {} ; /** * This function will perform a map on a given id in rec, then will call emit with the */ jx.mr.map = null /** * @param keys * @param values array of values that were mapped */ jx.mr.reduce = null; jx.mr.mapreduce = function(data,fn_map,fn_reduce){ if (fn_map == null){ throw new "Map function is not defined" } map = {} ; emit = function(id,values){ if(map[id] == null){ map[id] = [] } map[id].push(values); } if(data.constructor != Array){ for (id in data){ //rec = data[id] ; rec = {} rec['__id'] = id; rec['data'] = data[id] ; fn_map(rec,emit) } }else{ for (var i=0; i < data.length; i++){ rec = data[i]; fn_map(rec,emit); //if(i == 2)break; } } if(fn_reduce != null){ keys = jx.utils.keys(map) ; m = {} for(var i=0; i < keys.length; i++){ id = keys[i] ; values = map[id] ; value = fn_reduce(id,values) ; id = keys[i] ; m[id] = value; } map = m } return map ; }