Fix drawing.js bug
[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 // coefficients of regression for each series.
33 // if coeffs = [ null, [1, 2], null ] then we draw a regression for series 1
34 // only. The regression line is y = 1 + 2 * x.
35 var coeffs = [ null, null, null ];
36 function regression(series) {
37 // Only run the regression over visible points.
38 var range = g.xAxisRange();
39
40 var sum_xy = 0.0, sum_x = 0.0, sum_y = 0.0, sum_x2 = 0.0, num = 0;
41 for (var i = 0; i < g.numRows(); i++) {
42 var x = g.getValue(i, 0);
43 if (x < range[0] || x > range[1]) continue;
44
45 var y = g.getValue(i, series);
46 if (y == null) continue;
47 if (y.length == 2) {
48 // using fractions
49 y = y[0] / y[1];
50 }
51
52 num++;
53 sum_x += x;
54 sum_y += y;
55 sum_xy += x * y;
56 sum_x2 += x * x;
57 }
58
59 var a = (sum_xy - sum_x * sum_y / num) / (sum_x2 - sum_x * sum_x / num);
60 var b = (sum_y - a * sum_x) / num;
61
62 coeffs[series] = [b, a];
63 if (typeof(console) != 'undefined') {
64 console.log("coeffs(" + series + "): [" + b + ", " + a + "]");
65 }
66
67 g.updateOptions({}); // forces a redraw.
68 }
69
70 function clearLines() {
71 for (var i = 0; i < coeffs.length; i++) coeffs[i] = null;
72 g.updateOptions({});
73 }
74
75 function drawLines(ctx, area, layout) {
76 if (typeof(g) == 'undefined') return; // won't be set on the initial draw.
77
78 var range = g.xAxisRange();
79 for (var i = 0; i < coeffs.length; i++) {
80 if (!coeffs[i]) continue;
81 var a = coeffs[i][1];
82 var b = coeffs[i][0];
83
84 var x1 = range[0];
85 var y1 = a * x1 + b;
86 var x2 = range[1];
87 var y2 = a * x2 + b;
88
89 var p1 = g.toDomCoords(x1, y1);
90 var p2 = g.toDomCoords(x2, y2);
91
92 var c = new RGBColor(g.getColors()[i - 1]);
93 c.r = Math.floor(255 - 0.5 * (255 - c.r));
94 c.g = Math.floor(255 - 0.5 * (255 - c.g));
95 c.b = Math.floor(255 - 0.5 * (255 - c.b));
96 var color = c.toHex();
97 ctx.save();
98 ctx.strokeStyle = color;
99 ctx.lineWidth = 1.0;
100 ctx.beginPath();
101 ctx.moveTo(p1[0], p1[1]);
102 ctx.lineTo(p2[0], p2[1]);
103 ctx.closePath();
104 ctx.stroke();
105 ctx.restore();
106 }
107 }
108
109 g = new Dygraph(
110 document.getElementById("demodiv"),
111 data,
112 {
113 labels: ['X', 'Y1', 'Y2'],
114 underlayCallback: drawLines,
115 drawPoints: true,
116 strokeWidth: 0.0
117 }
118 );
119 }
120 });