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) {