(function(jStat, Math) { var slice = [].slice; var isNumber = jStat.utils.isNumber; var isArray = jStat.utils.isArray; // flag==true denotes use of sample standard deviation // Z Statistics jStat.extend({ // 2 different parameter lists: // (value, mean, sd) // (value, array, flag) zscore: function zscore() { var args = slice.call(arguments); if (isNumber(args[1])) { return (args[0] - args[1]) / args[2]; } return (args[0] - jStat.mean(args[1])) / jStat.stdev(args[1], args[2]); }, // 3 different paramter lists: // (value, mean, sd, sides) // (zscore, sides) // (value, array, sides, flag) ztest: function ztest() { var args = slice.call(arguments); var z; if (isArray(args[1])) { // (value, array, sides, flag) z = jStat.zscore(args[0],args[1],args[3]); return (args[2] === 1) ? (jStat.normal.cdf(-Math.abs(z), 0, 1)) : (jStat.normal.cdf(-Math.abs(z), 0, 1)*2); } else { if (args.length > 2) { // (value, mean, sd, sides) z = jStat.zscore(args[0],args[1],args[2]); return (args[3] === 1) ? (jStat.normal.cdf(-Math.abs(z),0,1)) : (jStat.normal.cdf(-Math.abs(z),0,1)* 2); } else { // (zscore, sides) z = args[0]; return (args[1] === 1) ? (jStat.normal.cdf(-Math.abs(z),0,1)) : (jStat.normal.cdf(-Math.abs(z),0,1)*2); } } } }); jStat.extend(jStat.fn, { zscore: function zscore(value, flag) { return (value - this.mean()) / this.stdev(flag); }, ztest: function ztest(value, sides, flag) { var zscore = Math.abs(this.zscore(value, flag)); return (sides === 1) ? (jStat.normal.cdf(-zscore, 0, 1)) : (jStat.normal.cdf(-zscore, 0, 1) * 2); } }); // T Statistics jStat.extend({ // 2 parameter lists // (value, mean, sd, n) // (value, array) tscore: function tscore() { var args = slice.call(arguments); return (args.length === 4) ? ((args[0] - args[1]) / (args[2] / Math.sqrt(args[3]))) : ((args[0] - jStat.mean(args[1])) / (jStat.stdev(args[1], true) / Math.sqrt(args[1].length))); }, // 3 different paramter lists: // (value, mean, sd, n, sides) // (tscore, n, sides) // (value, array, sides) ttest: function ttest() { var args = slice.call(arguments); var tscore; if (args.length === 5) { tscore = Math.abs(jStat.tscore(args[0], args[1], args[2], args[3])); return (args[4] === 1) ? (jStat.studentt.cdf(-tscore, args[3]-1)) : (jStat.studentt.cdf(-tscore, args[3]-1)*2); } if (isNumber(args[1])) { tscore = Math.abs(args[0]) return (args[2] == 1) ? (jStat.studentt.cdf(-tscore, args[1]-1)) : (jStat.studentt.cdf(-tscore, args[1]-1) * 2); } tscore = Math.abs(jStat.tscore(args[0], args[1])) return (args[2] == 1) ? (jStat.studentt.cdf(-tscore, args[1].length-1)) : (jStat.studentt.cdf(-tscore, args[1].length-1) * 2); } }); jStat.extend(jStat.fn, { tscore: function tscore(value) { return (value - this.mean()) / (this.stdev(true) / Math.sqrt(this.cols())); }, ttest: function ttest(value, sides) { return (sides === 1) ? (1 - jStat.studentt.cdf(Math.abs(this.tscore(value)), this.cols()-1)) : (jStat.studentt.cdf(-Math.abs(this.tscore(value)), this.cols()-1)*2); } }); // F Statistics jStat.extend({ // Paramter list is as follows: // (array1, array2, array3, ...) // or it is an array of arrays // array of arrays conversion anovafscore: function anovafscore() { var args = slice.call(arguments), expVar, sample, sampMean, sampSampMean, tmpargs, unexpVar, i, j; if (args.length === 1) { tmpargs = new Array(args[0].length); for (i = 0; i < args[0].length; i++) { tmpargs[i] = args[0][i]; } args = tmpargs; } // Builds sample array sample = new Array(); for (i = 0; i < args.length; i++) { sample = sample.concat(args[i]); } sampMean = jStat.mean(sample); // Computes the explained variance expVar = 0; for (i = 0; i < args.length; i++) { expVar = expVar + args[i].length * Math.pow(jStat.mean(args[i]) - sampMean, 2); } expVar /= (args.length - 1); // Computes unexplained variance unexpVar = 0; for (i = 0; i < args.length; i++) { sampSampMean = jStat.mean(args[i]); for (j = 0; j < args[i].length; j++) { unexpVar += Math.pow(args[i][j] - sampSampMean, 2); } } unexpVar /= (sample.length - args.length); return expVar / unexpVar; }, // 2 different paramter setups // (array1, array2, array3, ...) // (anovafscore, df1, df2) anovaftest: function anovaftest() { var args = slice.call(arguments), df1, df2, n, i; if (isNumber(args[0])) { return 1 - jStat.centralF.cdf(args[0], args[1], args[2]); } var anovafscore = jStat.anovafscore(args); df1 = args.length - 1; n = 0; for (i = 0; i < args.length; i++) { n = n + args[i].length; } df2 = n - df1 - 1; return 1 - jStat.centralF.cdf(anovafscore, df1, df2); }, ftest: function ftest(fscore, df1, df2) { return 1 - jStat.centralF.cdf(fscore, df1, df2); } }); jStat.extend(jStat.fn, { anovafscore: function anovafscore() { return jStat.anovafscore(this.toArray()); }, anovaftes: function anovaftes() { var n = 0; var i; for (i = 0; i < this.length; i++) { n = n + this[i].length; } return jStat.ftest(this.anovafscore(), this.length - 1, n - this.length); } }); // Tukey's range test jStat.extend({ // 2 parameter lists // (mean1, mean2, n1, n2, sd) // (array1, array2, sd) qscore: function qscore() { var args = slice.call(arguments); var mean1, mean2, n1, n2, sd; if (isNumber(args[0])) { mean1 = args[0]; mean2 = args[1]; n1 = args[2]; n2 = args[3]; sd = args[4]; } else { mean1 = jStat.mean(args[0]); mean2 = jStat.mean(args[1]); n1 = args[0].length; n2 = args[1].length; sd = args[2]; } return Math.abs(mean1 - mean2) / (sd * Math.sqrt((1 / n1 + 1 / n2) / 2)); }, // 3 different parameter lists: // (qscore, n, k) // (mean1, mean2, n1, n2, sd, n, k) // (array1, array2, sd, n, k) qtest: function qtest() { var args = slice.call(arguments); var qscore; if (args.length === 3) { qscore = args[0]; args = args.slice(1); } else if (args.length === 7) { qscore = jStat.qscore(args[0], args[1], args[2], args[3], args[4]); args = args.slice(5); } else { qscore = jStat.qscore(args[0], args[1], args[2]); args = args.slice(3); } var n = args[0]; var k = args[1]; return 1 - jStat.tukey.cdf(qscore, k, n - k); }, tukeyhsd: function tukeyhsd(arrays) { var sd = jStat.pooledstdev(arrays); var means = arrays.map(function (arr) {return jStat.mean(arr);}); var n = arrays.reduce(function (n, arr) {return n + arr.length;}, 0); var results = []; for (var i = 0; i < arrays.length; ++i) { for (var j = i + 1; j < arrays.length; ++j) { var p = jStat.qtest(means[i], means[j], arrays[i].length, arrays[j].length, sd, n, arrays.length); results.push([[i, j], p]); } } return results; } }); // Error Bounds jStat.extend({ // 2 different parameter setups // (value, alpha, sd, n) // (value, alpha, array) normalci: function normalci() { var args = slice.call(arguments), ans = new Array(2), change; if (args.length === 4) { change = Math.abs(jStat.normal.inv(args[1] / 2, 0, 1) * args[2] / Math.sqrt(args[3])); } else { change = Math.abs(jStat.normal.inv(args[1] / 2, 0, 1) * jStat.stdev(args[2]) / Math.sqrt(args[2].length)); } ans[0] = args[0] - change; ans[1] = args[0] + change; return ans; }, // 2 different parameter setups // (value, alpha, sd, n) // (value, alpha, array) tci: function tci() { var args = slice.call(arguments), ans = new Array(2), change; if (args.length === 4) { change = Math.abs(jStat.studentt.inv(args[1] / 2, args[3] - 1) * args[2] / Math.sqrt(args[3])); } else { change = Math.abs(jStat.studentt.inv(args[1] / 2, args[2].length - 1) * jStat.stdev(args[2], true) / Math.sqrt(args[2].length)); } ans[0] = args[0] - change; ans[1] = args[0] + change; return ans; }, significant: function significant(pvalue, alpha) { return pvalue < alpha; } }); jStat.extend(jStat.fn, { normalci: function normalci(value, alpha) { return jStat.normalci(value, alpha, this.toArray()); }, tci: function tci(value, alpha) { return jStat.tci(value, alpha, this.toArray()); } }); // internal method for calculating the z-score for a difference of proportions test function differenceOfProportions(p1, n1, p2, n2) { if (p1 > 1 || p2 > 1 || p1 <= 0 || p2 <= 0) { throw new Error("Proportions should be greater than 0 and less than 1") } var pooled = (p1 * n1 + p2 * n2) / (n1 + n2); var se = Math.sqrt(pooled * (1 - pooled) * ((1/n1) + (1/n2))); return (p1 - p2) / se; } // Difference of Proportions jStat.extend(jStat.fn, { oneSidedDifferenceOfProportions: function oneSidedDifferenceOfProportions(p1, n1, p2, n2) { var z = differenceOfProportions(p1, n1, p2, n2); return jStat.ztest(z, 1); }, twoSidedDifferenceOfProportions: function twoSidedDifferenceOfProportions(p1, n1, p2, n2) { var z = differenceOfProportions(p1, n1, p2, n2); return jStat.ztest(z, 2); } }); }(jStat, Math));