├── .gitignore
├── readme.md
├── package.json
├── gruntfile.js
└── src
├── style.css
├── index.html
└── scripts
├── bayes.js
└── libs
└── jstat.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # BayesianTestJS
2 | Rudimentary plotting of prior and posterior distributions of a Beta-Bernoulli model, to aid inference in binary A/B tests.
3 |
4 | Made at [Lyst](http://www.lyst.com) and available [here](http://developers.lyst.com/bayesian-calculator/).
5 |
6 | # Usage
7 | Clone the repo and open `build/index.html`.
8 |
9 | If you make changes, use `grunt build` to build. You will need grunt.
10 |
11 |
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bayesian-ab-testing-js",
3 | "version": "0.0.1",
4 | "description": "Bayesian A/B test inference",
5 | "dependencies": {
6 | "jStat": "0.0.2",
7 | "d3": "3.4.6",
8 | "beta-js": "0.0.1"
9 | },
10 | "devDependencies": {
11 | "grunt": "^0.4.5",
12 | "grunt-contrib-uglify": "^0.4.0",
13 | "browserify": "^4.1.5",
14 | "grunt-browserify": "^2.1.0",
15 | "grunt-contrib-cssmin": "^0.9.0",
16 | "grunt-contrib-copy": "^0.5.0",
17 | "grunt-contrib-watch": "^0.6.1"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | // Load tasks.
4 | grunt.loadNpmTasks('grunt-browserify');
5 | grunt.loadNpmTasks('grunt-contrib-uglify');
6 | grunt.loadNpmTasks('grunt-contrib-cssmin');
7 | grunt.loadNpmTasks('grunt-contrib-copy');
8 | grunt.loadNpmTasks('grunt-contrib-watch');
9 |
10 | grunt.initConfig({
11 |
12 | browserify: {
13 | build : {
14 | files : {
15 | 'build/scripts/bayes.js' : ['src/scripts/bayes.js'],
16 | },
17 | }
18 | },
19 |
20 | uglify : {
21 | build : {
22 | files : {
23 | 'build/scripts/bayes.min.js' : ['build/scripts/bayes.js']
24 | }
25 | }
26 | },
27 |
28 | cssmin : {
29 | build: {
30 | files : {
31 | 'build/style.css' : ['src/style.css']
32 | }
33 | }
34 | },
35 |
36 | copy : {
37 | build: {
38 | files : {
39 | 'build/index.html' : ['src/index.html']
40 | }
41 | }
42 | },
43 |
44 | watch : {
45 | css : {
46 | files : ['src/style.css'],
47 | tasks : ['cssmin:build']
48 | },
49 | js : {
50 | files : ['src/bayes.js'],
51 | tasks : [
52 | 'browserify:build',
53 | 'uglify:build'
54 | ]
55 | },
56 | html : {
57 | files : ['src/index.html'],
58 | tasks : ['copy:build']
59 | }
60 | }
61 | });
62 |
63 | grunt.registerTask('build', [
64 | 'browserify:build',
65 | 'uglify:build',
66 | 'cssmin:build',
67 | 'copy:build'
68 | ]);
69 |
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | margin: 0;
4 | font-size: 14px;
5 | font-family: 'Helvetica Neue', sans-serif;
6 | background: #F1F1F1;
7 | color: #000;
8 | }
9 |
10 | h1, h2, h3, h4 {
11 | margin: 0;
12 | }
13 |
14 | h1 {
15 | font-size: 22px;
16 | font-weight: 500;
17 | text-align: center;
18 | margin: 20px 0;
19 | }
20 |
21 | h3 {
22 | font-size: 15px;
23 | font-weight: 500;
24 | margin-bottom: 10px;
25 | }
26 |
27 | h2 {
28 | font-size: 18px;
29 | font-weight: 500;
30 | }
31 |
32 | h4 {
33 | font-size: 12px;
34 | font-weight: 500;
35 | }
36 |
37 | p {
38 | margin: 15px 0;
39 | line-height: 22px;
40 | }
41 |
42 | ol {
43 | line-height: 22px;
44 | list-style: none;
45 | margin: 0;
46 | padding: 0;
47 | }
48 |
49 | ol li {
50 | margin: 15px 0;
51 | padding: 0 0 0 35px;
52 | position: relative;
53 | }
54 |
55 | ol .number {
56 | display: block;
57 | position: absolute;
58 | left: 0;
59 | top: 2px;
60 | margin-right: 10px;
61 | width: 20px;
62 | height: 20px;
63 | font-size: 12px;
64 | line-height: 19px;
65 | text-align: center;
66 | border-radius: 15px;
67 | background: #E9E9E9;
68 | font-weight: 500;
69 | color: #333;
70 | }
71 |
72 | /* Form Styling */
73 | .form-group {
74 | margin: 0 0 30px 0;
75 | }
76 |
77 | .form-title {
78 | margin: 20px;
79 | }
80 |
81 | .form-group-title {
82 | padding: 10px 20px;
83 | color: #212121;
84 | border-bottom: 1px solid #E9E9E9;
85 | }
86 |
87 | .form-buttons {
88 | padding: 15px;
89 | text-align: center;
90 | }
91 |
92 | fieldset {
93 | margin: 0;
94 | padding: 0 10px 0 20px;
95 | border: none;
96 | border-bottom: 1px solid #E9E9E9;
97 | overflow: hidden;
98 | cursor: pointer;
99 | }
100 |
101 | fieldset:hover {
102 | background: #F7F7F7;
103 | }
104 |
105 | label {
106 | display: block;
107 | float: left;
108 | width: 82px;
109 | line-height: 40px;
110 | font-size: 14px;
111 | cursor: pointer;
112 | }
113 |
114 | input {
115 | font-family: 'Helvetica Neue', sans-serif;
116 | }
117 |
118 | input[type="number"] {
119 | padding: 5px 0 5px 5px;
120 | margin: 0;
121 | width: 95px;
122 | float: left;
123 | font-size: 15px;
124 | background: none;
125 | box-sizing: border-box;
126 | text-align: right;
127 | border: none;
128 | line-height: 30px;
129 | cursor: pointer;
130 | }
131 |
132 | input[type="submit"],
133 | input[type="reset"] {
134 | border-radius: 3px;
135 | padding: 10px 15px;
136 | background: #F7F7F7;
137 | border: 1px solid #E9E9E9;
138 | font-weight: 500;
139 | cursor: pointer;
140 | color: #000;
141 | font-size: 13px;
142 | display:inline-block;
143 | }
144 |
145 | input[type="submit"]:hover,
146 | input[type="reset"]:hover {
147 | color: #555;
148 | }
149 |
150 | input[type="submit"]:active,
151 | input[type="reset"]:active {
152 | position: relative;
153 | top: 1px;
154 | }
155 |
156 | input[type="reset"] {
157 | background: #FFF;
158 | color: #444;
159 | margin-left: 5px;
160 | }
161 |
162 | input[type="reset"]:hover {
163 | color: #E84A4A;
164 | }
165 |
166 | input:focus {
167 | outline: none;
168 | }
169 |
170 | /* Wrappers */
171 | .wrapper {
172 | margin: 30px auto;
173 | width:940px;
174 | overflow: hidden;
175 | position: relative;
176 | }
177 |
178 | .wrapper,
179 | .sidebar,
180 | .content {
181 | box-sizing: border-box;
182 | }
183 |
184 | /* Preamble Styling */
185 | .header {
186 | padding: 35px 0 25px;
187 | }
188 |
189 | .logo {
190 | margin-bottom: 0;
191 | }
192 |
193 | .tagline {
194 | color: #999;
195 | font-size: 15px;
196 | text-align: center;
197 | margin-top: 8px;
198 | }
199 |
200 | .note-wrapper {
201 | padding: 20px;
202 | }
203 |
204 | .note {
205 | font-size: 14px;
206 | line-height: 22px;
207 | margin-bottom: 15px;
208 | }
209 |
210 |
211 |
212 | /* Sidebar Styling */
213 | .sidebar {
214 | width: 210px;
215 | position: absolute;
216 | left: 0;
217 | top: 0;
218 | height: 100%;
219 | background: #FFF;
220 | height: 100%;
221 | border-radius:3px 0 0 3px;
222 | border-right: 3px solid #F7F7F7;
223 | }
224 |
225 | .form-group-title.control:before,
226 | .form-group-title.test:before {
227 | content: "";
228 | display: inline-block;
229 | width: 6px;
230 | height: 6px;
231 | border-radius: 100px;
232 | margin-right: 5px;
233 | position: relative;
234 | top: -1px;
235 | }
236 |
237 | .form-group-title.control:before {
238 | background: #007AFF;
239 | }
240 |
241 | .form-group-title.test:before {
242 | background: #E93A5E;
243 | }
244 |
245 | /* Content Styling */
246 | .content {
247 | width: 730px;
248 | padding: 20px;
249 | margin-left: 210px;
250 | background: #FCFCFC;
251 | border-radius:0 3px 3px 0;
252 | }
253 |
254 | .content.full-width {
255 | margin-left: 0;
256 | width: 940px;
257 | border-radius: 3px;
258 | background: #FFF;
259 | padding: 20px 20px 40px 40px;
260 | }
261 |
262 | .chart {
263 | margin: 10px 0 0 0;
264 | }
265 |
266 | .chart + .chart {
267 | margin-top: 80px;
268 | }
269 |
270 | .chart-title {
271 | text-align: center;
272 | margin-bottom: 5px;
273 | }
274 |
275 | .chart-description {
276 | font-size: 13px;
277 | text-align: center;
278 | margin: 0 0 20px;
279 | color: #999;
280 | }
281 |
282 | .chart text { /* Axis labels */
283 | font-size: 12px;
284 | }
285 |
286 | .tick line { /* The little ticks on the X & Y axis */
287 | stroke: #C9C9C9;
288 | }
289 |
290 | .axis path { /* The X & Y Axis paths */
291 | stroke: #27344B;
292 | stroke-width: 1px;
293 | shape-rendering: crispEdges;
294 | fill: none;
295 | }
296 |
297 | /* Columns */
298 | .columns {
299 | overflow: hidden;
300 | }
301 |
302 | .column {
303 | box-sizing: border-box;
304 | padding-right: 30px;
305 | width: 50%;
306 | float:left;
307 | }
308 |
309 |
310 | /* PDF Plot */
311 | #pdfplot #testLine,
312 | #pdfplot #controlLine {
313 | opacity: 0.95;
314 | }
315 |
316 | #pdfplot #testLine {
317 | fill: #E93A5E;
318 | }
319 |
320 | #pdfplot #controlLine {
321 | fill: #007AFF;
322 | }
323 |
324 |
325 | /* Histogram Plot */
326 | #histogram .bar rect {
327 | fill: #007AFF;
328 | shape-rendering: crispEdges;
329 | }
330 | #histogram .bar:nth-child(2n){
331 | opacity: 0.95;
332 | }
333 |
334 |
335 | /* Quantile Table */
336 | #quantileTable {
337 | border: none;
338 | border-collapse: collapse;
339 | }
340 |
341 | #quantileTable td {
342 | cellspacing: 0px;
343 | border-right : 1px solid #000;
344 | padding: 10px;
345 | }
346 |
347 | .table-row-title {
348 | font-weight: 500;
349 | }
350 |
351 | #quantileTable td:last-child {
352 | border-right: none;
353 | }
354 |
355 | #quantileTable tr:nth-child(2n) {
356 | border-top: 1px solid #000;
357 | }
358 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Bayesian A/B Test Calculator
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
30 |
31 |
32 |
TL:DR; Instructions
33 |
34 |
35 | 1 Specify the prior alpha and beta parameters.
36 | 2 Plot the priors and revise parameters as necessary.
37 | 3 Enter data on the number of successes and failures in the test and control groups.
38 | 4 Plot to see posterior distributions.
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
97 |
98 |
99 |
100 |
101 |
Test and Control Probability Density Functions
102 |
The success probability distributions in test and control groups.
103 |
104 |
105 |
106 |
107 |
Histogram of Test - Control Probability
108 |
Distribution of differences in success probability between test and control groups.
109 |
110 |
111 |
112 |
113 |
Quantiles of the differences distribution.
114 |
Posterior probability that the difference lies below the value x.
115 |
116 |
117 | The average difference between test and control is: 0 . The probability that test performs better: 0.5
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
Explanation
133 |
134 |
135 |
136 |
This simple calculator uses the Beta-Bernoulli model (a binary outcome model, where the prior for the success probability is a Beta distribution) applied in the A/B testing context, where the goal of inference is understanding the probability that the test group performs better than the control group.
137 |
Bayesian inference consists in first specifying a prior belief about what effects are likely, and then updating the prior with incoming data.
138 |
For example, if our conversion rate is 5%, we may say that it's reasonably likely that a change we want to test could improve that by 5 percentage points—but that it is most likely that the change will have no effect, and that it is entirely unlikely that the conversion rate will shoot up to 30% (after all, we are only making a small change).
139 |
As the data start coming in, we start updating our beliefs. If the incoming data points point to an improvement in the conversion rate, we start moving our estimate of the effect from the prior upwards; the more data we collect, the more confident we are in it and the further we can move away from our prior. The end result is what is called the posterior—a probability distribution describing the likely effect from our treatment.
140 |
141 |
142 |
143 | 1 Specify the prior through the alpha and beta parameters of the Beta distribution . The parameter values govern two things: the prior success probability (our belief about the average conversion rate, for example) as well as the variance of the prior distribution (small alpha and beta will lead to a prior distribution where success probabilities can vary quite a lot around their mean; large values will lead to a distribution with a small variance). For example, setting alpha to 10 and beta to 10 will give us a prior distribution where the expected success probability is 0.5, but there is a fair amount of uncertainty around that value. Setting them to 100 and 100 will give us the same expected probability of 0.5, but the variance around that value will be much smaller.
144 | 2 Have a look at the histogram of success probability differences between the test and control. It expresses prior beliefs about the likely difference of success probabilities between the test and control groups. Because we specified a symmetric prior, the belief is centered around a difference of zero (a priori, A/B tests are just as likely to do worse as they are to do better than the control). If our priors have a low variance, the histogram will put put a low weight on large differences (it is unlikely that a test will do much better or much worse than the control); if the priors have a high variance, large differences will be much more likely.
145 | 3 Gather data!
146 | 4 Input the number of successes (conversions, clicks and so on) and failures in both the test and control groups. This triggers updating the priors with the data.
147 | 5 The prior plots shift to express posterior (prior updated with data) distributions. The density plots will (may) diverge, showing the posterior distributions of the success probability in test and control groups. Similarly, the difference histogram will shift. The part of the distribution lying to the right of zero expresses the confidence that the test performs better; the part to the left that it performs worse.
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/src/scripts/bayes.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var beta = require("beta-js");
4 | var d3 = require("d3");
5 | var jStat = require("./libs/jstat.js");
6 |
7 |
8 | var BetaModel = function (alpha, beta) {
9 |
10 | this.alpha = alpha;
11 | this.beta = beta;
12 | };
13 |
14 | BetaModel.prototype.distribution = function () {
15 | return beta.beta(this.alpha, this.beta);
16 | };
17 |
18 | BetaModel.prototype.getPDF = function (noPoints) {
19 | var pdf = [];
20 | var distribution = this.distribution();
21 | for (var i=0; i < noPoints; i++) {
22 | var val = distribution.pdf(i/noPoints);
23 | // Get rid of density singularities for plotting
24 | if (val == Number.POSITIVE_INFINITY) {
25 | val = 0;
26 | };
27 | pdf.push({'x': i/noPoints, 'y': val});
28 | };
29 | return pdf;
30 | };
31 |
32 | BetaModel.prototype.getRvs = function (noSamples) {
33 | return this.distribution().rvs(noSamples);
34 | };
35 |
36 | BetaModel.prototype.update = function (successes, failures) {
37 | this.alpha = this.alpha + successes;
38 | this.beta = this.beta + failures;
39 | };
40 |
41 | BetaModel.prototype.percentileOfScore = function(arr, score, kind) {
42 | var counter = 0;
43 | var len = arr.length;
44 | var strict = false;
45 | var value, i;
46 |
47 | if (kind === 'strict') strict = true;
48 |
49 | for (i = 0; i < len; i++) {
50 | value = arr[i];
51 | if ((strict && value < score) ||
52 | (!strict && value <= score)) {
53 | counter++;
54 | }
55 | }
56 |
57 | return counter / len;
58 | };
59 |
60 | BetaModel.prototype.mean = function(arr) {
61 | var i = 0;
62 | var counter = 0;
63 |
64 | for (i = 0; i < arr.length; i++) {
65 | counter = counter + arr[i];
66 | }
67 |
68 | return counter/i;
69 | };
70 |
71 | // -----------------------------------------------
72 |
73 | var Plots = function(alpha, beta) {
74 | this.controlBeta = new BetaModel(alpha, beta);
75 | this.testBeta = new BetaModel(alpha, beta);
76 | };
77 |
78 | Plots.prototype.getHistogramElements = function () {
79 |
80 | var noSamples = 5000;
81 | var noBins = 200;
82 |
83 | var controlData = this.controlBeta.getRvs(noSamples);
84 | var testData = this.testBeta.getRvs(noSamples);
85 | var differenceData = [];
86 |
87 | for (var i=0; i < controlData.length; i++) {
88 | differenceData.push(testData[i] - controlData[i]);
89 | };
90 |
91 | var margin = {top: 20, right: 20, bottom: 30, left: 50};
92 | var width = 690 - margin.left - margin.right;
93 | var height = 350 - margin.top - margin.bottom;
94 |
95 | var x = d3.scale.linear()
96 | .domain([-1, 1])
97 | .range([0, width]);
98 |
99 | var histogram = d3.layout.histogram()
100 | .bins(x.ticks(noBins))(differenceData);
101 |
102 | var y = d3.scale.linear()
103 | .domain([0, d3.max(histogram, function(d) { return d.y; })])
104 | .range([height, 0]);
105 |
106 | var xAxis = d3.svg.axis()
107 | .scale(x)
108 | .orient("bottom");
109 |
110 | var yAxis = d3.svg.axis()
111 | .scale(y)
112 | .orient("left");
113 |
114 | return {
115 | "margin": margin,
116 | "width": width,
117 | "height": height,
118 | "xAxis": xAxis,
119 | "yAxis": yAxis,
120 | "x": x,
121 | "y": y,
122 | "differenceData": differenceData,
123 | "histogram": histogram
124 | };
125 | };
126 |
127 | Plots.prototype.getPDFElements = function () {
128 |
129 | var controlData = this.controlBeta.getPDF(1000);
130 | var testData = this.testBeta.getPDF(1000);
131 | var allData = controlData.concat(testData);
132 | var interpolationMode = 'cardinal';
133 |
134 | var margin = {top: 20, right: 20, bottom: 30, left: 50};
135 | var width = 690 - margin.left - margin.right;
136 | var height = 350 - margin.top - margin.bottom;
137 |
138 | var x = d3.scale.linear()
139 | .domain(d3.extent(allData, function(d) { return d.x; }))
140 | .range([0, width]);
141 |
142 | var y = d3.scale.linear()
143 | .domain([0, d3.max(allData, function(d) { return d.y; })+1])
144 | .range([height, 0]);
145 |
146 | var xAxis = d3.svg.axis()
147 | .scale(x)
148 | .orient("bottom");
149 |
150 | var yAxis = d3.svg.axis()
151 | .scale(y)
152 | .orient("left");
153 |
154 | var controlLine = d3.svg.area()
155 | .x(function(d) { return x(d.x); })
156 | .y1(height)
157 | .y0(function(d) { return y(d.y); })
158 | .interpolate(interpolationMode);
159 |
160 | var testLine = d3.svg.area()
161 | .x(function(d) { return x(d.x); })
162 | .y1(height)
163 | .y0(function(d) { return y(d.y); })
164 | .interpolate(interpolationMode);
165 |
166 | return {
167 | "margin": margin,
168 | "width": width,
169 | "height": height,
170 | "xAxis": xAxis,
171 | "yAxis": yAxis,
172 | "testLine": testLine,
173 | "controlLine": controlLine,
174 | "testData": testData,
175 | "controlData": controlData
176 | };
177 | };
178 |
179 | Plots.prototype.drawHistogram = function () {
180 | var el = this.getHistogramElements();
181 |
182 | var svg = d3.select("#histogram").append("svg")
183 | .attr("width", el.width + el.margin.left + el.margin.right)
184 | .attr("height", el.height + el.margin.top + el.margin.bottom)
185 | .append("g")
186 | .attr("transform", "translate(" + el.margin.left + "," + el.margin.top + ")");
187 |
188 | svg.append("g")
189 | .attr("class", "x axis")
190 | .attr("transform", "translate(0," + el.height + ")")
191 | .call(el.xAxis);
192 |
193 | svg.append("g")
194 | .attr("class", "y axis")
195 | .call(el.yAxis)
196 | .append("text")
197 | .attr("transform", "rotate(-90)")
198 | .attr("y", 6)
199 | .attr("dy", ".71em")
200 | .style("text-anchor", "end")
201 | .text("Samples");
202 |
203 | var bar = svg.selectAll(".bar")
204 | .data(el.histogram)
205 | .enter().append("g")
206 | .attr("class", "bar")
207 | .attr("transform", function(d) { return "translate(" + el.x(d.x) + ",0)"; });
208 |
209 | bar.append("rect")
210 | .attr("x", 1)
211 | .attr("y", function(d) { return el.y(d.y);})
212 | .attr("width", el.histogram[0].dx/2 * el.width)
213 | .attr("height", function(d) { return el.height - el.y(d.y); });
214 |
215 | this.histogramSVG = svg;
216 |
217 | this.drawSummaryStatistics(el);
218 |
219 | };
220 |
221 |
222 | Plots.prototype.drawPDF = function () {
223 | var d = this.getPDFElements();
224 |
225 | var svg = d3.select("#pdfplot").append("svg")
226 | .attr("width", d.width + d.margin.left + d.margin.right)
227 | .attr("height", d.height + d.margin.top + d.margin.bottom)
228 | .append("g")
229 | .attr("transform", "translate(" + d.margin.left + "," + d.margin.top + ")");
230 |
231 | svg.append("g")
232 | .attr("class", "x axis")
233 | .attr("transform", "translate(0," + d.height + ")")
234 | .call(d.xAxis);
235 |
236 | svg.append("g")
237 | .attr("class", "y axis")
238 | .call(d.yAxis)
239 | .append("text")
240 | .attr("transform", "rotate(-90)")
241 | .attr("y", 6)
242 | .attr("dy", ".71em")
243 | .style("text-anchor", "end")
244 | .text("Density");
245 |
246 | svg.append("path")
247 | .datum(d.testData)
248 | .attr("class", "line")
249 | .attr("d", d.testLine)
250 | .attr("id", "testLine");
251 |
252 | svg.append("path")
253 | .datum(d.controlData)
254 | .attr("class", "area")
255 | .attr("d", d.controlLine)
256 | .attr("id", "controlLine");
257 |
258 | this.pdfSVG = svg;
259 | };
260 |
261 |
262 | Plots.prototype.drawTable = function (arr1, arr2) {
263 | var tb = '';
264 |
265 | tb += "";
266 | tb += "Percentiles ";
267 | for (var i=0; i < arr1.length; i++) {
268 | tb += ("" + arr1[i] * 100 + "% ");
269 | }
270 |
271 | tb += " ";
272 | tb += "Value ";
273 | for (var i=0; i < arr1.length; i++) {
274 | tb += ("" + (Math.round(100 * arr2[i]) / 100) + " ");
275 | }
276 |
277 | tb += " "
278 |
279 | return tb;
280 | };
281 |
282 | Plots.prototype.drawSummaryStatistics = function (el) {
283 |
284 | var quantiles = [0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.975, 0.99]
285 | var differenceQuantiles = jStat.jStat.quantiles(el.differenceData, quantiles);
286 | var tableElement = document.getElementById('quantileTable');
287 | tableElement.innerHTML = this.drawTable(quantiles, differenceQuantiles);
288 |
289 | var percentileOfZero = BetaModel.prototype.percentileOfScore(el.differenceData, 0);
290 | var testSuccessProbability = document.getElementById('testSuccessProbability');
291 | testSuccessProbability.innerHTML = Math.round((1.0 - percentileOfZero) * 100) / 100;
292 |
293 | var differenceMeanHTML = document.getElementById('differenceMean');
294 | var differenceMean = BetaModel.prototype.mean(el.differenceData);
295 | differenceMeanHTML.innerHTML = Math.round(100 * differenceMean) / 100;
296 | };
297 |
298 |
299 | Plots.prototype.redrawHistogram = function () {
300 | var el = this.getHistogramElements();
301 |
302 | var svg = this.histogramSVG;
303 |
304 | svg.selectAll("rect")
305 | .data(el.histogram)
306 | .transition()
307 | .duration(1000)
308 | .attr("y", function(d) { return el.y(d.y);})
309 | .attr("height", function(d) { return el.height - el.y(d.y); });
310 |
311 | this.drawSummaryStatistics(el);
312 | };
313 |
314 | Plots.prototype.redrawPDF = function () {
315 |
316 | var d = this.getPDFElements();
317 |
318 | var svg = this.pdfSVG;
319 |
320 | svg.select('#testLine')
321 | .datum(d.testData)
322 | .transition()
323 | .duration(1000)
324 | .attr("d", d.testLine);
325 |
326 | svg.select('#controlLine')
327 | .datum(d.controlData)
328 | .transition()
329 | .duration(1000)
330 | .attr("d", d.controlLine);
331 |
332 | svg.select('.y.axis')
333 | .transition()
334 | .duration(1000)
335 | .call(d.yAxis);
336 |
337 | svg.select('.x.axis')
338 | .transition()
339 | .call(d.xAxis);
340 | };
341 |
342 | Plots.prototype.updatePrior = function (alpha, beta) {
343 | this.controlBeta = new BetaModel(alpha, beta);
344 | this.testBeta = new BetaModel(alpha, beta);
345 | };
346 |
347 | Plots.prototype.updatePosterior = function (testSuccesses, testFailures, controlSuccesses, controlFailures) {
348 | this.testBeta.update(testSuccesses, testFailures);
349 | this.controlBeta.update(controlSuccesses, controlFailures);
350 | };
351 |
352 |
353 | var getNumber = function (x, def) {
354 | return Number(x);
355 | };
356 |
357 | var getInputs = function () {
358 |
359 | var priorAlpha = getNumber(document.getElementById("priorAlpha").value, 10);
360 | var priorBeta = getNumber(document.getElementById("priorBeta").value, 10);
361 | var controlSuccesses = getNumber(document.getElementById("controlSuccesses").value, 10);
362 | var controlFailures = getNumber(document.getElementById("controlFailures").value, 10);
363 | var testSuccesses = getNumber(document.getElementById("testSuccesses").value, 10);
364 | var testFailures = getNumber(document.getElementById("testFailures").value, 10);
365 |
366 | return {
367 | "priorAlpha": priorAlpha,
368 | "priorBeta": priorBeta,
369 | "controlSuccesses": controlSuccesses,
370 | "controlFailures": controlFailures,
371 | "testSuccesses": testSuccesses,
372 | "testFailures": testFailures
373 | };
374 | };
375 |
376 | var initializePlots = function() {
377 |
378 | var inputs = getInputs();
379 | var plots = new Plots(inputs.priorAlpha, inputs.priorBeta);
380 | plots.drawPDF();
381 | plots.drawHistogram();
382 | window.plots = plots;
383 | };
384 |
385 | initializePlots();
386 |
387 | var updatePlots = function() {
388 |
389 | var inputs = getInputs();
390 | var plots = window.plots;
391 | plots.updatePrior(inputs.priorAlpha, inputs.priorBeta);
392 | plots.updatePosterior(inputs.testSuccesses,
393 | inputs.testFailures,
394 | inputs.controlSuccesses,
395 | inputs.controlFailures);
396 | plots.redrawPDF();
397 | plots.redrawHistogram();
398 |
399 | };
400 |
401 | var bindInputs = function() {
402 | document.getElementById("form").onsubmit = function(event) {
403 | event.preventDefault();
404 | updatePlots();
405 | };
406 | };
407 |
408 |
409 | bindInputs();
410 |
--------------------------------------------------------------------------------
/src/scripts/libs/jstat.js:
--------------------------------------------------------------------------------
1 | this.j$ = this.jStat = (function(Math, undefined) {
2 |
3 | // For quick reference.
4 | var concat = Array.prototype.concat;
5 | var slice = Array.prototype.slice;
6 | var toString = Object.prototype.toString;
7 |
8 | // Calculate correction for IEEE error
9 | // TODO: This calculation can be improved.
10 | function calcRdx(n, m) {
11 | var val = n > m ? n : m;
12 | return Math.pow(10,
13 | 17 - ~~(Math.log(((val > 0) ? val : -val)) * Math.LOG10E));
14 | }
15 |
16 |
17 | var isArray = Array.isArray || function isArray(arg) {
18 | return toString.call(arg) === '[object Array]';
19 | };
20 |
21 |
22 | function isFunction(arg) {
23 | return toString.call(arg) === '[object Function]';
24 | }
25 |
26 |
27 | function isNumber(arg) {
28 | return typeof arg === 'number' && arg === arg;
29 | }
30 |
31 |
32 | // Converts the jStat matrix to vector.
33 | function toVector(arr) {
34 | return concat.apply([], arr);
35 | }
36 |
37 |
38 | // The one and only jStat constructor.
39 | function jStat() {
40 | return new jStat._init(arguments);
41 | }
42 |
43 |
44 | // TODO: Remove after all references in src files have been removed.
45 | jStat.fn = jStat.prototype;
46 |
47 |
48 | // By separating the initializer from the constructor it's easier to handle
49 | // always returning a new instance whether "new" was used or not.
50 | jStat._init = function _init(args) {
51 | var i;
52 |
53 | // If first argument is an array, must be vector or matrix.
54 | if (isArray(args[0])) {
55 | // Check if matrix.
56 | if (isArray(args[0][0])) {
57 | // See if a mapping function was also passed.
58 | if (isFunction(args[1]))
59 | args[0] = jStat.map(args[0], args[1]);
60 | // Iterate over each is faster than this.push.apply(this, args[0].
61 | for (i = 0; i < args[0].length; i++)
62 | this[i] = args[0][i];
63 | this.length = args[0].length;
64 |
65 | // Otherwise must be a vector.
66 | } else {
67 | this[0] = isFunction(args[1]) ? jStat.map(args[0], args[1]) : args[0];
68 | this.length = 1;
69 | }
70 |
71 | // If first argument is number, assume creation of sequence.
72 | } else if (isNumber(args[0])) {
73 | this[0] = jStat.seq.apply(null, args);
74 | this.length = 1;
75 |
76 | // Handle case when jStat object is passed to jStat.
77 | } else if (args[0] instanceof jStat) {
78 | // Duplicate the object and pass it back.
79 | return jStat(args[0].toArray());
80 |
81 | // Unexpected argument value, return empty jStat object.
82 | // TODO: This is strange behavior. Shouldn't this throw or some such to let
83 | // the user know they had bad arguments?
84 | } else {
85 | this[0] = [];
86 | this.length = 1;
87 | }
88 |
89 | return this;
90 | };
91 | jStat._init.prototype = jStat.prototype;
92 | jStat._init.constructor = jStat;
93 |
94 |
95 | // Utility functions.
96 | // TODO: for internal use only?
97 | jStat.utils = {
98 | calcRdx: calcRdx,
99 | isArray: isArray,
100 | isFunction: isFunction,
101 | isNumber: isNumber,
102 | toVector: toVector
103 | };
104 |
105 |
106 | // Easily extend the jStat object.
107 | // TODO: is this seriously necessary?
108 | jStat.extend = function extend(obj) {
109 | var i, j;
110 |
111 | if (arguments.length === 1) {
112 | for (j in obj)
113 | jStat[j] = obj[j];
114 | return this;
115 | }
116 |
117 | for (i = 1; i < arguments.length; i++) {
118 | for (j in arguments[i])
119 | obj[j] = arguments[i][j];
120 | }
121 |
122 | return obj;
123 | };
124 |
125 |
126 | // Returns the number of rows in the matrix.
127 | jStat.rows = function rows(arr) {
128 | return arr.length || 1;
129 | };
130 |
131 |
132 | // Returns the number of columns in the matrix.
133 | jStat.cols = function cols(arr) {
134 | return arr[0].length || 1;
135 | };
136 |
137 |
138 | // Returns the dimensions of the object { rows: i, cols: j }
139 | jStat.dimensions = function dimensions(arr) {
140 | return {
141 | rows: jStat.rows(arr),
142 | cols: jStat.cols(arr)
143 | };
144 | };
145 |
146 |
147 | // Returns a specified row as a vector
148 | jStat.row = function row(arr, index) {
149 | return arr[index];
150 | };
151 |
152 |
153 | // Returns the specified column as a vector
154 | jStat.col = function cols(arr, index) {
155 | var column = new Array(arr.length);
156 | for (var i = 0; i < arr.length; i++)
157 | column[i] = [arr[i][index]];
158 | return column;
159 | };
160 |
161 |
162 | // Returns the diagonal of the matrix
163 | jStat.diag = function diag(arr) {
164 | var nrow = jStat.rows(arr);
165 | var res = new Array(nrow);
166 | for (var row = 0; row < nrow; row++)
167 | res[row] = [arr[row][row]];
168 | return res;
169 | };
170 |
171 |
172 | // Returns the anti-diagonal of the matrix
173 | jStat.antidiag = function antidiag(arr) {
174 | var nrow = jStat.rows(arr) - 1;
175 | var res = new Array(nrow);
176 | for (var i = 0; nrow >= 0; nrow--, i++)
177 | res[i] = [arr[i][nrow]];
178 | return res;
179 | };
180 |
181 | // Transpose a matrix or array.
182 | jStat.transpose = function transpose(arr) {
183 | var obj = [];
184 | var objArr, rows, cols, j, i;
185 |
186 | // Make sure arr is in matrix format.
187 | if (!isArray(arr[0]))
188 | arr = [arr];
189 |
190 | rows = arr.length;
191 | cols = arr[0].length;
192 |
193 | for (i = 0; i < cols; i++) {
194 | objArr = new Array(rows);
195 | for (j = 0; j < rows; j++)
196 | objArr[j] = arr[j][i];
197 | obj.push(objArr);
198 | }
199 |
200 | // If obj is vector, return only single array.
201 | return obj.length === 1 ? obj[0] : obj;
202 | };
203 |
204 |
205 | // Map a function to an array or array of arrays.
206 | // "toAlter" is an internal variable.
207 | jStat.map = function map(arr, func, toAlter) {
208 | var row, nrow, ncol, res, col;
209 |
210 | if (!isArray(arr[0]))
211 | arr = [arr];
212 |
213 | nrow = arr.length;
214 | ncol = arr[0].length;
215 | res = toAlter ? arr : new Array(nrow);
216 |
217 | for (row = 0; row < nrow; row++) {
218 | // if the row doesn't exist, create it
219 | if (!res[row])
220 | res[row] = new Array(ncol);
221 | for (col = 0; col < ncol; col++)
222 | res[row][col] = func(arr[row][col], row, col);
223 | }
224 |
225 | return res.length === 1 ? res[0] : res;
226 | };
227 |
228 |
229 | // Destructively alter an array.
230 | jStat.alter = function alter(arr, func) {
231 | return jStat.map(arr, func, true);
232 | };
233 |
234 |
235 | // Generate a rows x cols matrix according to the supplied function.
236 | jStat.create = function create(rows, cols, func) {
237 | var res = new Array(rows);
238 | var i, j;
239 |
240 | if (isFunction(cols)) {
241 | func = cols;
242 | cols = rows;
243 | }
244 |
245 | for (i = 0; i < rows; i++) {
246 | res[i] = new Array(cols);
247 | for (j = 0; j < cols; j++)
248 | res[i][j] = func(i, j);
249 | }
250 |
251 | return res;
252 | };
253 |
254 |
255 | function retZero() { return 0; }
256 |
257 |
258 | // Generate a rows x cols matrix of zeros.
259 | jStat.zeros = function zeros(rows, cols) {
260 | if (!isNumber(cols))
261 | cols = rows;
262 | return jStat.create(rows, cols, retZero);
263 | };
264 |
265 |
266 | function retOne() { return 1; }
267 |
268 |
269 | // Generate a rows x cols matrix of ones.
270 | jStat.ones = function ones(rows, cols) {
271 | if (!isNumber(cols))
272 | cols = rows;
273 | return jStat.create(rows, cols, retOne);
274 | };
275 |
276 |
277 | // Generate a rows x cols matrix of uniformly random numbers.
278 | jStat.rand = function rand(rows, cols) {
279 | if (!isNumber(cols))
280 | cols = rows;
281 | return jStat.create(rows, cols, Math.random);
282 | };
283 |
284 |
285 | function retIdent(i, j) { return i === j ? 1 : 0; }
286 |
287 |
288 | // Generate an identity matrix of size row x cols.
289 | jStat.identity = function identity(rows, cols) {
290 | if (!isNumber(cols))
291 | cols = rows;
292 | return jStat.create(rows, cols, retIdent);
293 | };
294 |
295 |
296 | // Tests whether a matrix is symmetric
297 | jStat.symmetric = function symmetric(arr) {
298 | var issymmetric = true;
299 | var size = arr.length;
300 | var row, col;
301 |
302 | if (arr.length !== arr[0].length)
303 | return false;
304 |
305 | for (row = 0; row < size; row++) {
306 | for (col = 0; col < size; col++)
307 | if (arr[col][row] !== arr[row][col])
308 | return false;
309 | }
310 |
311 | return true;
312 | };
313 |
314 |
315 | // Set all values to zero.
316 | jStat.clear = function clear(arr) {
317 | return jStat.alter(arr, retZero);
318 | };
319 |
320 |
321 | // Generate sequence.
322 | jStat.seq = function seq(min, max, length, func) {
323 | if (!isFunction(func))
324 | func = false;
325 |
326 | var arr = [];
327 | var hival = calcRdx(min, max);
328 | var step = (max * hival - min * hival) / ((length - 1) * hival);
329 | var current = min;
330 | var cnt;
331 |
332 | // Current is assigned using a technique to compensate for IEEE error.
333 | // TODO: Needs better implementation.
334 | for (cnt = 0;
335 | current <= max;
336 | cnt++, current = (min * hival + step * hival * cnt) / hival) {
337 | arr.push((func ? func(current, cnt) : current));
338 | }
339 |
340 | return arr;
341 | };
342 |
343 |
344 | // TODO: Go over this entire implementation. Seems a tragic waste of resources
345 | // doing all this work. Instead, and while ugly, use new Function() to generate
346 | // a custom function for each static method.
347 |
348 | // Quick reference.
349 | var jProto = jStat.prototype;
350 |
351 | // Default length.
352 | jProto.length = 0;
353 |
354 | // For internal use only.
355 | // TODO: Check if they're actually used, and if they are then rename them
356 | // to _*
357 | jProto.push = Array.prototype.push;
358 | jProto.sort = Array.prototype.sort;
359 | jProto.splice = Array.prototype.splice;
360 | jProto.slice = Array.prototype.slice;
361 |
362 |
363 | // Return a clean array.
364 | jProto.toArray = function toArray() {
365 | return this.length > 1 ? slice.call(this) : slice.call(this)[0];
366 | };
367 |
368 |
369 | // Map a function to a matrix or vector.
370 | jProto.map = function map(func, toAlter) {
371 | return jStat(jStat.map(this, func, toAlter));
372 | };
373 |
374 |
375 | // Destructively alter an array.
376 | jProto.alter = function alter(func) {
377 | jStat.alter(this, func);
378 | return this;
379 | };
380 |
381 |
382 | // Extend prototype with methods that have no argument.
383 | (function(funcs) {
384 | for (var i = 0; i < funcs.length; i++) (function(passfunc) {
385 | jProto[passfunc] = function(func) {
386 | var self = this,
387 | results;
388 | // Check for callback.
389 | if (func) {
390 | setTimeout(function() {
391 | func.call(self, jProto[passfunc].call(self));
392 | });
393 | return this;
394 | }
395 | results = jStat[passfunc](this);
396 | return isArray(results) ? jStat(results) : results;
397 | };
398 | })(funcs[i]);
399 | })('transpose clear symmetric rows cols dimensions diag antidiag'.split(' '));
400 |
401 |
402 | // Extend prototype with methods that have one argument.
403 | (function(funcs) {
404 | for (var i = 0; i < funcs.length; i++) (function(passfunc) {
405 | jProto[passfunc] = function(index, func) {
406 | var self = this;
407 | // check for callback
408 | if (func) {
409 | setTimeout(function() {
410 | func.call(self, jProto[passfunc].call(self, index));
411 | });
412 | return this;
413 | }
414 | return jStat(jStat[passfunc](this, index));
415 | };
416 | })(funcs[i]);
417 | })('row col'.split(' '));
418 |
419 |
420 | // Extend prototype with simple shortcut methods.
421 | (function(funcs) {
422 | for (var i = 0; i < funcs.length; i++) (function(passfunc) {
423 | jProto[passfunc] = new Function(
424 | 'return jStat(jStat.' + passfunc + '.apply(null, arguments));');
425 | })(funcs[i]);
426 | })('create zeros ones rand identity'.split(' '));
427 |
428 |
429 | // Exposing jStat.
430 | return jStat;
431 |
432 | }(Math));
433 | (function(jStat, Math) {
434 |
435 | var isFunction = jStat.utils.isFunction;
436 |
437 | // Ascending functions for sort
438 | function ascNum(a, b) { return a - b; }
439 |
440 | function clip(arg, min, max) {
441 | return Math.max(min, Math.min(arg, max));
442 | }
443 |
444 |
445 | // sum of an array
446 | jStat.sum = function sum(arr) {
447 | var sum = 0;
448 | var i = arr.length;
449 | var tmp;
450 | while (--i >= 0)
451 | sum += arr[i];
452 | return sum;
453 | };
454 |
455 |
456 | // sum squared
457 | jStat.sumsqrd = function sumsqrd(arr) {
458 | var sum = 0;
459 | var i = arr.length;
460 | while (--i >= 0)
461 | sum += arr[i] * arr[i];
462 | return sum;
463 | };
464 |
465 |
466 | // sum of squared errors of prediction (SSE)
467 | jStat.sumsqerr = function sumsqerr(arr) {
468 | var mean = jStat.mean(arr);
469 | var sum = 0;
470 | var i = arr.length;
471 | var tmp;
472 | while (--i >= 0) {
473 | tmp = arr[i] - mean;
474 | sum += tmp * tmp;
475 | }
476 | return sum;
477 | };
478 |
479 |
480 | // product of an array
481 | jStat.product = function product(arr) {
482 | var prod = 1;
483 | var i = arr.length;
484 | while (--i >= 0)
485 | prod *= arr[i];
486 | return prod;
487 | };
488 |
489 |
490 | // minimum value of an array
491 | jStat.min = function min(arr) {
492 | var low = arr[0];
493 | var i = 0;
494 | while (++i < arr.length)
495 | if (arr[i] < low)
496 | low = arr[i];
497 | return low;
498 | };
499 |
500 |
501 | // maximum value of an array
502 | jStat.max = function max(arr) {
503 | var high = arr[0];
504 | var i = 0;
505 | while (++i < arr.length)
506 | if (arr[i] > high)
507 | high = arr[i];
508 | return high;
509 | };
510 |
511 |
512 | // mean value of an array
513 | jStat.mean = function mean(arr) {
514 | return jStat.sum(arr) / arr.length;
515 | };
516 |
517 |
518 | // mean squared error (MSE)
519 | jStat.meansqerr = function meansqerr(arr) {
520 | return jStat.sumsqerr(arr) / arr.length;
521 | };
522 |
523 |
524 | // geometric mean of an array
525 | jStat.geomean = function geomean(arr) {
526 | return Math.pow(jStat.product(arr), 1 / arr.length);
527 | };
528 |
529 |
530 | // median of an array
531 | jStat.median = function median(arr) {
532 | var arrlen = arr.length;
533 | var _arr = arr.slice().sort(ascNum);
534 | // check if array is even or odd, then return the appropriate
535 | return !(arrlen & 1)
536 | ? (_arr[(arrlen / 2) - 1 ] + _arr[(arrlen / 2)]) / 2
537 | : _arr[(arrlen / 2) | 0 ];
538 | };
539 |
540 |
541 | // cumulative sum of an array
542 | jStat.cumsum = function cumsum(arr) {
543 | var len = arr.length;
544 | var sums = new Array(len);
545 | var i;
546 | sums[0] = arr[0];
547 | for (i = 1; i < len; i++)
548 | sums[i] = sums[i - 1] + arr[i];
549 | return sums;
550 | };
551 |
552 |
553 | // successive differences of a sequence
554 | jStat.diff = function diff(arr) {
555 | var diffs = [];
556 | var arrLen = arr.length;
557 | var i;
558 | for (i = 1; i < arrLen; i++)
559 | diffs.push(arr[i] - arr[i - 1]);
560 | return diffs;
561 | };
562 |
563 |
564 | // mode of an array
565 | // if there are multiple modes of an array, return all of them
566 | // is this the appropriate way of handling it?
567 | jStat.mode = function mode(arr) {
568 | var arrLen = arr.length;
569 | var _arr = arr.slice().sort(ascNum);
570 | var count = 1;
571 | var maxCount = 0;
572 | var numMaxCount = 0;
573 | var mode_arr = [];
574 | var i;
575 |
576 | for (i = 0; i < arrLen; i++) {
577 | if (_arr[i] === _arr[i + 1]) {
578 | count++;
579 | } else {
580 | if (count > maxCount) {
581 | mode_arr = [_arr[i]];
582 | maxCount = count;
583 | numMaxCount = 0;
584 | }
585 | // are there multiple max counts
586 | else if (count === maxCount) {
587 | mode_arr.push(_arr[i]);
588 | numMaxCount++;
589 | }
590 | // resetting count for new value in array
591 | count = 1;
592 | }
593 | }
594 |
595 | return numMaxCount === 0 ? mode_arr[0] : mode_arr;
596 | };
597 |
598 |
599 | // range of an array
600 | jStat.range = function range(arr) {
601 | return jStat.max(arr) - jStat.min(arr);
602 | };
603 |
604 | // variance of an array
605 | // flag indicates population vs sample
606 | jStat.variance = function variance(arr, flag) {
607 | return jStat.sumsqerr(arr) / (arr.length - (flag ? 1 : 0));
608 | };
609 |
610 |
611 | // standard deviation of an array
612 | // flag indicates population vs sample
613 | jStat.stdev = function stdev(arr, flag) {
614 | return Math.sqrt(jStat.variance(arr, flag));
615 | };
616 |
617 |
618 | // mean deviation (mean absolute deviation) of an array
619 | jStat.meandev = function meandev(arr) {
620 | var devSum = 0;
621 | var mean = jStat.mean(arr);
622 | var i;
623 | for (i = arr.length - 1; i >= 0; i--)
624 | devSum += Math.abs(arr[i] - mean);
625 | return devSum / arr.length;
626 | };
627 |
628 |
629 | // median deviation (median absolute deviation) of an array
630 | jStat.meddev = function meddev(arr) {
631 | var devSum = 0;
632 | var median = jStat.median(arr);
633 | var i;
634 | for (i = arr.length - 1; i >= 0; i--)
635 | devSum += Math.abs(arr[i] - median);
636 | return devSum / arr.length;
637 | };
638 |
639 |
640 | // coefficient of variation
641 | jStat.coeffvar = function coeffvar(arr) {
642 | return jStat.stdev(arr) / jStat.mean(arr);
643 | };
644 |
645 |
646 | // quartiles of an array
647 | jStat.quartiles = function quartiles(arr) {
648 | var arrlen = arr.length;
649 | var _arr = arr.slice().sort(ascNum);
650 | return [
651 | _arr[ Math.round((arrlen) / 4) - 1 ],
652 | _arr[ Math.round((arrlen) / 2) - 1 ],
653 | _arr[ Math.round((arrlen) * 3 / 4) - 1 ]
654 | ];
655 | };
656 |
657 |
658 | // Arbitary quantiles of an array. Direct port of the scipy.stats
659 | // implementation by Pierre GF Gerard-Marchant.
660 | jStat.quantiles = function quantiles(arr, quantilesArray, alphap, betap) {
661 | var sortedArray = arr.slice().sort(ascNum);
662 | var quantileVals = [quantilesArray.length];
663 | var n = arr.length;
664 | var i, p, m, aleph, k, gamma;
665 |
666 | if (typeof alphap === 'undefined')
667 | alphap = 3 / 8;
668 | if (typeof betap === 'undefined')
669 | betap = 3 / 8;
670 |
671 | for (i = 0; i < quantilesArray.length; i++) {
672 | p = quantilesArray[i];
673 | m = alphap + p * (1 - alphap - betap);
674 | aleph = n * p + m;
675 | k = Math.floor(clip(aleph, 1, n - 1));
676 | gamma = clip(aleph - k, 0, 1);
677 | quantileVals[i] = (1 - gamma) * sortedArray[k - 1] + gamma * sortedArray[k];
678 | }
679 |
680 | return quantileVals;
681 | };
682 |
683 | // The percentile rank of score in a given array. Returns the percentage
684 | // of all values in the input array that are less than (kind='strict') or
685 | // less or equal than (kind='weak') score. Default is weak.
686 | jStat.percentileOfScore = function percentileOfScore(arr, score, kind) {
687 | var counter = 0;
688 | var len = arr.length;
689 | var strict = false;
690 | var value, i;
691 |
692 | if (kind === 'strict')
693 | strict = true;
694 |
695 | for (i = 0; i < len; i++) {
696 | value = arr[i];
697 | if ((strict && value < score) ||
698 | (!strict && value <= score)) {
699 | counter++;
700 | }
701 | }
702 |
703 | return counter / len;
704 | };
705 |
706 | // covariance of two arrays
707 | jStat.covariance = function covariance(arr1, arr2) {
708 | var u = jStat.mean(arr1);
709 | var v = jStat.mean(arr2);
710 | var arr1Len = arr1.length;
711 | var sq_dev = new Array(arr1Len);
712 | var i;
713 |
714 | for (i = 0; i < arr1Len; i++)
715 | sq_dev[i] = (arr1[i] - u) * (arr2[i] - v);
716 |
717 | return jStat.sum(sq_dev) / (arr1Len - 1);
718 | };
719 |
720 |
721 | // (pearson's) population correlation coefficient, rho
722 | jStat.corrcoeff = function corrcoeff(arr1, arr2) {
723 | return jStat.covariance(arr1, arr2) /
724 | jStat.stdev(arr1, 1) /
725 | jStat.stdev(arr2, 1);
726 | };
727 |
728 |
729 | var jProto = jStat.prototype;
730 |
731 |
732 | // Extend jProto with method for calculating cumulative sums, as it does not
733 | // run again in case of true.
734 | // If a matrix is passed, automatically assume operation should be done on the
735 | // columns.
736 | jProto.cumsum = function(fullbool, func) {
737 | var arr = [];
738 | var i = 0;
739 | var tmpthis = this;
740 |
741 | // Assignment reassignation depending on how parameters were passed in.
742 | if (isFunction(fullbool)) {
743 | func = fullbool;
744 | fullbool = false;
745 | }
746 |
747 | // Check if a callback was passed with the function.
748 | if (func) {
749 | setTimeout(function() {
750 | func.call(tmpthis, jProto.cumsum.call(tmpthis, fullbool));
751 | });
752 | return this;
753 | }
754 |
755 | // Check if matrix and run calculations.
756 | if (this.length > 1) {
757 | tmpthis = fullbool === true ? this : this.transpose();
758 | for (; i < tmpthis.length; i++)
759 | arr[i] = jStat.cumsum(tmpthis[i]);
760 | return arr;
761 | }
762 |
763 | return jStat.cumsum(this[0], fullbool);
764 | };
765 |
766 |
767 | // Extend jProto with methods which don't require arguments and work on columns.
768 | (function(funcs) {
769 | for (var i = 0; i < funcs.length; i++) (function(passfunc) {
770 | // If a matrix is passed, automatically assume operation should be done on
771 | // the columns.
772 | jProto[passfunc] = function(fullbool, func) {
773 | var arr = [];
774 | var i = 0;
775 | var tmpthis = this;
776 | // Assignment reassignation depending on how parameters were passed in.
777 | if (isFunction(fullbool)) {
778 | func = fullbool;
779 | fullbool = false;
780 | }
781 | // Check if a callback was passed with the function.
782 | if (func) {
783 | setTimeout(function() {
784 | func.call(tmpthis, jProto[passfunc].call(tmpthis, fullbool));
785 | });
786 | return this;
787 | }
788 | // Check if matrix and run calculations.
789 | if (this.length > 1) {
790 | tmpthis = fullbool === true ? this : this.transpose();
791 | for (; i < tmpthis.length; i++)
792 | arr[i] = jStat[passfunc](tmpthis[i]);
793 | return fullbool === true
794 | ? jStat[passfunc](jStat.utils.toVector(arr))
795 | : arr;
796 | }
797 | // Pass fullbool if only vector, not a matrix. for variance and stdev.
798 | return jStat[passfunc](this[0], fullbool);
799 | };
800 | })(funcs[i]);
801 | })(('sum sumsqrd sumsqerr product min max mean meansqerr geomean median diff ' +
802 | 'mode range variance stdev meandev meddev coeffvar quartiles').split(' '));
803 |
804 |
805 | // Extend jProto with functions that take arguments. Operations on matrices are
806 | // done on columns.
807 | (function(funcs) {
808 | for (var i = 0; i < funcs.length; i++) (function(passfunc) {
809 | jProto[passfunc] = function() {
810 | var arr = [];
811 | var i = 0;
812 | var tmpthis = this;
813 | var args = Array.prototype.slice.call(arguments);
814 |
815 | // If the last argument is a function, we assume it's a callback; we
816 | // strip the callback out and call the function again.
817 | if (isFunction(args[args.length - 1])) {
818 | var callbackFunction = args[args.length - 1];
819 | var argsToPass = args.slice(0, args.length - 1);
820 |
821 | setTimeout(function() {
822 | callbackFunction.call(tmpthis,
823 | jProto[passfunc].apply(tmpthis, argsToPass));
824 | });
825 | return this;
826 |
827 | // Otherwise we curry the function args and call normally.
828 | } else {
829 | var callbackFunction = undefined;
830 | var curriedFunction = function curriedFunction(vector) {
831 | return jStat[passfunc].apply(tmpthis, [vector].concat(args));
832 | }
833 | }
834 |
835 | // If this is a matrix, run column-by-column.
836 | if (this.length > 1) {
837 | tmpthis = tmpthis.transpose();
838 | for (; i < tmpthis.length; i++)
839 | arr[i] = curriedFunction(tmpthis[i]);
840 | return arr;
841 | }
842 |
843 | // Otherwise run on the vector.
844 | return curriedFunction(this[0]);
845 | };
846 | })(funcs[i]);
847 | })('quantiles percentileOfScore'.split(' '));
848 |
849 | }(this.jStat, Math));
850 | // Special functions //
851 | (function(jStat, Math) {
852 |
853 | // Log-gamma function
854 | jStat.gammaln = function gammaln(x) {
855 | var j = 0;
856 | var cof = [
857 | 76.18009172947146, -86.50532032941677, 24.01409824083091,
858 | -1.231739572450155, 0.1208650973866179e-2, -0.5395239384953e-5
859 | ];
860 | var ser = 1.000000000190015;
861 | var xx, y, tmp;
862 | tmp = (y = xx = x) + 5.5;
863 | tmp -= (xx + 0.5) * Math.log(tmp);
864 | for (; j < 6; j++)
865 | ser += cof[j] / ++y;
866 | return Math.log(2.5066282746310005 * ser / xx) - tmp;
867 | };
868 |
869 |
870 | // gamma of x
871 | jStat.gammafn = function gammafn(x) {
872 | var p = [-1.716185138865495, 24.76565080557592, -379.80425647094563,
873 | 629.3311553128184, 866.9662027904133, -31451.272968848367,
874 | -36144.413418691176, 66456.14382024054
875 | ];
876 | var q = [-30.8402300119739, 315.35062697960416, -1015.1563674902192,
877 | -3107.771671572311, 22538.118420980151, 4755.8462775278811,
878 | -134659.9598649693, -115132.2596755535];
879 | var fact = false;
880 | var n = 0;
881 | var xden = 0;
882 | var xnum = 0;
883 | var y = x;
884 | var i, z, yi, res, sum, ysq;
885 | if (y <= 0) {
886 | res = y % 1 + 3.6e-16;
887 | if (res) {
888 | fact = (!(y & 1) ? 1 : -1) * Math.PI / Math.sin(Math.PI * res);
889 | y = 1 - y;
890 | } else {
891 | return Infinity;
892 | }
893 | }
894 | yi = y;
895 | if (y < 1) {
896 | z = y++;
897 | } else {
898 | z = (y -= n = (y | 0) - 1) - 1;
899 | }
900 | for (i = 0; i < 8; ++i) {
901 | xnum = (xnum + p[i]) * z;
902 | xden = xden * z + q[i];
903 | }
904 | res = xnum / xden + 1;
905 | if (yi < y) {
906 | res /= yi;
907 | } else if (yi > y) {
908 | for (i = 0; i < n; ++i) {
909 | res *= y;
910 | y++;
911 | }
912 | }
913 | if (fact) {
914 | res = fact / res;
915 | }
916 | return res;
917 | };
918 |
919 |
920 | // lower incomplete gamma function P(a,x)
921 | jStat.gammap = function gammap(a, x) {
922 | var aln = jStat.gammaln(a);
923 | var ap = a;
924 | var sum = 1 / a;
925 | var del = sum;
926 | var b = x + 1 - a;
927 | var c = 1 / 1.0e-30;
928 | var d = 1 / b;
929 | var h = d;
930 | var i = 1;
931 | // calculate maximum number of itterations required for a
932 | var ITMAX = -~(Math.log((a >= 1) ? a : 1 / a) * 8.5 + a * 0.4 + 17);
933 | var an, endval;
934 |
935 | if (x < 0 || a <= 0) {
936 | return NaN;
937 | } else if (x < a + 1) {
938 | for (; i <= ITMAX; i++) {
939 | sum += del *= x / ++ap;
940 | }
941 | return sum * Math.exp(-x + a * Math.log(x) - (aln));
942 | }
943 |
944 | for (; i <= ITMAX; i++) {
945 | an = -i * (i - a);
946 | b += 2;
947 | d = an * d + b;
948 | c = b + an / c;
949 | d = 1 / d;
950 | h *= d * c;
951 | }
952 |
953 | return 1 - h * Math.exp(-x + a * Math.log(x) - (aln));
954 | };
955 |
956 |
957 | // natural log factorial of n
958 | jStat.factorialln = function factorialln(n) {
959 | return n < 0 ? NaN : jStat.gammaln(n + 1);
960 | };
961 |
962 | // factorial of n
963 | jStat.factorial = function factorial(n) {
964 | return n < 0 ? NaN : jStat.gammafn(n + 1);
965 | };
966 |
967 | // combinations of n, m
968 | jStat.combination = function combination(n, m) {
969 | // make sure n or m don't exceed the upper limit of usable values
970 | return (n > 170 || m > 170)
971 | ? Math.exp(jStat.combinationln(n, m))
972 | : (jStat.factorial(n) / jStat.factorial(m)) / jStat.factorial(n - m);
973 | };
974 |
975 |
976 | jStat.combinationln = function combinationln(n, m){
977 | return jStat.factorialln(n) - jStat.factorialln(m) - jStat.factorialln(n - m);
978 | };
979 |
980 |
981 | // permutations of n, m
982 | jStat.permutation = function permutation(n, m) {
983 | return jStat.factorial(n) / jStat.factorial(n - m);
984 | };
985 |
986 |
987 | // beta function
988 | jStat.betafn = function betafn(x, y) {
989 | // ensure arguments are positive
990 | if (x <= 0 || y <= 0)
991 | return undefined;
992 | // make sure x + y doesn't exceed the upper limit of usable values
993 | return (x + y > 170)
994 | ? Math.exp(jStat.betaln(x, y))
995 | : jStat.gammafn(x) * jStat.gammafn(y) / jStat.gammafn(x + y);
996 | };
997 |
998 |
999 | // natural logarithm of beta function
1000 | jStat.betaln = function betaln(x, y) {
1001 | return jStat.gammaln(x) + jStat.gammaln(y) - jStat.gammaln(x + y);
1002 | };
1003 |
1004 |
1005 | // Evaluates the continued fraction for incomplete beta function by modified
1006 | // Lentz's method.
1007 | jStat.betacf = function betacf(x, a, b) {
1008 | var fpmin = 1e-30;
1009 | var m = 1;
1010 | var qab = a + b;
1011 | var qap = a + 1;
1012 | var qam = a - 1;
1013 | var c = 1;
1014 | var d = 1 - qab * x / qap;
1015 | var m2, aa, del, h;
1016 |
1017 | // These q's will be used in factors that occur in the coefficients
1018 | if (Math.abs(d) < fpmin)
1019 | d = fpmin;
1020 | d = 1 / d;
1021 | h = d;
1022 |
1023 | for (; m <= 100; m++) {
1024 | m2 = 2 * m;
1025 | aa = m * (b - m) * x / ((qam + m2) * (a + m2));
1026 | // One step (the even one) of the recurrence
1027 | d = 1 + aa * d;
1028 | if (Math.abs(d) < fpmin)
1029 | d = fpmin;
1030 | c = 1 + aa / c;
1031 | if (Math.abs(c) < fpmin)
1032 | c = fpmin;
1033 | d = 1 / d;
1034 | h *= d * c;
1035 | aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
1036 | // Next step of the recurrence (the odd one)
1037 | d = 1 + aa * d;
1038 | if (Math.abs(d) < fpmin)
1039 | d = fpmin;
1040 | c = 1 + aa / c;
1041 | if (Math.abs(c) < fpmin)
1042 | c = fpmin;
1043 | d = 1 / d;
1044 | del = d * c;
1045 | h *= del;
1046 | if (Math.abs(del - 1.0) < 3e-7)
1047 | break;
1048 | }
1049 |
1050 | return h;
1051 | };
1052 |
1053 |
1054 | // Returns the inverse incomplte gamma function
1055 | jStat.gammapinv = function gammapinv(p, a) {
1056 | var j = 0;
1057 | var a1 = a - 1;
1058 | var EPS = 1e-8;
1059 | var gln = jStat.gammaln(a);
1060 | var x, err, t, u, pp, lna1, afac;
1061 |
1062 | if (p >= 1)
1063 | return Math.max(100, a + 100 * Math.sqrt(a));
1064 | if (p <= 0)
1065 | return 0;
1066 | if (a > 1) {
1067 | lna1 = Math.log(a1);
1068 | afac = Math.exp(a1 * (lna1 - 1) - gln);
1069 | pp = (p < 0.5) ? p : 1 - p;
1070 | t = Math.sqrt(-2 * Math.log(pp));
1071 | x = (2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t;
1072 | if (p < 0.5)
1073 | x = -x;
1074 | x = Math.max(1e-3,
1075 | a * Math.pow(1 - 1 / (9 * a) - x / (3 * Math.sqrt(a)), 3));
1076 | } else {
1077 | t = 1 - a * (0.253 + a * 0.12);
1078 | if (p < t)
1079 | x = Math.pow(p / t, 1 / a);
1080 | else
1081 | x = 1 - Math.log(1 - (p - t) / (1 - t));
1082 | }
1083 |
1084 | for(; j < 12; j++) {
1085 | if (x <= 0)
1086 | return 0;
1087 | err = jStat.gammap(a, x) - p;
1088 | if (a > 1)
1089 | t = afac * Math.exp(-(x - a1) + a1 * (Math.log(x) - lna1));
1090 | else
1091 | t = Math.exp(-x + a1 * Math.log(x) - gln);
1092 | u = err / t;
1093 | x -= (t = u / (1 - 0.5 * Math.min(1, u * ((a - 1) / x - 1))));
1094 | if (x <= 0)
1095 | x = 0.5 * (x + t);
1096 | if (Math.abs(t) < EPS * x)
1097 | break;
1098 | }
1099 |
1100 | return x;
1101 | };
1102 |
1103 |
1104 | // Returns the error function erf(x)
1105 | jStat.erf = function erf(x) {
1106 | var cof = [-1.3026537197817094, 6.4196979235649026e-1, 1.9476473204185836e-2,
1107 | -9.561514786808631e-3, -9.46595344482036e-4, 3.66839497852761e-4,
1108 | 4.2523324806907e-5, -2.0278578112534e-5, -1.624290004647e-6,
1109 | 1.303655835580e-6, 1.5626441722e-8, -8.5238095915e-8,
1110 | 6.529054439e-9, 5.059343495e-9, -9.91364156e-10,
1111 | -2.27365122e-10, 9.6467911e-11, 2.394038e-12,
1112 | -6.886027e-12, 8.94487e-13, 3.13092e-13,
1113 | -1.12708e-13, 3.81e-16, 7.106e-15,
1114 | -1.523e-15, -9.4e-17, 1.21e-16,
1115 | -2.8e-17];
1116 | var j = cof.length - 1;
1117 | var isneg = false;
1118 | var d = 0;
1119 | var dd = 0;
1120 | var t, ty, tmp, res;
1121 |
1122 | if (x < 0) {
1123 | x = -x;
1124 | isneg = true;
1125 | }
1126 |
1127 | t = 2 / (2 + x);
1128 | ty = 4 * t - 2;
1129 |
1130 | for(; j > 0; j--) {
1131 | tmp = d;
1132 | d = ty * d - dd + cof[j];
1133 | dd = tmp;
1134 | }
1135 |
1136 | res = t * Math.exp(-x * x + 0.5 * (cof[0] + ty * d) - dd);
1137 | return isneg ? res - 1 : 1 - res;
1138 | };
1139 |
1140 |
1141 | // Returns the complmentary error function erfc(x)
1142 | jStat.erfc = function erfc(x) {
1143 | return 1 - jStat.erf(x);
1144 | };
1145 |
1146 |
1147 | // Returns the inverse of the complementary error function
1148 | jStat.erfcinv = function erfcinv(p) {
1149 | var j = 0;
1150 | var x, err, t, pp;
1151 | if (p >= 2)
1152 | return -100;
1153 | if (p <= 0)
1154 | return 100;
1155 | pp = (p < 1) ? p : 2 - p;
1156 | t = Math.sqrt(-2 * Math.log(pp / 2));
1157 | x = -0.70711 * ((2.30753 + t * 0.27061) /
1158 | (1 + t * (0.99229 + t * 0.04481)) - t);
1159 | for (; j < 2; j++) {
1160 | err = jStat.erfc(x) - pp;
1161 | x += err / (1.12837916709551257 * Math.exp(-x * x) - x * err);
1162 | }
1163 | return (p < 1) ? x : -x;
1164 | };
1165 |
1166 |
1167 | // Returns the inverse of the incomplete beta function
1168 | jStat.ibetainv = function ibetainv(p, a, b) {
1169 | var EPS = 1e-8;
1170 | var a1 = a - 1;
1171 | var b1 = b - 1;
1172 | var j = 0;
1173 | var lna, lnb, pp, t, u, err, x, al, h, w, afac;
1174 | if (p <= 0)
1175 | return 0;
1176 | if (p >= 1)
1177 | return 1;
1178 | if (a >= 1 && b >= 1) {
1179 | pp = (p < 0.5) ? p : 1 - p;
1180 | t = Math.sqrt(-2 * Math.log(pp));
1181 | x = (2.30753 + t * 0.27061) / (1 + t* (0.99229 + t * 0.04481)) - t;
1182 | if (p < 0.5)
1183 | x = -x;
1184 | al = (x * x - 3) / 6;
1185 | h = 2 / (1 / (2 * a - 1) + 1 / (2 * b - 1));
1186 | w = (x * Math.sqrt(al + h) / h) - (1 / (2 * b - 1) - 1 / (2 * a - 1)) *
1187 | (al + 5 / 6 - 2 / (3 * h));
1188 | x = a / (a + b * Math.exp(2 * w));
1189 | } else {
1190 | lna = Math.log(a / (a + b));
1191 | lnb = Math.log(b / (a + b));
1192 | t = Math.exp(a * lna) / a;
1193 | u = Math.exp(b * lnb) / b;
1194 | w = t + u;
1195 | if (p < t / w)
1196 | x = Math.pow(a * w * p, 1 / a);
1197 | else
1198 | x = 1 - Math.pow(b * w * (1 - p), 1 / b);
1199 | }
1200 | afac = -jStat.gammaln(a) - jStat.gammaln(b) + jStat.gammaln(a + b);
1201 | for(; j < 10; j++) {
1202 | if (x === 0 || x === 1)
1203 | return x;
1204 | err = jStat.ibeta(x, a, b) - p;
1205 | t = Math.exp(a1 * Math.log(x) + b1 * Math.log(1 - x) + afac);
1206 | u = err / t;
1207 | x -= (t = u / (1 - 0.5 * Math.min(1, u * (a1 / x - b1 / (1 - x)))));
1208 | if (x <= 0)
1209 | x = 0.5 * (x + t);
1210 | if (x >= 1)
1211 | x = 0.5 * (x + t + 1);
1212 | if (Math.abs(t) < EPS * x && j > 0)
1213 | break;
1214 | }
1215 | return x;
1216 | };
1217 |
1218 |
1219 | // Returns the incomplete beta function I_x(a,b)
1220 | jStat.ibeta = function ibeta(x, a, b) {
1221 | // Factors in front of the continued fraction.
1222 | var bt = (x === 0 || x === 1) ? 0 :
1223 | Math.exp(jStat.gammaln(a + b) - jStat.gammaln(a) -
1224 | jStat.gammaln(b) + a * Math.log(x) + b *
1225 | Math.log(1 - x));
1226 | if (x < 0 || x > 1)
1227 | return false;
1228 | if (x < (a + 1) / (a + b + 2))
1229 | // Use continued fraction directly.
1230 | return bt * jStat.betacf(x, a, b) / a;
1231 | // else use continued fraction after making the symmetry transformation.
1232 | return 1 - bt * jStat.betacf(1 - x, b, a) / b;
1233 | };
1234 |
1235 |
1236 | // Returns a normal deviate (mu=0, sigma=1).
1237 | // If n and m are specified it returns a object of normal deviates.
1238 | jStat.randn = function randn(n, m) {
1239 | var u, v, x, y, q, mat;
1240 | if (!m)
1241 | m = n;
1242 | if (n)
1243 | return jStat.create(n, m, function() { return jStat.randn(); });
1244 | do {
1245 | u = Math.random();
1246 | v = 1.7156 * (Math.random() - 0.5);
1247 | x = u - 0.449871;
1248 | y = Math.abs(v) + 0.386595;
1249 | q = x * x + y * (0.19600 * y - 0.25472 * x);
1250 | } while (q > 0.27597 && (q > 0.27846 || v * v > -4 * Math.log(u) * u * u));
1251 | return v / u;
1252 | };
1253 |
1254 |
1255 | // Returns a gamma deviate by the method of Marsaglia and Tsang.
1256 | jStat.randg = function randg(shape, n, m) {
1257 | var oalph = shape;
1258 | var a1, a2, u, v, x, mat;
1259 | if (!m)
1260 | m = n;
1261 | if (!shape)
1262 | shape = 1;
1263 | if (n) {
1264 | mat = jStat.zeros(n,m);
1265 | mat.alter(function() { return jStat.randg(shape); });
1266 | return mat;
1267 | }
1268 | if (shape < 1)
1269 | shape += 1;
1270 | a1 = shape - 1 / 3;
1271 | a2 = 1 / Math.sqrt(9 * a1);
1272 | do {
1273 | do {
1274 | x = jStat.randn();
1275 | v = 1 + a2 * x;
1276 | } while(v <= 0);
1277 | v = v * v * v;
1278 | u = Math.random();
1279 | } while(u > 1 - 0.331 * Math.pow(x, 4) &&
1280 | Math.log(u) > 0.5 * x*x + a1 * (1 - v + Math.log(v)));
1281 | // alpha > 1
1282 | if (shape == oalph)
1283 | return a1 * v;
1284 | // alpha < 1
1285 | do {
1286 | u = Math.random();
1287 | } while(u === 0);
1288 | return Math.pow(u, 1 / oalph) * a1 * v;
1289 | };
1290 |
1291 |
1292 | // making use of static methods on the instance
1293 | (function(funcs) {
1294 | for (var i = 0; i < funcs.length; i++) (function(passfunc) {
1295 | jStat.fn[passfunc] = function() {
1296 | return jStat(
1297 | jStat.map(this, function(value) { return jStat[passfunc](value); }));
1298 | }
1299 | })(funcs[i]);
1300 | })('gammaln gammafn factorial factorialln'.split(' '));
1301 |
1302 |
1303 | (function(funcs) {
1304 | for (var i = 0; i < funcs.length; i++) (function(passfunc) {
1305 | jStat.fn[passfunc] = function() {
1306 | return jStat(jStat[passfunc].apply(null, arguments));
1307 | };
1308 | })(funcs[i]);
1309 | })('randn'.split(' '));
1310 |
1311 | }(this.jStat, Math));
1312 | (function(jStat, Math) {
1313 |
1314 | // generate all distribution instance methods
1315 | (function(list) {
1316 | for (var i = 0; i < list.length; i++) (function(func) {
1317 | // distribution instance method
1318 | jStat[ func ] = function(a, b, c) {
1319 | if (!(this instanceof arguments.callee)) return new arguments.callee(a, b, c);
1320 | this._a = a;
1321 | this._b = b;
1322 | this._c = c;
1323 | return this;
1324 | };
1325 | // distribution method to be used on a jStat instance
1326 | jStat.fn[ func ] = function(a, b, c) {
1327 | var newthis = jStat[ func ](a, b, c);
1328 | newthis.data = this;
1329 | return newthis;
1330 | };
1331 | // sample instance method
1332 | jStat[ func ].prototype.sample = function(arr) {
1333 | var a = this._a,
1334 | b = this._b,
1335 | c = this._c;
1336 | if (arr)
1337 | return jStat.alter(arr, function() {
1338 | return jStat[ func ].sample(a, b, c);
1339 | });
1340 | else
1341 | return jStat[ func ].sample(a, b, c);
1342 | };
1343 | // generate the pdf, cdf and inv instance methods
1344 | (function(vals) {
1345 | for (var i = 0; i < vals.length; i++) (function(fnfunc) {
1346 | jStat[ func ].prototype[ fnfunc ] = function(x) {
1347 | var a = this._a,
1348 | b = this._b,
1349 | c = this._c;
1350 | if (!x) x = this.data;
1351 | if (typeof x !== 'number') {
1352 | return jStat.fn.map.call(x, function(x) {
1353 | return jStat[ func ][ fnfunc ](x, a, b, c);
1354 | });
1355 | }
1356 | return jStat[ func ][ fnfunc ](x, a, b, c);
1357 | };
1358 | })(vals[ i ]);
1359 | })('pdf cdf inv'.split(' '));
1360 | // generate the mean, median, mode and variance instance methods
1361 | (function(vals) {
1362 | for (var i = 0; i < vals.length; i++) (function(fnfunc) {
1363 | jStat[ func ].prototype[ fnfunc ] = function() {
1364 | return jStat[ func ][ fnfunc ](this._a, this._b, this._c);
1365 | };
1366 | })(vals[ i ]);
1367 | })('mean median mode variance'.split(' '));
1368 | })(list[ i ]);
1369 | })((
1370 | 'beta centralF cauchy chisquare exponential gamma invgamma kumaraswamy lognormal normal ' +
1371 | 'pareto studentt weibull uniform binomial negbin hypgeom poisson triangular'
1372 | ).split(' '));
1373 |
1374 |
1375 |
1376 | // extend beta function with static methods
1377 | jStat.extend(jStat.beta, {
1378 |
1379 | pdf : function(x, alpha, beta) {
1380 | // PDF is zero outside the support
1381 | if (x > 1 || x < 0) {
1382 | return 0;
1383 | };
1384 | // PDF is one for the uniform case
1385 | if (alpha == 1 && beta == 1) {
1386 | return 1;
1387 | };
1388 | return Math.exp((alpha - 1) * Math.log(x)
1389 | + (beta - 1) * Math.log(1-x)
1390 | - jStat.betaln(alpha, beta));
1391 | },
1392 |
1393 | cdf : function(x, alpha, beta) {
1394 | return (x > 1 || x < 0) ? (x > 1) * 1 : jStat.ibeta(x, alpha, beta);
1395 | },
1396 |
1397 | inv : function(x, alpha, beta) {
1398 | return jStat.ibetainv(x, alpha, beta);
1399 | },
1400 |
1401 | mean : function(alpha, beta) {
1402 | return alpha / (alpha + beta);
1403 | },
1404 |
1405 | median : function(alpha, beta) {
1406 | // TODO: implement beta median
1407 | },
1408 |
1409 | mode : function(alpha, beta) {
1410 | return (alpha * beta) / (Math.pow(alpha + beta, 2) * (alpha + beta + 1));
1411 | },
1412 |
1413 | // return a random sample
1414 | sample : function(alpha, beta) {
1415 | var u = jStat.randg(alpha);
1416 | return u / (u + jStat.randg(beta));
1417 | },
1418 |
1419 | variance : function(alpha, beta) {
1420 | return (alpha * beta) / (Math.pow(alpha + beta, 2) * (alpha + beta + 1));
1421 | }
1422 | });
1423 |
1424 | // extend F function with static methods
1425 | jStat.extend(jStat.centralF, {
1426 | pdf : function(x, df1, df2) {
1427 | return (x >= 0) ?
1428 | Math.sqrt((Math.pow(df1 * x, df1) * Math.pow(df2, df2)) / (Math.pow(df1 * x + df2, df1 + df2))) / (x * jStat.betafn(df1/2, df2/2)) : undefined;
1429 |
1430 | },
1431 |
1432 | cdf : function(x, df1, df2) {
1433 | return jStat.ibeta((df1 * x) / (df1 * x + df2), df1 / 2, df2 / 2);
1434 | },
1435 |
1436 | inv : function(x, df1, df2) {
1437 | return df2 / (df1 * (1 / jStat.ibetainv(x, df1 / 2, df2 / 2) - 1));
1438 | },
1439 |
1440 | mean : function(df1, df2) {
1441 | return (df2 > 2) ? df2 / (df2 - 2) : undefined;
1442 | },
1443 |
1444 | mode : function(df1, df2) {
1445 | return (df1 > 2) ? (df2 * (df1 - 2)) / (df1 * (df2 + 2)) : undefined;
1446 | },
1447 |
1448 | // return a random sample
1449 | sample : function(df1, df2) {
1450 | var x1 = jStat.randg(df1 / 2) * 2;
1451 | var x2 = jStat.randg(df2 / 2) * 2;
1452 | return (x1 / df1) / (x2 / df2);
1453 | },
1454 |
1455 | variance : function(df1, df2) {
1456 | return (df2 > 4) ? 2 * df2 * df2 * (df1 + df2 - 2) / (df1 * (df2 - 2) * (df2 - 2) * (df2 - 4)): undefined;
1457 | }
1458 | });
1459 |
1460 |
1461 | // extend cauchy function with static methods
1462 | jStat.extend(jStat.cauchy, {
1463 | pdf : function(x, local, scale) {
1464 | return (scale / (Math.pow(x - local, 2) + Math.pow(scale, 2))) / Math.PI;
1465 | },
1466 |
1467 | cdf : function(x, local, scale) {
1468 | return Math.atan((x - local) / scale) / Math.PI + 0.5;
1469 | },
1470 |
1471 | inv : function(p, local, scale) {
1472 | return local + scale * Math.tan(Math.PI * (p - 0.5));
1473 | },
1474 |
1475 | median: function(local, scale) {
1476 | return local;
1477 | },
1478 |
1479 | mode : function(local, scale) {
1480 | return local;
1481 | },
1482 |
1483 | sample : function(local, scale) {
1484 | return jStat.randn() * Math.sqrt(1 / (2 * jStat.randg(0.5))) * scale + local;
1485 | }
1486 | });
1487 |
1488 |
1489 |
1490 | // extend chisquare function with static methods
1491 | jStat.extend(jStat.chisquare, {
1492 | pdf : function(x, dof) {
1493 | return Math.exp((dof / 2 - 1) * Math.log(x) - x / 2 - (dof / 2) * Math.log(2) - jStat.gammaln(dof / 2));
1494 | },
1495 |
1496 | cdf : function(x, dof) {
1497 | return jStat.gammap(dof / 2, x / 2);
1498 | },
1499 |
1500 | inv : function(p, dof) {
1501 | return 2 * jStat.gammapinv(p, 0.5 * dof);
1502 | },
1503 |
1504 | mean : function(dof) {
1505 | return dof;
1506 | },
1507 |
1508 | //TODO: this is an approximation (is there a better way?)
1509 | median : function(dof) {
1510 | return dof * Math.pow(1 - (2 / (9 * dof)), 3);
1511 | },
1512 |
1513 | mode : function(dof) {
1514 | return (dof - 2 > 0) ? dof - 2 : 0;
1515 | },
1516 |
1517 | sample : function(dof) {
1518 | return jStat.randg(dof / 2) * 2;
1519 | },
1520 |
1521 | variance: function(dof) {
1522 | return 2 * dof;
1523 | }
1524 | });
1525 |
1526 |
1527 |
1528 | // extend exponential function with static methods
1529 | jStat.extend(jStat.exponential, {
1530 | pdf : function(x, rate) {
1531 | return x < 0 ? 0 : rate * Math.exp(-rate * x);
1532 | },
1533 |
1534 | cdf : function(x, rate) {
1535 | return x < 0 ? 0 : 1 - Math.exp(-rate * x);
1536 | },
1537 |
1538 | inv : function(p, rate) {
1539 | return -Math.log(1 - p) / rate;
1540 | },
1541 |
1542 | mean : function(rate) {
1543 | return 1 / rate;
1544 | },
1545 |
1546 | median : function (rate) {
1547 | return (1 / rate) * Math.log(2);
1548 | },
1549 |
1550 | mode : function(rate) {
1551 | return 0;
1552 | },
1553 |
1554 | sample : function(rate) {
1555 | return -1 / rate * Math.log(Math.random());
1556 | },
1557 |
1558 | variance : function(rate) {
1559 | return Math.pow(rate, -2);
1560 | }
1561 | });
1562 |
1563 |
1564 |
1565 | // extend gamma function with static methods
1566 | jStat.extend(jStat.gamma, {
1567 | pdf : function(x, shape, scale) {
1568 | return Math.exp((shape - 1) * Math.log(x) - x / scale - jStat.gammaln(shape) - shape * Math.log(scale));
1569 | },
1570 |
1571 | cdf : function(x, shape, scale) {
1572 | return jStat.gammap(shape, x / scale);
1573 | },
1574 |
1575 | inv : function(p, shape, scale) {
1576 | return jStat.gammapinv(p, shape) * scale;
1577 | },
1578 |
1579 | mean : function(shape, scale) {
1580 | return shape * scale;
1581 | },
1582 |
1583 | mode : function(shape, scale) {
1584 | if(shape > 1) return (shape - 1) * scale;
1585 | return undefined;
1586 | },
1587 |
1588 | sample : function(shape, scale) {
1589 | return jStat.randg(shape) * scale;
1590 | },
1591 |
1592 | variance: function(shape, scale) {
1593 | return shape * scale * scale;
1594 | }
1595 | });
1596 |
1597 | // extend inverse gamma function with static methods
1598 | jStat.extend(jStat.invgamma, {
1599 | pdf : function(x, shape, scale) {
1600 | return Math.exp(-(shape + 1) * Math.log(x) - scale/x - jStat.gammaln(shape) + shape * Math.log(scale));
1601 | },
1602 |
1603 | cdf : function(x, shape, scale) {
1604 | return 1 - jStat.gammap(shape, scale / x);
1605 | },
1606 |
1607 | inv : function(p, shape, scale) {
1608 | return scale / jStat.gammapinv(1 - p, shape);
1609 | },
1610 |
1611 | mean : function(shape, scale) {
1612 | return (shape > 1) ? scale / (shape - 1) : undefined;
1613 | },
1614 |
1615 | mode : function(shape, scale) {
1616 | return scale / (shape + 1);
1617 | },
1618 |
1619 | sample : function(shape, scale) {
1620 | return scale / jStat.randg(shape);
1621 | },
1622 |
1623 | variance: function(shape, scale) {
1624 | return (shape > 2) ? scale * scale / ((shape - 1) * (shape - 1) * (shape - 2)): undefined;
1625 | }
1626 | });
1627 |
1628 |
1629 | // extend kumaraswamy function with static methods
1630 | jStat.extend(jStat.kumaraswamy, {
1631 | pdf : function(x, alpha, beta) {
1632 | return Math.exp(Math.log(alpha) + Math.log(beta) + (alpha - 1) * Math.log(x) + (beta - 1) * Math.log(1 - Math.pow(x, alpha)));
1633 | },
1634 |
1635 | cdf : function(x, alpha, beta) {
1636 | return (1 - Math.pow(1 - Math.pow(x, alpha), beta));
1637 | },
1638 |
1639 | mean : function(alpha, beta) {
1640 | return (beta * jStat.gammafn(1 + 1 / alpha) * jStat.gammafn(beta)) / (jStat.gammafn(1 + 1 / alpha + beta));
1641 | },
1642 |
1643 | median : function(alpha, beta) {
1644 | return Math.pow(1 - Math.pow(2, -1 / beta), 1 / alpha);
1645 | },
1646 |
1647 | mode : function(alpha, beta) {
1648 | return (alpha >= 1 && beta >= 1 && (alpha !== 1 && beta !== 1)) ? Math.pow((alpha - 1) / (alpha * beta - 1), 1 / alpha) : undefined;
1649 | },
1650 |
1651 | variance: function(alpha, beta) {
1652 | // TODO: complete this
1653 | }
1654 | });
1655 |
1656 |
1657 |
1658 | // extend lognormal function with static methods
1659 | jStat.extend(jStat.lognormal, {
1660 | pdf : function(x, mu, sigma) {
1661 | return Math.exp(-Math.log(x) - 0.5 * Math.log(2 * Math.PI) - Math.log(sigma) - Math.pow(Math.log(x) - mu, 2) / (2 * sigma * sigma));
1662 | },
1663 |
1664 | cdf : function(x, mu, sigma) {
1665 | return 0.5 + (0.5 * jStat.erf((Math.log(x) - mu) / Math.sqrt(2 * sigma * sigma)));
1666 | },
1667 |
1668 | inv : function(p, mu, sigma) {
1669 | return Math.exp(-1.41421356237309505 * sigma * jStat.erfcinv(2 * p) + mu);
1670 | },
1671 |
1672 | mean : function(mu, sigma) {
1673 | return Math.exp(mu + sigma * sigma / 2);
1674 | },
1675 |
1676 | median : function(mu, sigma) {
1677 | return Math.exp(mu);
1678 | },
1679 |
1680 | mode : function(mu, sigma) {
1681 | return Math.exp(mu - sigma * sigma);
1682 | },
1683 |
1684 | sample : function(mu, sigma) {
1685 | return Math.exp(jStat.randn() * sigma + mu);
1686 | },
1687 |
1688 | variance : function(mu, sigma) {
1689 | return (Math.exp(sigma * sigma) - 1) * Math.exp(2 * mu + sigma * sigma);
1690 | }
1691 | });
1692 |
1693 |
1694 |
1695 | // extend normal function with static methods
1696 | jStat.extend(jStat.normal, {
1697 | pdf : function(x, mean, std) {
1698 | return Math.exp(-0.5 * Math.log(2 * Math.PI) - Math.log(std) - Math.pow(x - mean, 2) / (2 * std * std));
1699 | },
1700 |
1701 | cdf : function(x, mean, std) {
1702 | return 0.5 * (1 + jStat.erf((x - mean) / Math.sqrt(2 * std * std)));
1703 | },
1704 |
1705 | inv : function(p, mean, std) {
1706 | return -1.41421356237309505 * std * jStat.erfcinv(2 * p) + mean;
1707 | },
1708 |
1709 | mean : function(mean, std) {
1710 | return mean;
1711 | },
1712 |
1713 | median : function(mean, std) {
1714 | return mean;
1715 | },
1716 |
1717 | mode : function (mean, std) {
1718 | return mean;
1719 | },
1720 |
1721 | sample : function(mean, std) {
1722 | return jStat.randn() * std + mean;
1723 | },
1724 |
1725 | variance : function(mean, std) {
1726 | return std * std;
1727 | }
1728 | });
1729 |
1730 |
1731 |
1732 | // extend pareto function with static methods
1733 | jStat.extend(jStat.pareto, {
1734 | pdf : function(x, scale, shape) {
1735 | return (x > scale) ? (shape * Math.pow(scale, shape)) / Math.pow(x, shape + 1) : undefined;
1736 | },
1737 |
1738 | cdf : function(x, scale, shape) {
1739 | return 1 - Math.pow(scale / x, shape);
1740 | },
1741 |
1742 | mean : function(scale, shape) {
1743 | return (shape > 1) ? (shape * Math.pow(scale, shape)) / (shape - 1) : undefined;
1744 | },
1745 |
1746 | median : function(scale, shape) {
1747 | return scale * (shape * Math.SQRT2);
1748 | },
1749 |
1750 | mode : function(scale, shape) {
1751 | return scale;
1752 | },
1753 |
1754 | variance : function(scale, shape) {
1755 | return (shape > 2) ? (scale*scale * shape) / (Math.pow(shape - 1, 2) * (shape - 2)) : undefined;
1756 | }
1757 | });
1758 |
1759 |
1760 |
1761 | // extend studentt function with static methods
1762 | jStat.extend(jStat.studentt, {
1763 | pdf : function(x, dof) {
1764 | return (jStat.gammafn((dof + 1) / 2) / (Math.sqrt(dof * Math.PI) * jStat.gammafn(dof / 2))) * Math.pow(1 + ((x*x) / dof), -((dof + 1) / 2));
1765 | },
1766 |
1767 | cdf : function(x, dof) {
1768 | var dof2 = dof / 2;
1769 | return jStat.ibeta((x + Math.sqrt(x * x + dof)) / (2 * Math.sqrt(x * x + dof)), dof2, dof2);
1770 | },
1771 |
1772 | inv : function(p, dof) {
1773 | var x = jStat.ibetainv(2 * Math.min(p, 1 - p), 0.5 * dof, 0.5);
1774 | x = Math.sqrt(dof * (1 - x) / x);
1775 | return (p > 0) ? x : -x;
1776 | },
1777 |
1778 | mean : function(dof) {
1779 | return (dof > 1) ? 0 : undefined;
1780 | },
1781 |
1782 | median : function (dof) {
1783 | return 0;
1784 | },
1785 |
1786 | mode : function(dof) {
1787 | return 0;
1788 | },
1789 |
1790 | sample : function(dof) {
1791 | return jStat.randn() * Math.sqrt(dof / (2 * jStat.randg(dof / 2)));
1792 | },
1793 |
1794 | variance : function(dof) {
1795 | return (dof > 2) ? dof / (dof - 2) : (dof > 1) ? Infinity : undefined;
1796 | }
1797 | });
1798 |
1799 |
1800 |
1801 | // extend weibull function with static methods
1802 | jStat.extend(jStat.weibull, {
1803 | pdf : function(x, scale, shape) {
1804 | return x < 0 ? 0 : (shape / scale) * Math.pow((x / scale),(shape - 1)) * Math.exp(-(Math.pow((x / scale), shape)));
1805 | },
1806 |
1807 | cdf : function(x, scale, shape) {
1808 | return x < 0 ? 0 : 1 - Math.exp(-Math.pow((x / scale), shape));
1809 | },
1810 |
1811 | inv : function(p, scale, shape) {
1812 | return scale * Math.pow(-Math.log(1 - p), 1 / shape);
1813 | },
1814 |
1815 | mean : function(scale, shape) {
1816 | return scale * jStat.gammafn(1 + 1 / shape);
1817 | },
1818 |
1819 | median : function(scale, shape) {
1820 | return scale * Math.pow(Math.log(2), 1 / shape);
1821 | },
1822 |
1823 | mode : function(scale, shape) {
1824 | return (shape > 1) ? scale * Math.pow((shape - 1) / shape, 1 / shape) : undefined;
1825 | },
1826 |
1827 | sample : function(scale, shape) {
1828 | return scale * Math.pow(-Math.log(Math.random()), 1 / shape);
1829 | },
1830 |
1831 | variance : function(scale, shape) {
1832 | return scale * scale * jStat.gammafn(1 + 2 / shape) - Math.pow(this.mean(scale, shape), 2);
1833 | }
1834 | });
1835 |
1836 |
1837 |
1838 | // extend uniform function with static methods
1839 | jStat.extend(jStat.uniform, {
1840 | pdf : function(x, a, b) {
1841 | return (x < a || x > b) ? 0 : 1 / (b - a);
1842 | },
1843 |
1844 | cdf : function(x, a, b) {
1845 | if (x < a) {
1846 | return 0;
1847 | } else if (x < b) {
1848 | return (x - a) / (b - a);
1849 | }
1850 | return 1;
1851 | },
1852 |
1853 | mean : function(a, b) {
1854 | return 0.5 * (a + b);
1855 | },
1856 |
1857 | median : function(a, b) {
1858 | return jStat.mean(a, b);
1859 | },
1860 |
1861 | mode : function(a, b) {
1862 | // TODO: complete this
1863 | },
1864 |
1865 | sample : function(a, b) {
1866 | return (a / 2 + b / 2) + (b / 2 - a / 2) * (2 * Math.random() - 1);
1867 | },
1868 |
1869 | variance : function(a, b) {
1870 | return Math.pow(b - a, 2) / 12;
1871 | }
1872 | });
1873 |
1874 |
1875 |
1876 | // extend uniform function with static methods
1877 | jStat.extend(jStat.binomial, {
1878 | pdf : function(k, n, p) {
1879 | return (p === 0 || p === 1) ?
1880 | ((n * p) === k ? 1 : 0) :
1881 | jStat.combination(n, k) * Math.pow(p, k) * Math.pow(1 - p, n - k);
1882 | },
1883 |
1884 | cdf : function(x, n, p) {
1885 | var binomarr = [],
1886 | k = 0;
1887 | if (x < 0) {
1888 | return 0;
1889 | }
1890 | if (x < n) {
1891 | for (; k <= x; k++) {
1892 | binomarr[ k ] = jStat.binomial.pdf(k, n, p);
1893 | }
1894 | return jStat.sum(binomarr);
1895 | }
1896 | return 1;
1897 | }
1898 | });
1899 |
1900 |
1901 |
1902 | // extend uniform function with static methods
1903 | jStat.extend(jStat.negbin, {
1904 | pdf : function(k, r, p) {
1905 | return k !== k | 0
1906 | ? false
1907 | : k < 0
1908 | ? 0
1909 | : jStat.combination(k + r - 1, k) * Math.pow(1 - p, r) * Math.pow(p, k);
1910 | },
1911 |
1912 | cdf : function(x, r, p) {
1913 | var sum = 0,
1914 | k = 0;
1915 | if (x < 0) return 0;
1916 | for (; k <= x; k++) {
1917 | sum += jStat.negbin.pdf(k, r, p);
1918 | }
1919 | return sum;
1920 | }
1921 | });
1922 |
1923 |
1924 |
1925 | // extend uniform function with static methods
1926 | jStat.extend(jStat.hypgeom, {
1927 | pdf : function(k, N, m, n) {
1928 | return k !== k | 0
1929 | ? false
1930 | : (k < 0)
1931 | ? 0
1932 | : jStat.combination(m, k) * jStat.combination(N - m , n - k) / jStat.combination(N, n);
1933 | },
1934 |
1935 | cdf : function(x, N, m, n) {
1936 | var sum = 0,
1937 | k = 0;
1938 | if (x < 0) return 0;
1939 | for (; k <= x; k++) {
1940 | sum += jStat.hypgeom.pdf(k, N, m, n);
1941 | }
1942 | return sum;
1943 | }
1944 | });
1945 |
1946 |
1947 |
1948 | // extend uniform function with static methods
1949 | jStat.extend(jStat.poisson, {
1950 | pdf : function(k, l) {
1951 | return Math.pow(l, k) * Math.exp(-l) / jStat.factorial(k);
1952 | },
1953 |
1954 | cdf : function(x, l) {
1955 | var sumarr = [],
1956 | k = 0;
1957 | if (x < 0) return 0;
1958 | for (; k <= x; k++) {
1959 | sumarr.push(jStat.poisson.pdf(k, l));
1960 | }
1961 | return jStat.sum(sumarr);
1962 | },
1963 |
1964 | mean : function(l) {
1965 | return l;
1966 | },
1967 |
1968 | variance : function(l) {
1969 | return l;
1970 | },
1971 |
1972 | sample : function(l) {
1973 | var p = 1, k = 0, L = Math.exp(-l);
1974 | do {
1975 | k++;
1976 | p *= Math.random();
1977 | } while (p > L);
1978 | return k - 1;
1979 | }
1980 | });
1981 |
1982 | // extend triangular function with static methods
1983 | jStat.extend(jStat.triangular, {
1984 | pdf : function(x, a, b, c) {
1985 | return (b <= a || c < a || c > b)
1986 | ? undefined
1987 | : (x < a || x > b)
1988 | ? 0
1989 | : (x <= c)
1990 | ? (2 * (x - a)) / ((b - a) * (c - a))
1991 | : (2 * (b - x)) / ((b - a) * (b - c));
1992 | },
1993 |
1994 | cdf : function(x, a, b, c) {
1995 | if (b <= a || c < a || c > b)
1996 | return undefined;
1997 | if (x < a) {
1998 | return 0;
1999 | } else {
2000 | if (x <= c)
2001 | return Math.pow(x - a, 2) / ((b - a) * (c - a));
2002 | return 1 - Math.pow(b - x, 2) / ((b - a) * (b - c));
2003 | }
2004 | // never reach this
2005 | return 1;
2006 | },
2007 |
2008 | mean : function(a, b, c) {
2009 | return (a + b + c) / 3;
2010 | },
2011 |
2012 | median : function(a, b, c) {
2013 | if (c <= (a + b) / 2) {
2014 | return b - Math.sqrt((b - a) * (b - c)) / Math.sqrt(2);
2015 | } else if (c > (a + b) / 2) {
2016 | return a + Math.sqrt((b - a) * (c - a)) / Math.sqrt(2);
2017 | }
2018 | },
2019 |
2020 | mode : function(a, b, c) {
2021 | return c;
2022 | },
2023 |
2024 | sample : function(a, b, c) {
2025 | var u = Math.random();
2026 | return u < ((c - a) / (b - a)) ?
2027 | a + Math.sqrt(u * (b - a) * (c - a)) : b - Math.sqrt((1 - u) * (b - a) * (b - c));
2028 | },
2029 |
2030 | variance : function(a, b, c) {
2031 | return (a * a + b * b + c * c - a * b - a * c - b * c) / 18;
2032 | }
2033 | });
2034 |
2035 | }(this.jStat, Math));
2036 | /* Provides functions for the solution of linear system of equations, integration, extrapolation,
2037 | * interpolation, eigenvalue problems, differential equations and PCA analysis. */
2038 |
2039 | (function(jStat, Math) {
2040 |
2041 | var push = Array.prototype.push,
2042 | isArray = jStat.utils.isArray;
2043 |
2044 | jStat.extend({
2045 |
2046 | // add a vector/matrix to a vector/matrix or scalar
2047 | add : function(arr, arg) {
2048 | // check if arg is a vector or scalar
2049 | if (isArray(arg)) {
2050 | if (!isArray(arg[0])) arg = [ arg ];
2051 | return jStat.map(arr, function(value, row, col) { return value + arg[row][col]; });
2052 | }
2053 | return jStat.map(arr, function(value) { return value + arg; });
2054 | },
2055 |
2056 | // subtract a vector or scalar from the vector
2057 | subtract : function(arr, arg) {
2058 | // check if arg is a vector or scalar
2059 | if (isArray(arg)) {
2060 | if (!isArray(arg[0])) arg = [ arg ];
2061 | return jStat.map(arr, function(value, row, col) { return value - arg[row][col] || 0; });
2062 | }
2063 | return jStat.map(arr, function(value) { return value - arg; });
2064 | },
2065 |
2066 | // matrix division
2067 | divide : function(arr, arg) {
2068 | if (isArray(arg)) {
2069 | if (!isArray(arg[0])) arg = [ arg ];
2070 | return jStat.multiply(arr, jStat.inv(arg));
2071 | }
2072 | return jStat.map(arr, function(value) { return value / arg; });
2073 | },
2074 |
2075 | // matrix multiplication
2076 | multiply : function(arr, arg) {
2077 | var row, col, nrescols, sum,
2078 | nrow = arr.length,
2079 | ncol = arr[0].length,
2080 | res = jStat.zeros(nrow, nrescols = (isArray(arg)) ? arg[0].length : ncol),
2081 | rescols = 0;
2082 | if (isArray(arg)) {
2083 | for (; rescols < nrescols; rescols++) {
2084 | for (row = 0; row < nrow; row++) {
2085 | sum = 0;
2086 | for (col = 0; col < ncol; col++)
2087 | sum += arr[row][col] * arg[col][rescols];
2088 | res[row][rescols] = sum;
2089 | }
2090 | }
2091 | return (nrow === 1 && rescols === 1) ? res[0][0] : res;
2092 | }
2093 | return jStat.map(arr, function(value) { return value * arg; });
2094 | },
2095 |
2096 | // Returns the dot product of two matricies
2097 | dot : function(arr, arg) {
2098 | if (!isArray(arr[0])) arr = [ arr ];
2099 | if (!isArray(arg[0])) arg = [ arg ];
2100 | // convert column to row vector
2101 | var left = (arr[0].length === 1 && arr.length !== 1) ? jStat.transpose(arr) : arr,
2102 | right = (arg[0].length === 1 && arg.length !== 1) ? jStat.transpose(arg) : arg,
2103 | res = [],
2104 | row = 0,
2105 | nrow = left.length,
2106 | ncol = left[0].length,
2107 | sum, col;
2108 | for (; row < nrow; row++) {
2109 | res[row] = [];
2110 | sum = 0;
2111 | for (col = 0; col < ncol; col++)
2112 | sum += left[row][col] * right[row][col];
2113 | res[row] = sum;
2114 | }
2115 | return (res.length === 1) ? res[0] : res;
2116 | },
2117 |
2118 | // raise every element by a scalar
2119 | pow : function(arr, arg) {
2120 | return jStat.map(arr, function(value) { return Math.pow(value, arg); });
2121 | },
2122 |
2123 | // generate the absolute values of the vector
2124 | abs : function(arr) {
2125 | return jStat.map(arr, function(value) { return Math.abs(value); });
2126 | },
2127 |
2128 | // TODO: make compatible with matrices
2129 | // computes the p-norm of the vector
2130 | norm : function(arr, p) {
2131 | var nnorm = 0,
2132 | i = 0;
2133 | // check the p-value of the norm, and set for most common case
2134 | if (isNaN(p)) p = 2;
2135 | // check if multi-dimensional array, and make vector correction
2136 | if (isArray(arr[0])) arr = arr[0];
2137 | // vector norm
2138 | for (; i < arr.length; i++) {
2139 | nnorm += Math.pow(Math.abs(arr[i]), p);
2140 | }
2141 | return Math.pow(nnorm, 1 / p);
2142 | },
2143 |
2144 | // TODO: make compatible with matrices
2145 | // computes the angle between two vectors in rads
2146 | angle : function(arr, arg) {
2147 | return Math.acos(jStat.dot(arr, arg) / (jStat.norm(arr) * jStat.norm(arg)));
2148 | },
2149 |
2150 | // augment one matrix by another
2151 | aug : function(a, b) {
2152 | var newarr = a.slice(),
2153 | i = 0;
2154 | for (; i < newarr.length; i++) {
2155 | push.apply(newarr[i], b[i]);
2156 | }
2157 | return newarr;
2158 | },
2159 |
2160 | inv : function(a) {
2161 | var rows = a.length,
2162 | cols = a[0].length,
2163 | b = jStat.identity(rows, cols),
2164 | c = jStat.gauss_jordan(a, b),
2165 | obj = [],
2166 | i = 0,
2167 | j;
2168 | for (; i < rows; i++) {
2169 | obj[i] = [];
2170 | for (j = cols - 1; j < c[0].length; j++)
2171 | obj[i][j - cols] = c[i][j];
2172 | }
2173 | return obj;
2174 | },
2175 |
2176 | // calculate the determinant of a matrix
2177 | det : function(a) {
2178 | var alen = a.length,
2179 | alend = alen * 2,
2180 | vals = new Array(alend),
2181 | rowshift = alen - 1,
2182 | colshift = alend - 1,
2183 | mrow = rowshift - alen + 1,
2184 | mcol = colshift,
2185 | i = 0,
2186 | result = 0,
2187 | j;
2188 | // check for special 2x2 case
2189 | if (alen === 2) {
2190 | return a[0][0] * a[1][1] - a[0][1] * a[1][0];
2191 | }
2192 | for (; i < alend; i++) {
2193 | vals[i] = 1;
2194 | }
2195 | for (i = 0; i < alen; i++) {
2196 | for (j = 0; j < alen; j++) {
2197 | vals[(mrow < 0) ? mrow + alen : mrow ] *= a[i][j];
2198 | vals[(mcol < alen) ? mcol + alen : mcol ] *= a[i][j];
2199 | mrow++;
2200 | mcol--;
2201 | }
2202 | mrow = --rowshift - alen + 1;
2203 | mcol = --colshift;
2204 | }
2205 | for (i = 0; i < alen; i++) {
2206 | result += vals[i];
2207 | }
2208 | for (; i < alend; i++) {
2209 | result -= vals[i];
2210 | }
2211 | return result;
2212 | },
2213 |
2214 | gauss_elimination : function(a, b) {
2215 | var i = 0,
2216 | j = 0,
2217 | n = a.length,
2218 | m = a[0].length,
2219 | factor = 1,
2220 | sum = 0,
2221 | x = [],
2222 | maug, pivot, temp, k;
2223 | a = jStat.aug(a, b);
2224 | maug = a[0].length;
2225 | for(; i < n; i++) {
2226 | pivot = a[i][i];
2227 | j = i;
2228 | for (k = i + 1; k < m; k++) {
2229 | if (pivot < Math.abs(a[k][i])) {
2230 | pivot = a[k][i];
2231 | j = k;
2232 | }
2233 | }
2234 | if (j != i) {
2235 | for(k = 0; k < maug; k++) {
2236 | temp = a[i][k];
2237 | a[i][k] = a[j][k];
2238 | a[j][k] = temp;
2239 | }
2240 | }
2241 | for (j = i + 1; j < n; j++) {
2242 | factor = a[j][i] / a[i][i];
2243 | for(k = i; k < maug; k++) {
2244 | a[j][k] = a[j][k] - factor * a[i][k];
2245 | }
2246 | }
2247 | }
2248 | for (i = n - 1; i >= 0; i--) {
2249 | sum = 0;
2250 | for (j = i + 1; j<= n - 1; j++) {
2251 | sum = x[j] * a[i][j];
2252 | }
2253 | x[i] =(a[i][maug - 1] - sum) / a[i][i];
2254 | }
2255 | return x;
2256 | },
2257 |
2258 | gauss_jordan : function(a, b) {
2259 | var m = jStat.aug(a, b),
2260 | h = m.length,
2261 | w = m[0].length;
2262 | // find max pivot
2263 | for (var y = 0; y < h; y++) {
2264 | var maxrow = y;
2265 | for (var y2 = y+1; y2 < h; y2++) {
2266 | if (Math.abs(m[y2][y]) > Math.abs(m[maxrow][y]))
2267 | maxrow = y2;
2268 | }
2269 | var tmp = m[y];
2270 | m[y] = m[maxrow];
2271 | m[maxrow] = tmp
2272 | for (var y2 = y+1; y2 < h; y2++) {
2273 | c = m[y2][y] / m[y][y];
2274 | for (var x = y; x < w; x++) {
2275 | m[y2][x] -= m[y][x] * c;
2276 | }
2277 | }
2278 | }
2279 | // backsubstitute
2280 | for (var y = h-1; y >= 0; y--) {
2281 | c = m[y][y];
2282 | for (var y2 = 0; y2 < y; y2++) {
2283 | for (var x = w-1; x > y-1; x--) {
2284 | m[y2][x] -= m[y][x] * m[y2][y] / c;
2285 | }
2286 | }
2287 | m[y][y] /= c;
2288 | for (var x = h; x < w; x++) {
2289 | m[y][x] /= c;
2290 | }
2291 | }
2292 | return m;
2293 | },
2294 |
2295 | lu : function(a, b) {
2296 | //TODO
2297 | },
2298 |
2299 | cholesky : function(a, b) {
2300 | //TODO
2301 | },
2302 |
2303 | gauss_jacobi : function(a, b, x, r) {
2304 | var i = 0,
2305 | j = 0,
2306 | n = a.length,
2307 | l = [],
2308 | u = [],
2309 | d = [],
2310 | xv, c, h, xk;
2311 | for (; i < n; i++) {
2312 | l[i] = [];
2313 | u[i] = [];
2314 | d[i] = [];
2315 | for (j = 0; j < n; j++) {
2316 | if (i > j) {
2317 | l[i][j] = a[i][j];
2318 | u[i][j] = d[i][j] = 0;
2319 | } else if (i < j) {
2320 | u[i][j] = a[i][j];
2321 | l[i][j] = d[i][j] = 0;
2322 | } else {
2323 | d[i][j] = a[i][j];
2324 | l[i][j] = u[i][j] = 0;
2325 | }
2326 | }
2327 | }
2328 | h = jStat.multiply(jStat.multiply(jStat.inv(d), jStat.add(l, u)), -1);
2329 | c = jStat.multiply(jStat.inv(d), b);
2330 | xv = x;
2331 | xk = jStat.add(jStat.multiply(h, x), c);
2332 | i = 2;
2333 | while (Math.abs(jStat.norm(jStat.subtract(xk,xv))) > r) {
2334 | xv = xk;
2335 | xk = jStat.add(jStat.multiply(h, xv), c);
2336 | i++;
2337 | }
2338 | return xk;
2339 | },
2340 |
2341 | gauss_seidel : function(a, b, x, r) {
2342 | var i = 0,
2343 | n = a.length,
2344 | l = [],
2345 | u = [],
2346 | d = [],
2347 | j, xv, c, h, xk;
2348 | for (; i < n; i++) {
2349 | l[i] = [];
2350 | u[i] = [];
2351 | d[i] = [];
2352 | for (j = 0; j < n; j++) {
2353 | if (i > j) {
2354 | l[i][j] = a[i][j];
2355 | u[i][j] = d[i][j] = 0;
2356 | } else if (i < j) {
2357 | u[i][j] = a[i][j];
2358 | l[i][j] = d[i][j] = 0;
2359 | } else {
2360 | d[i][j] = a[i][j];
2361 | l[i][j] = u[i][j] = 0;
2362 | }
2363 | }
2364 | }
2365 | h = jStat.multiply(jStat.multiply(jStat.inv(jStat.add(d, l)), u), -1);
2366 | c = jStat.multiply(jStat.inv(jStat.add(d, l)), b);
2367 | xv = x;
2368 | xk = jStat.add(jStat.multiply(h, x), c);
2369 | i = 2;
2370 | while (Math.abs(jStat.norm(jStat.subtract(xk, xv))) > r) {
2371 | xv = xk;
2372 | xk = jStat.add(jStat.multiply(h, xv), c);
2373 | i = i + 1;
2374 | }
2375 | return xk;
2376 | },
2377 |
2378 | SOR : function(a, b, x, r, w) {
2379 | var i = 0,
2380 | n = a.length,
2381 | l = [],
2382 | u = [],
2383 | d = [],
2384 | j, xv, c, h, xk;
2385 | for (; i < n; i++) {
2386 | l[i] = [];
2387 | u[i] = [];
2388 | d[i] = [];
2389 | for (j = 0; j < n; j++) {
2390 | if (i > j) {
2391 | l[i][j] = a[i][j];
2392 | u[i][j] = d[i][j] = 0;
2393 | } else if (i < j) {
2394 | u[i][j] = a[i][j];
2395 | l[i][j] = d[i][j] = 0;
2396 | } else {
2397 | d[i][j] = a[i][j];
2398 | l[i][j] = u[i][j] = 0;
2399 | }
2400 | }
2401 | }
2402 | h = jStat.multiply(jStat.inv(jStat.add(d, jStat.multiply(l, w))), jStat.subtract(jStat.multiply(d, 1 - w), jStat.multiply(u, w)));
2403 | c = jStat.multiply(jStat.multiply(jStat.inv(jStat.add(d, jStat.multiply(l, w))), b), w);
2404 | xv = x;
2405 | xk = jStat.add(jStat.multiply(h, x), c);
2406 | i = 2;
2407 | while (Math.abs(jStat.norm(jStat.subtract(xk, xv))) > r) {
2408 | xv = xk;
2409 | xk = jStat.add(jStat.multiply(h, xv), c);
2410 | i++;
2411 | }
2412 | return xk;
2413 | },
2414 |
2415 | householder : function(a) {
2416 | var m = a.length,
2417 | n = a[0].length,
2418 | i = 0,
2419 | w = [],
2420 | p = [],
2421 | alpha, r, k, j, factor;
2422 | for (; i < m - 1; i++) {
2423 | alpha = 0;
2424 | for (j = i + 1; j < n; j++)
2425 | alpha += (a[j][i] * a[j][i]);
2426 | factor = (a[i + 1][i] > 0) ? -1 : 1;
2427 | alpha = factor * Math.sqrt(alpha);
2428 | r = Math.sqrt((((alpha * alpha) - a[i + 1][i] * alpha) / 2));
2429 | w = jStat.zeros(m, 1);
2430 | w[i + 1][0] = (a[i + 1][i] - alpha) / (2 * r);
2431 | for (k = i + 2; k < m; k++) w[k][0] = a[k][i] / (2 * r);
2432 | p = jStat.subtract(jStat.identity(m, n), jStat.multiply(jStat.multiply(w, jStat.transpose(w)), 2));
2433 | a = jStat.multiply(p, jStat.multiply(a, p));
2434 | }
2435 | return a;
2436 | },
2437 |
2438 | // TODO: not working properly.
2439 | QR : function(a, b) {
2440 | var m = a.length,
2441 | n = a[0].length,
2442 | i = 0,
2443 | w = [],
2444 | p = [],
2445 | x = [],
2446 | j, alpha, r, k, factor,sum;
2447 | for (; i < m - 1; i++) {
2448 | alpha = 0;
2449 | for (j = i + 1; j < n; j++)
2450 | alpha += (a[j][i] * a[j][i]);
2451 | factor = (a[i + 1][i] > 0) ? -1 : 1;
2452 | alpha = factor * Math.sqrt(alpha);
2453 | r = Math.sqrt((((alpha * alpha) - a[i + 1][i] * alpha) / 2));
2454 | w = jStat.zeros(m, 1);
2455 | w[i + 1][0] = (a[i + 1][i] - alpha) / (2 * r);
2456 | for (k = i + 2; k < m; k++) w[k][0] = a[k][i] / (2 * r);
2457 | p = jStat.subtract(jStat.identity(m, n), jStat.multiply(jStat.multiply(w, jStat.transpose(w)), 2));
2458 | a = jStat.multiply(p, a);
2459 | b = jStat.multiply(p, b);
2460 | }
2461 | for (i = m - 1; i >= 0; i--) {
2462 | sum = 0;
2463 | for (j = i + 1; j <= n - 1; j++)
2464 | sum = x[j] * a[i][j];
2465 | x[i] = b[i][0] / a[i][i];
2466 | }
2467 | return x;
2468 | },
2469 |
2470 | jacobi : function(a) {
2471 | var condition = 1,
2472 | count = 0,
2473 | n = a.length,
2474 | e = jStat.identity(n, n),
2475 | ev = [],
2476 | b, i, j, p, q, maxim, theta, s;
2477 | // condition === 1 only if tolerance is not reached
2478 | while (condition === 1) {
2479 | count++;
2480 | maxim = a[0][1];
2481 | p = 0;
2482 | q = 1;
2483 | for (i = 0; i < n; i++) {
2484 | for (j = 0; j < n; j++) {
2485 | if (i != j) {
2486 | if (maxim < Math.abs(a[i][j])) {
2487 | maxim = Math.abs(a[i][j]);
2488 | p = i;
2489 | q = j;
2490 | }
2491 | }
2492 | }
2493 | }
2494 | if (a[p][p] === a[q][q])
2495 | theta = (a[p][q] > 0) ? Math.PI / 4 : -Math.PI / 4;
2496 | else
2497 | theta = Math.atan(2 * a[p][q] / (a[p][p] - a[q][q])) / 2;
2498 | s = jStat.identity(n, n);
2499 | s[p][p] = Math.cos(theta);
2500 | s[p][q] = -Math.sin(theta);
2501 | s[q][p] = Math.sin(theta);
2502 | s[q][q] = Math.cos(theta);
2503 | // eigen vector matrix
2504 | e = jStat.multiply(e, s);
2505 | b = jStat.multiply(jStat.multiply(jStat.inv(s), a), s);
2506 | a = b;
2507 | condition = 0;
2508 | for (i = 1; i < n; i++) {
2509 | for (j = 1; j < n; j++) {
2510 | if (i != j && Math.abs(a[i][j]) > 0.001) {
2511 | condition = 1;
2512 | }
2513 | }
2514 | }
2515 | }
2516 | for(i = 0; i < n; i++) ev.push(a[i][i]);
2517 | //returns both the eigenvalue and eigenmatrix
2518 | return [e, ev];
2519 | },
2520 |
2521 | rungekutta : function(f, h, p, t_j, u_j, order) {
2522 | var k1, k2, u_j1, k3, k4;
2523 | if (order === 2) {
2524 | while (t_j <= p) {
2525 | k1 = h * f(t_j, u_j);
2526 | k2 = h * f(t_j + h, u_j + k1);
2527 | u_j1 = u_j + (k1 + k2) / 2;
2528 | u_j = u_j1;
2529 | t_j = t_j + h;
2530 | }
2531 | }
2532 | if (order === 4) {
2533 | while (t_j <= p) {
2534 | k1 = h * f(t_j, u_j);
2535 | k2 = h * f(t_j + h / 2, u_j + k1 / 2);
2536 | k3 = h * f(t_j + h / 2, u_j + k2 / 2);
2537 | k4 = h * f(t_j +h, u_j + k3);
2538 | u_j1 = u_j + (k1 + 2 * k2 + 2 * k3 + k4) / 6;
2539 | u_j = u_j1;
2540 | t_j = t_j + h;
2541 | }
2542 | }
2543 | return u_j;
2544 | },
2545 |
2546 | romberg : function(f, a, b, order) {
2547 | var i = 0,
2548 | h = (b - a) / 2,
2549 | x = [],
2550 | h1 = [],
2551 | g = [],
2552 | m, a1, j, k, I, d;
2553 | while (i < order / 2) {
2554 | I = f(a);
2555 | for (j = a, k = 0; j <= b; j = j + h, k++) x[k] = j;
2556 | m = x.length;
2557 | for (j = 1; j < m - 1; j++) {
2558 | I += (((j % 2) !== 0) ? 4 : 2) * f(x[j]);
2559 | }
2560 | I = (h / 3) * (I + f(b));
2561 | g[i] = I;
2562 | h /= 2;
2563 | i++;
2564 | }
2565 | a1 = g.length;
2566 | m = 1;
2567 | while (a1 !== 1) {
2568 | for (j = 0; j < a1 - 1; j++)
2569 | h1[j] = ((Math.pow(4, m)) * g[j + 1] - g[j]) / (Math.pow(4, m) - 1);
2570 | a1 = h1.length;
2571 | g = h1;
2572 | h1 = [];
2573 | m++;
2574 | }
2575 | return g;
2576 | },
2577 |
2578 | richardson : function(X, f, x, h) {
2579 | function pos(X, x) {
2580 | var i = 0,
2581 | n = X.length,
2582 | p;
2583 | for (; i < n; i++)
2584 | if (X[i] === x) p = i;
2585 | return p;
2586 | }
2587 | var n = X.length,
2588 | h_min = Math.abs(x - X[pos(X, x) + 1]),
2589 | i = 0,
2590 | g = [],
2591 | h1 = [],
2592 | y1, y2, m, a, j;
2593 | while (h >= h_min) {
2594 | y1 = pos(X, x + h);
2595 | y2 = pos(X, x);
2596 | g[i] = (f[y1] - 2 * f[y2] + f[2 * y2 - y1]) / (h * h);
2597 | h /= 2;
2598 | i++;
2599 | }
2600 | a = g.length;
2601 | m = 1;
2602 | while (a != 1) {
2603 | for (j = 0; j < a - 1; j++)
2604 | h1[j] = ((Math.pow(4, m)) * g[j + 1] - g[j]) / (Math.pow(4, m) - 1);
2605 | a = h1.length;
2606 | g = h1;
2607 | h1 = [];
2608 | m++;
2609 | }
2610 | return g;
2611 | },
2612 |
2613 | simpson : function(f, a, b, n) {
2614 | var h = (b - a) / n,
2615 | I = f(a),
2616 | x = [],
2617 | j = a,
2618 | k = 0,
2619 | i = 1,
2620 | m;
2621 | for (; j <= b; j = j + h, k++)
2622 | x[k] = j;
2623 | m = x.length;
2624 | for (; i < m - 1; i++) {
2625 | I += ((i % 2 !== 0) ? 4 : 2) * f(x[i]);
2626 | }
2627 | return (h / 3) * (I + f(b));
2628 | },
2629 |
2630 | hermite : function(X, F, dF, value) {
2631 | var n = X.length,
2632 | p = 0,
2633 | i = 0,
2634 | l = [],
2635 | dl = [],
2636 | A = [],
2637 | B = [],
2638 | j;
2639 | for (; i < n; i++) {
2640 | l[i] = 1;
2641 | for (j = 0; j < n; j++) {
2642 | if (i != j) l[i] *= (value - X[j]) / (X[i] - X[j]);
2643 | }
2644 | dl[i] = 0;
2645 | for (j = 0; j < n; j++) {
2646 | if (i != j) dl[i] += 1 / (X [i] - X[j]);
2647 | }
2648 | A[i] = (1 - 2 * (value - X[i]) * dl[i]) * (l[i] * l[i]);
2649 | B[i] = (value - X[i]) * (l[i] * l[i]);
2650 | p += (A[i] * F[i] + B[i] * dF[i]);
2651 | }
2652 | return p;
2653 | },
2654 |
2655 | lagrange : function(X, F, value) {
2656 | var p = 0,
2657 | i = 0,
2658 | j, l,
2659 | n = X.length;
2660 | for (; i < n; i++) {
2661 | l = F[i];
2662 | for (j = 0; j < n; j++) {
2663 | // calculating the lagrange polynomial L_i
2664 | if (i != j) l *= (value - X[j]) / (X[i] - X[j]);
2665 | }
2666 | // adding the lagrange polynomials found above
2667 | p += l;
2668 | }
2669 | return p;
2670 | },
2671 |
2672 | cubic_spline : function(X, F, value) {
2673 | var n = X.length,
2674 | i = 0, j,
2675 | A = [],
2676 | B = [],
2677 | alpha = [],
2678 | c = [],
2679 | h = [],
2680 | b = [],
2681 | d = [];
2682 | for (; i < n - 1; i++)
2683 | h[i] = X[i + 1] - X[i];
2684 | alpha[0] = 0;
2685 | for (i = 1; i < n - 1; i++)
2686 | alpha[i] = (3 / h[i]) * (F[i + 1] - F[i]) - (3 / h[i-1]) * (F[i] - F[i-1]);
2687 | for (i = 1; i < n - 1; i++) {
2688 | A[i] = [];
2689 | B[i] = [];
2690 | A[i][i-1] = h[i-1];
2691 | A[i][i] = 2 * (h[i - 1] + h[i]);
2692 | A[i][i+1] = h[i];
2693 | B[i][0] = alpha[i];
2694 | }
2695 | c = jStat.multiply(jStat.inv(A), B);
2696 | for (j = 0; j < n - 1; j++) {
2697 | b[j] = (F[j + 1] - F[j]) / h[j] - h[j] * (c[j + 1][0] + 2 * c[j][0]) / 3;
2698 | d[j] = (c[j + 1][0] - c[j][0]) / (3 * h[j]);
2699 | }
2700 | for (j = 0; j < n; j++) {
2701 | if (X[j] > value) break;
2702 | }
2703 | j -= 1;
2704 | return F[j] + (value - X[j]) * b[j] + jStat.sq(value-X[j]) * c[j] + (value - X[j]) * jStat.sq(value - X[j]) * d[j];
2705 | },
2706 |
2707 | gauss_quadrature : function() {
2708 | //TODO
2709 | },
2710 |
2711 | PCA : function(X) {
2712 | var m = X.length,
2713 | n = X[0].length,
2714 | flag = false,
2715 | i = 0,
2716 | j, temp1,
2717 | u = [],
2718 | D = [],
2719 | result = [],
2720 | temp2 = [],
2721 | Y = [],
2722 | Bt = [],
2723 | B = [],
2724 | C = [],
2725 | V = [],
2726 | Vt = [];
2727 | for (i = 0; i < m; i++) {
2728 | u[i] = jStat.sum(X[i]) / n;
2729 | }
2730 | for (i = 0; i < n; i++) {
2731 | B[i] = [];
2732 | for(j = 0; j < m; j++) {
2733 | B[i][j] = X[j][i] - u[j];
2734 | }
2735 | }
2736 | B = jStat.transpose(B);
2737 | for (i = 0; i < m; i++) {
2738 | C[i] = [];
2739 | for (j = 0; j < m; j++) {
2740 | C[i][j] = (jStat.dot([B[i]], [B[j]])) / (n - 1);
2741 | }
2742 | }
2743 | result = jStat.jacobi(C);
2744 | V = result[0];
2745 | D = result[1];
2746 | Vt = jStat.transpose(V);
2747 | for (i = 0; i < D.length; i++) {
2748 | for (j = i; j < D.length; j++) {
2749 | if(D[i] < D[j]) {
2750 | temp1 = D[i];
2751 | D[i] = D[j];
2752 | D[j] = temp1;
2753 | temp2 = Vt[i];
2754 | Vt[i] = Vt[j];
2755 | Vt[j] = temp2;
2756 | }
2757 | }
2758 | }
2759 | Bt = jStat.transpose(B);
2760 | for (i = 0; i < m; i++) {
2761 | Y[i] = [];
2762 | for (j = 0; j < Bt.length; j++) {
2763 | Y[i][j] = jStat.dot([Vt[i]], [Bt[j]]);
2764 | }
2765 | }
2766 | return [X, D, Vt, Y];
2767 | }
2768 | });
2769 |
2770 | // extend jStat.fn with methods that require one argument
2771 | (function(funcs) {
2772 | for (var i = 0; i < funcs.length; i++) (function(passfunc) {
2773 | jStat.fn[ passfunc ] = function(arg, func) {
2774 | var tmpthis = this;
2775 | // check for callback
2776 | if (func) {
2777 | setTimeout(function() {
2778 | func.call(tmpthis, jStat.fn[ passfunc ].call(tmpthis, arg));
2779 | }, 15);
2780 | return this;
2781 | }
2782 | return jStat(jStat[ passfunc ](this, arg));
2783 | };
2784 | }(funcs[i]));
2785 | }('add divide multiply subtract dot pow abs norm angle'.split(' ')));
2786 |
2787 | }(this.jStat, Math));
2788 | (function(jStat, Math) {
2789 |
2790 | var slice = [].slice,
2791 | isNumber = jStat.utils.isNumber;
2792 |
2793 | // flag==true denotes use of sample standard deviation
2794 | // Z Statistics
2795 | jStat.extend({
2796 | // 2 different parameter lists:
2797 | // (value, mean, sd)
2798 | // (value, array, flag)
2799 | zscore : function() {
2800 | var args = slice.call(arguments);
2801 | if (isNumber(args[1])) {
2802 | return (args[0] - args[1]) / args[2];
2803 | }
2804 | return (args[0] - jStat.mean(args[1])) / jStat.stdev(args[1], args[2]);
2805 | },
2806 |
2807 | // 3 different paramter lists:
2808 | // (value, mean, sd, sides)
2809 | // (zscore, sides)
2810 | // (value, array, sides, flag)
2811 | ztest : function() {
2812 | var args = slice.call(arguments);
2813 | if (args.length === 4) {
2814 | if(isNumber(args[1])) {
2815 | var z = jStat.zscore(args[0],args[1],args[2])
2816 | return (args[3] === 1) ?
2817 | (jStat.normal.cdf(-Math.abs(z),0,1)) :
2818 | (jStat.normal.cdf(-Math.abs(z),0,1)* 2);
2819 | }
2820 | var z = args[0]
2821 | return (args[2] === 1) ?
2822 | (jStat.normal.cdf(-Math.abs(z),0,1)) :
2823 | (jStat.normal.cdf(-Math.abs(z),0,1)*2);
2824 | }
2825 | var z = jStat.zscore(args[0],args[1],args[3])
2826 | return (args[1] === 1) ?
2827 | (jStat.normal.cdf(-Math.abs(z), 0, 1)) :
2828 | (jStat.normal.cdf(-Math.abs(z), 0, 1)*2);
2829 | }
2830 | });
2831 |
2832 | jStat.extend(jStat.fn, {
2833 | zscore : function(value, flag) {
2834 | return (value - this.mean()) / this.stdev(flag);
2835 | },
2836 |
2837 | ztest : function(value, sides, flag) {
2838 | var zscore = Math.abs(this.zscore(value, flag));
2839 | return (sides === 1) ?
2840 | (jStat.normal.cdf(-zscore, 0, 1)) :
2841 | (jStat.normal.cdf(-zscore, 0, 1) * 2);
2842 | }
2843 | });
2844 |
2845 | // T Statistics
2846 | jStat.extend({
2847 | // 2 parameter lists
2848 | // (value, mean, sd, n)
2849 | // (value, array)
2850 | tscore : function() {
2851 | var args = slice.call(arguments);
2852 | return (args.length === 4) ?
2853 | ((args[0] - args[1]) / (args[2] / Math.sqrt(args[3]))) :
2854 | ((args[0] - jStat.mean(args[1])) / (jStat.stdev(args[1], true) / Math.sqrt(args[1].length)));
2855 | },
2856 |
2857 | // 3 different paramter lists:
2858 | // (value, mean, sd, n, sides)
2859 | // (tscore, n, sides)
2860 | // (value, array, sides)
2861 | ttest : function() {
2862 | var args = slice.call(arguments);
2863 | var tscore;
2864 | if (args.length === 5) {
2865 | tscore = Math.abs(jStat.tscore(args[0], args[1], args[2], args[3]));
2866 | return (args[4] === 1) ?
2867 | (jStat.studentt.cdf(-tscore, args[3]-1)) :
2868 | (jStat.studentt.cdf(-tscore, args[3]-1)*2);
2869 | }
2870 | if (isNumber(args[1])) {
2871 | tscore = Math.abs(args[0])
2872 | return (args[2] == 1) ?
2873 | (jStat.studentt.cdf(-tscore, args[1]-1)) :
2874 | (jStat.studentt.cdf(-tscore, args[1]-1) * 2);
2875 | }
2876 | tscore = Math.abs(jStat.tscore(args[0], args[1]))
2877 | return (args[2] == 1) ?
2878 | (jStat.studentt.cdf(-tscore, args[1].length-1)) :
2879 | (jStat.studentt.cdf(-tscore, args[1].length-1) * 2);
2880 | }
2881 | });
2882 |
2883 | jStat.extend(jStat.fn, {
2884 | tscore : function(value) {
2885 | return (value - this.mean()) / (this.stdev(true) / Math.sqrt(this.cols()));
2886 | },
2887 |
2888 | ttest : function(value, sides) {
2889 | return (sides === 1) ?
2890 | (1 - jStat.studentt.cdf(Math.abs(this.tscore(value)), this.cols()-1)) :
2891 | (jStat.studentt.cdf(-Math.abs(this.tscore(value)), this.cols()-1)*2);
2892 | }
2893 | });
2894 |
2895 | // F Statistics
2896 | jStat.extend({
2897 | // Paramter list is as follows:
2898 | // (array1, array2, array3, ...)
2899 | // or it is an array of arrays
2900 | // array of arrays conversion
2901 | anovafscore : function() {
2902 | var args = slice.call(arguments),
2903 | expVar, sample, sampMean, sampSampMean, tmpargs, unexpVar, i, j;
2904 | if (args.length === 1) {
2905 | tmpargs = new Array(args[0].length);
2906 | for (i = 0; i < args[0].length; i++) {
2907 | tmpargs[i] = args[0][i];
2908 | }
2909 | args = tmpargs;
2910 | }
2911 | // 2 sample case
2912 | if (args.length === 2) {
2913 | return jStat.variance(args[0]) / jStat.variance(args[1]);
2914 | }
2915 | // Builds sample array
2916 | sample = new Array();
2917 | for (i = 0; i < args.length; i++) {
2918 | sample = sample.concat(args[i]);
2919 | }
2920 | sampMean = jStat.mean(sample);
2921 | // Computes the explained variance
2922 | expVar = 0;
2923 | for (i = 0; i < args.length; i++) {
2924 | expVar = expVar + args[i].length * Math.pow(jStat.mean(args[i]) - sampMean, 2);
2925 | }
2926 | expVar /= (args.length - 1);
2927 | // Computes unexplained variance
2928 | unexpVar = 0;
2929 | for (i = 0; i < args.length; i++) {
2930 | sampSampMean = jStat.mean(args[i]);
2931 | for (j = 0; j < args[i].length; j++) {
2932 | unexpVar += Math.pow(args[i][j] - sampSampMean, 2);
2933 | }
2934 | }
2935 | unexpVar /= (sample.length - args.length);
2936 | return expVar / unexpVar;
2937 | },
2938 |
2939 | // 2 different paramter setups
2940 | // (array1, array2, array3, ...)
2941 | // (anovafscore, df1, df2)
2942 | anovaftest : function() {
2943 | var args = slice.call(arguments),
2944 | df1, df2, n, i;
2945 | if (isNumber(args[0])) {
2946 | return 1 - jStat.centralF.cdf(args[0], args[1], args[2]);
2947 | }
2948 | anovafscore = jStat.anovafscore(args);
2949 | df1 = args.length - 1;
2950 | n = 0;
2951 | for (i = 0; i < args.length; i++) {
2952 | n = n + args[i].length;
2953 | }
2954 | df2 = n - df1 - 1;
2955 | return 1 - jStat.centralF.cdf(anovafscore, df1, df2);
2956 | },
2957 |
2958 | ftest : function(fscore, df1, df2) {
2959 | return 1 - jStat.centralF.cdf(fscore, df1, df2);
2960 | }
2961 | });
2962 |
2963 | jStat.extend(jStat.fn, {
2964 | anovafscore : function() {
2965 | return jStat.anovafscore(this.toArray());
2966 | },
2967 |
2968 | anovaftest: function() {
2969 | var n = 0,
2970 | i;
2971 | for (i = 0; i < this.length; i++) {
2972 | n = n + this[i].length;
2973 | }
2974 | return jStat.ftest(this.anovafscore(), this.length - 1, n - this.length);
2975 | }
2976 | });
2977 |
2978 | // Error Bounds
2979 | jStat.extend({
2980 | // 2 different parameter setups
2981 | // (value, alpha, sd, n)
2982 | // (value, alpha, array)
2983 | normalci : function() {
2984 | var args = slice.call(arguments),
2985 | ans = new Array(2),
2986 | change;
2987 | if (args.length === 4) {
2988 | change = Math.abs(jStat.normal.inv(args[1] / 2, 0, 1) * args[2] / Math.sqrt(args[3]));
2989 | } else {
2990 | change = Math.abs(jStat.normal.inv(args[1] / 2, 0, 1) * jStat.stdev(args[2]) / Math.sqrt(args[2].length));
2991 | }
2992 | ans[0] = args[0] - change;
2993 | ans[1] = args[0] + change;
2994 | return ans;
2995 | },
2996 |
2997 | // 2 different parameter setups
2998 | // (value, alpha, sd, n)
2999 | // (value, alpha, array)
3000 | tci : function() {
3001 | var args = slice.call(arguments),
3002 | ans = new Array(2),
3003 | change;
3004 | if (args.length === 4) {
3005 | change = Math.abs(jStat.studentt.inv(args[1] / 2, args[3] - 1) * args[2] / Math.sqrt(args[3]));
3006 | } else {
3007 | change = Math.abs(jStat.studentt.inv(args[1] / 2, args[2].length) * jStat.stdev(args[2], true) / Math.sqrt(args[2].length));
3008 | }
3009 | ans[0] = args[0] - change;
3010 | ans[1] = args[0] + change;
3011 | return ans;
3012 | },
3013 |
3014 | significant : function(pvalue, alpha) {
3015 | return pvalue < alpha;
3016 | }
3017 | });
3018 |
3019 | jStat.extend(jStat.fn, {
3020 | normalci : function(value, alpha) {
3021 | return jStat.normalci(value, alpha, this.toArray());
3022 | },
3023 |
3024 | tci : function(value, alpha) {
3025 | return jStat.tci(value, alpha, this.toArray());
3026 | }
3027 | });
3028 |
3029 | }(this.jStat, Math));
3030 |
--------------------------------------------------------------------------------