1 /*
  2  * Copyright (c) 2012 LabKey Corporation
  3  *
  4  * Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
  5  */
  6 
  7 /********** Stats **********/
  8 
  9 if(!LABKEY.vis.Stat){
 10     /**
 11      * @namespace The namespace used for statistics related functions.
 12      */
 13 	LABKEY.vis.Stat = {};
 14 }
 15 
 16 
 17 /**
 18  * Calculates a statistical summary of an array of data. The summary includes Quartiles 1, 2, 3, minimum, maximum and
 19  * the inner quartile range. It is used internally to create box plots.
 20  * @param {Array} data An array of data. Can be an array of any type of object.
 21  * @param {Function} accessor A function that is used to access the value of each item in the array.
 22  * @returns {Object} summary
 23  * @example
 24     var data = [],
 25         accessor,
 26         summary;
 27 
 28     // Let's generate some data.
 29     for (var i = 0; i < 500; i++){
 30         data.push(parseInt(Math.random() * 50));
 31     }
 32 
 33     // Let's define how we access the data.
 34     accessor = function(row){
 35         return row;
 36     }
 37 
 38     // Now we'll get a summary.
 39     summary = LABKEY.vis.Stat.summary(data, accessor);
 40 
 41     console.log(summary);
 42  *
 43  */
 44 LABKEY.vis.Stat.summary = function(data, accessor){
 45     /*
 46         Returns an object with the min, max, Q1, Q2 (median), Q3, interquartile range, and the sorted array of values.
 47      */
 48     var summary = {};
 49 
 50     summary.sortedValues = LABKEY.vis.Stat.sortNumericAscending(data, accessor);
 51     summary.min = summary.sortedValues[0];
 52     summary.max = summary.sortedValues[summary.sortedValues.length -1];
 53     summary.Q1 = LABKEY.vis.Stat.Q1(summary.sortedValues);
 54     summary.Q2 = LABKEY.vis.Stat.Q2(summary.sortedValues);
 55     summary.Q3 = LABKEY.vis.Stat.Q3(summary.sortedValues);
 56     summary.IQR = summary.Q3 - summary.Q1;
 57 
 58     return summary;
 59 };
 60 
 61 /**
 62  * Returns the 1st quartile for a sorted (asc) array.
 63  * @param numbers An array of numbers.
 64  * @returns {Number}
 65  */
 66 LABKEY.vis.Stat.Q1 = function(numbers){
 67     return d3.quantile(numbers,0.25);
 68 };
 69 
 70 /**
 71  * Returns the 2nd quartile (median) for a sorted (asc) array.
 72  * @param numbers An array of numbers.
 73  * @returns {Number}
 74  */
 75 LABKEY.vis.Stat.Q2 = function(numbers){
 76     return d3.quantile(numbers,0.5);
 77 };
 78 
 79 /**
 80  * An alias for {@link LABKEY.vis.Stat.Q2}
 81  */
 82 LABKEY.vis.Stat.median = LABKEY.vis.Stat.Q2;
 83 
 84 
 85 /**
 86  * Returns the 3rd quartile for a sorted (asc) array.
 87  * @param numbers An array of numbers.
 88  * @returns {Number}
 89  */
 90 LABKEY.vis.Stat.Q3 = function(numbers){
 91     return d3.quantile(numbers,0.75);
 92 };
 93 
 94 
 95 /**
 96  * Sorts an array of data in ascending order. Removes null/undefined values.
 97  * @param {Array} data An array of objects that have numeric values.
 98  * @param {Function} accessor A function used to access the numeric value that needs to be sorted.
 99  * @returns {Array}
100  */
101 LABKEY.vis.Stat.sortNumericAscending = function(data, accessor){
102     var numbers = [];
103     for(var i = 0; i < data.length; i++){
104         var value = accessor(data[i]);
105         if(value !== null && value !== undefined){
106             numbers.push(value);
107         }
108     }
109     numbers.sort(function(a, b){return a-b;});
110     return numbers;
111 };
112 
113 /**
114  * Sorts an array of data in descending order. Removes null/undefined values.
115  * @param {Array} data An array of objects that have numeric values.
116  * @param {Function} accessor A function used to access the numeric value that needs to be sorted.
117  * @returns {Array}
118  */
119 LABKEY.vis.Stat.sortNumericDescending = function(data, accessor){
120     var numbers = [];
121     for(var i = 0; i < data.length; i++){
122         var value = accessor(data[i]);
123         if(value !== null && value !== undefined){
124             numbers.push(value);
125         }
126     }
127     numbers.sort(function(a, b){return b-a;});
128     return numbers;
129 };
130 
131 /**
132  * Executes a given function n times passing in values between min and max and returns an array of each result. Could
133  * be used to generate data to plot a curve fit as part of a plot.
134  * @param {Function} fn The function to be executed n times. The function must take one number as a parameter.
135  * @param {Number} n The number of times to execute fn.
136  * @param {Number} min The minimum value to pass to fn.
137  * @param {Number} max The maximum value to pass to fn.
138  */
139 LABKEY.vis.Stat.fn = function(fn, n, min, max){
140     if(n === undefined || n === null || n < 2){
141         // We need at least 2 points to make a line.
142         n = 2;
143     }
144 
145     var data = [],
146         stepSize = Math.abs((max - min) / (n-1)),
147         count = min;
148 
149     for(var i = 0; i < n; i++){
150         data.push({x: count, y: fn(count)});
151         count += stepSize;
152     }
153 
154     return data;
155 };
156