Add first part of linear regression, and fixed hash hack so it doesn't jump.
[dygraphs.git] / gallery / linear-regression.js
1 // Use this as a template for new Gallery entries.
2 Gallery.register(
3 'linear-regression',
4 {
5 name: 'Linear Regressions',
6 title: 'Linear Regression Demo',
7 setup: function(parent) {
8 parent.innerHTML =
9 "<p>Click the buttons to generate linear regressions over either data "+
10 "series. If you zoom in and then click the regression button, the regression "+
11 "will only be run over visible points. Zoom back out to see what the local "+
12 "regression looks like over the full data.</p> "+
13 "<div id='demodiv' style='width: 480px; height: 320px;'></div>" +
14 "<div style='text-align:center; width: 480px'>" +
15 "<button style='color: green;' id='ry1'>Regression (Y1)</button> " +
16 "<button style='color: blue;' id='ry2'>Regression (Y2)</button> " +
17 "<button id='clear'>Clear Lines</button>" +
18 "</div>";
19 },
20 run: function() {
21 document.getElementById("ry1").onclick = function() { regression(1) };
22 document.getElementById("ry2").onclick = function() { regression(2) };
23 document.getElementById("clear").onclick = function() { clearLines() };
24
25 var data = [];
26 for (var i = 0; i < 120; i++) {
27 data.push([i,
28 i / 5.0 + 10.0 * Math.sin(i / 3.0),
29 30.0 - i / 5.0 - 10.0 * Math.sin(i / 3.0 + 1.0)]);
30 }
31
32 g = new Dygraph(
33 document.getElementById("demodiv"),
34 data,
35 {
36 labels: ['X', 'Y1', 'Y2'],
37 underlayCallback: drawLines,
38 drawPoints: true,
39 strokeWidth: 0.0
40 }
41 );
42
43 // coefficients of regression for each series.
44 // if coeffs = [ null, [1, 2], null ] then we draw a regression for series 1
45 // only. The regression line is y = 1 + 2 * x.
46 var coeffs = [ null, null, null ];
47 function regression(series) {
48 // Only run the regression over visible points.
49 var range = g.xAxisRange();
50
51 var sum_xy = 0.0, sum_x = 0.0, sum_y = 0.0, sum_x2 = 0.0, num = 0;
52 for (var i = 0; i < g.numRows(); i++) {
53 var x = g.getValue(i, 0);
54 if (x < range[0] || x > range[1]) continue;
55
56 var y = g.getValue(i, series);
57 if (y == null) continue;
58 if (y.length == 2) {
59 // using fractions
60 y = y[0] / y[1];
61 }
62
63 num++;
64 sum_x += x;
65 sum_y += y;
66 sum_xy += x * y;
67 sum_x2 += x * x;
68 }
69
70 var a = (sum_xy - sum_x * sum_y / num) / (sum_x2 - sum_x * sum_x / num);
71 var b = (sum_y - a * sum_x) / num;
72
73 coeffs[series] = [b, a];
74 if (typeof(console) != 'undefined') {
75 console.log("coeffs(" + series + "): [" + b + ", " + a + "]");
76 }
77
78 g.updateOptions({}); // forces a redraw.
79 }
80
81 function clearLines() {
82 for (var i = 0; i < coeffs.length; i++) coeffs[i] = null;
83 g.updateOptions({});
84 }
85
86 function drawLines(ctx, area, layout) {
87 if (typeof(g) == 'undefined') return; // won't be set on the initial draw.
88
89 var range = g.xAxisRange();
90 for (var i = 0; i < coeffs.length; i++) {
91 if (!coeffs[i]) continue;
92 var a = coeffs[i][1];
93 var b = coeffs[i][0];
94
95 var x1 = range[0];
96 var y1 = a * x1 + b;
97 var x2 = range[1];
98 var y2 = a * x2 + b;
99
100 var p1 = g.toDomCoords(x1, y1);
101 var p2 = g.toDomCoords(x2, y2);
102
103 var c = new RGBColor(g.getColors()[i - 1]);
104 c.r = Math.floor(255 - 0.5 * (255 - c.r));
105 c.g = Math.floor(255 - 0.5 * (255 - c.g));
106 c.b = Math.floor(255 - 0.5 * (255 - c.b));
107 var color = c.toHex();
108 ctx.save();
109 ctx.strokeStyle = color;
110 ctx.lineWidth = 1.0;
111 ctx.beginPath();
112 ctx.moveTo(p1[0], p1[1]);
113 ctx.lineTo(p2[0], p2[1]);
114 ctx.closePath();
115 ctx.stroke();
116 ctx.restore();
117 }
118 }
119 }
120 });