Add first part of linear regression, and fixed hash hack so it doesn't jump.
authorRobert Konigsberg <konigsberg@google.com>
Sun, 15 Jan 2012 22:42:50 +0000 (17:42 -0500)
committerRobert Konigsberg <konigsberg@google.com>
Sun, 15 Jan 2012 22:42:50 +0000 (17:42 -0500)
gallery/gallery.js
gallery/index.html
gallery/linear-regression.js [new file with mode: 0644]

index 10128ff..08299aa 100644 (file)
@@ -46,7 +46,7 @@ Gallery.start = function() {
       document.getElementById("title").textContent = demo.title ? demo.title : "";
       demo.innerDiv.className = "selected";
       Gallery.workareaChild.id = id;
-      location.hash = id;
+      location.hash = "g/" + id;
       Gallery.workareaChild.innerHTML='';
       if (demo.setup) {
         demo.setup(Gallery.workareaChild);
@@ -71,8 +71,10 @@ Gallery.register = function(id, demo) {
 
 Gallery.hashChange = function(event) {
   if (location.hash) {
-    var id = location.hash.substring(1) + "-toc";
-    var elem = document.getElementById(id);
-    elem.onclick();
+    if (location.hash.indexOf("#g/") == 0) {
+      var id = location.hash.substring(3) + "-toc";
+      var elem = document.getElementById(id);
+      elem.onclick();
+    }
   }
 };
\ No newline at end of file
index 75c0521..5b304c2 100644 (file)
@@ -29,6 +29,7 @@
     <script src="styled-chart-labels.js"></script>
     <script src="temperature-sf-ny.js"></script>
     <script src="interaction.js"></script>
+    <script src="linear-regression.js"></script>
 
     <!-- These might not remain in the gallery -->
     <script src="dygraph-simple.js"></script>
diff --git a/gallery/linear-regression.js b/gallery/linear-regression.js
new file mode 100644 (file)
index 0000000..77f69c5
--- /dev/null
@@ -0,0 +1,120 @@
+// Use this as a template for new Gallery entries.
+Gallery.register(
+  'linear-regression',
+  {
+    name: 'Linear Regressions',
+    title: 'Linear Regression Demo',
+    setup: function(parent) {
+      parent.innerHTML =
+        "<p>Click the buttons to generate linear regressions over either data "+
+        "series. If you zoom in and then click the regression button, the regression "+
+        "will only be run over visible points. Zoom back out to see what the local "+
+        "regression looks like over the full data.</p> "+
+        "<div id='demodiv' style='width: 480px; height: 320px;'></div>" +
+        "<div style='text-align:center; width: 480px'>" + 
+        "<button style='color: green;' id='ry1'>Regression (Y1)</button> " +
+        "<button style='color: blue;' id='ry2'>Regression (Y2)</button> " +
+        "<button id='clear'>Clear Lines</button>" +
+        "</div>";
+    },
+    run: function() {
+      document.getElementById("ry1").onclick = function() { regression(1) };
+      document.getElementById("ry2").onclick = function() { regression(2) };
+      document.getElementById("clear").onclick = function() { clearLines() };
+
+      var data = [];
+      for (var i = 0; i < 120; i++) {
+        data.push([i,
+                   i / 5.0 + 10.0 * Math.sin(i / 3.0),
+                   30.0 - i / 5.0 - 10.0 * Math.sin(i / 3.0 + 1.0)]);
+      }
+
+      g = new Dygraph(
+              document.getElementById("demodiv"),
+              data,
+              {
+                labels: ['X', 'Y1', 'Y2'],
+                underlayCallback: drawLines,
+                drawPoints: true,
+                strokeWidth: 0.0
+              }
+          );
+
+      // coefficients of regression for each series.
+      // if coeffs = [ null, [1, 2], null ] then we draw a regression for series 1
+      // only. The regression line is y = 1 + 2 * x.
+      var coeffs = [ null, null, null ];
+      function regression(series) {
+        // Only run the regression over visible points.
+        var range = g.xAxisRange();
+
+        var sum_xy = 0.0, sum_x = 0.0, sum_y = 0.0, sum_x2 = 0.0, num = 0;
+        for (var i = 0; i < g.numRows(); i++) {
+          var x = g.getValue(i, 0);
+          if (x < range[0] || x > range[1]) continue;
+
+          var y = g.getValue(i, series);
+          if (y == null) continue;
+          if (y.length == 2) {
+            // using fractions
+            y = y[0] / y[1];
+          }
+
+          num++;
+          sum_x += x;
+          sum_y += y;
+          sum_xy += x * y;
+          sum_x2 += x * x;
+        }
+
+        var a = (sum_xy - sum_x * sum_y / num) / (sum_x2 - sum_x * sum_x / num);
+        var b = (sum_y - a * sum_x) / num;
+
+        coeffs[series] = [b, a];
+        if (typeof(console) != 'undefined') {
+          console.log("coeffs(" + series + "): [" + b + ", " + a + "]");
+        }
+
+        g.updateOptions({});  // forces a redraw.
+      }
+
+      function clearLines() {
+        for (var i = 0; i < coeffs.length; i++) coeffs[i] = null;
+        g.updateOptions({});
+      }
+
+      function drawLines(ctx, area, layout) {
+        if (typeof(g) == 'undefined') return;  // won't be set on the initial draw.
+
+        var range = g.xAxisRange();
+        for (var i = 0; i < coeffs.length; i++) {
+          if (!coeffs[i]) continue;
+          var a = coeffs[i][1];
+          var b = coeffs[i][0];
+
+          var x1 = range[0];
+          var y1 = a * x1 + b;
+          var x2 = range[1];
+          var y2 = a * x2 + b;
+
+          var p1 = g.toDomCoords(x1, y1);
+          var p2 = g.toDomCoords(x2, y2);
+
+          var c = new RGBColor(g.getColors()[i - 1]);
+          c.r = Math.floor(255 - 0.5 * (255 - c.r));
+          c.g = Math.floor(255 - 0.5 * (255 - c.g));
+          c.b = Math.floor(255 - 0.5 * (255 - c.b));
+          var color = c.toHex();
+          ctx.save();
+          ctx.strokeStyle = color;
+          ctx.lineWidth = 1.0;
+          ctx.beginPath();
+          ctx.moveTo(p1[0], p1[1]);
+          ctx.lineTo(p2[0], p2[1]);
+          ctx.closePath();
+          ctx.stroke();
+          ctx.restore();
+        }
+      }
+    }
+  });