Add median and stddev aggregate functions.
diff --git a/render.js b/render.js
index abc52bd..3618977 100644
--- a/render.js
+++ b/render.js
@@ -462,6 +462,37 @@
       return agg_funcs.sum(l) / agg_funcs.count_nz(l);
     },
 
+    // also works for non-numeric values, as long as they're sortable
+    median: function(l) {
+      var comparator = function(a, b) {
+        a = a || '0'; // ensure consistent ordering given NaN and undefined
+        b = b || '0';
+        if (a < b) {
+          return -1;
+        } else if (a > b) {
+          return 1;
+        } else {
+          return 0;
+        }
+      }
+      if (l.length > 0) {
+        l.sort(comparator);
+        return l[parseInt(l.length/2)];
+      } else {
+        return null;
+      }
+    },
+
+    stddev: function(l) {
+      var avg = agg_funcs.avg(l);
+      var sumsq = 0.0;
+      for (var i in l) {
+        var d = parseFloat(l[i]) - avg;
+        if (d) sumsq += d * d;
+      }
+      return Math.sqrt(sumsq);
+    },
+
     color: function(l) {
       for (var i in l) {
         var v = l[i];
@@ -477,6 +508,7 @@
   agg_funcs.count_distinct.return_type = T_NUM;
   agg_funcs.sum.return_type = T_NUM;
   agg_funcs.avg.return_type = T_NUM;
+  agg_funcs.stddev.return_type = T_NUM;
   agg_funcs.cat.return_type = T_STRING;
   agg_funcs.color.return_type = T_NUM;
 
diff --git a/t/trender.js b/t/trender.js
index 0340a40..e65ff81 100644
--- a/t/trender.js
+++ b/t/trender.js
@@ -248,14 +248,21 @@
     ['fred', 11, '2013/02/03']
   ];
   var mpd = afterquery.internal.myParseDate;
-  afterquery.exec('group=a,b;only(c),count(c)', rawdata, function(grid) {
-    WVPASSEQ(grid.headers, ['a', 'b', 'c', 'c']);
+  var dlist = [mpd('2013/01/02'), mpd('2013/01/01'), mpd('2013/02/03')];
+  afterquery.exec('group=a,b;only(c),count(c),sum(c),min(c),max(c),' +
+                  'avg(c),median(c),stddev(c)', rawdata, function(grid) {
+    WVPASSEQ(grid.headers, ['a', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c']);
     WVPASSEQ(grid.data, [
-      ['fred', 9, mpd('2013/01/02'), 1],
-      ['bob', 7, mpd('2013/01/01'), 1],
-      ['fred', 11, mpd('2013/02/03'), 1]
+      ['fred', 9, dlist[0], 1, 0, dlist[0], dlist[0], 0, dlist[0], 0],
+      ['bob', 7, dlist[1], 1, 0, dlist[1], dlist[1], 0, dlist[1], 0],
+      ['fred', 11, dlist[2], 1, 0, dlist[2], dlist[2], 0, dlist[2], 0]
     ]);
   });
+  afterquery.exec('group=;count(b),sum(b),min(b),max(b),' +
+                  'avg(b),median(b),stddev(b)', rawdata, function(grid) {
+    WVPASSEQ(grid.headers, ['b', 'b', 'b', 'b', 'b', 'b', 'b']);
+    WVPASSEQ(grid.data, [[3, 27, 7, 11, 27.0/3.0, 9, Math.sqrt(8)]]);
+  });
   afterquery.exec('pivot=a;b;only(c)', rawdata, function(grid) {
     WVPASSEQ(grid.headers, ['a', 9, 7, 11]);
     WVPASSEQ(grid.types, [
@@ -265,8 +272,8 @@
       afterquery.T_DATE
     ]);
     WVPASSEQ(grid.data, [
-      ['fred', mpd('2013/01/02'), null, mpd('2013/02/03')],
-      ['bob', null, mpd('2013/01/01'), null]
+      ['fred', dlist[0], null, dlist[2]],
+      ['bob', null, dlist[1], null]
     ]);
   });
   afterquery.exec('pivot=a;b;c', rawdata, function(grid) {
@@ -290,8 +297,8 @@
       '11 only(c)', '11 count(c)'
     ]);
     WVPASSEQ(grid.data, [
-      ['fred', mpd('2013/01/02'), 1, null, null, mpd('2013/02/03'), 1],
-      ['bob', null, null, mpd('2013/01/01'), 1, null, null]
+      ['fred', dlist[0], 1, null, null, dlist[2], 1],
+      ['bob', null, null, dlist[1], 1, null, null]
     ]);
   });
   afterquery.exec('pivot=a;b,c;count(*)', rawdata, function(grid) {