Skip to content

Commit

Permalink
Add d3.median.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Oct 11, 2011
1 parent 9523c4d commit 31cb66f
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 6 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ d3.core.js: \
src/core/ascending.js \
src/core/descending.js \
src/core/mean.js \
src/core/median.js \
src/core/min.js \
src/core/max.js \
src/core/number.js \
src/core/sum.js \
src/core/quantile.js \
src/core/zip.js \
Expand Down
12 changes: 10 additions & 2 deletions d3.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,17 @@ d3.mean = function(array, f) {
i = -1,
j = 0;
if (arguments.length === 1) {
while (++i < n) if (!isNaN(a = array[i]) && a != null) m += (a - m) / ++j;
while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j;
} else {
while (++i < n) if (!isNaN(a = f.call(array, array[i], i)) && a != null) m += (a - m) / ++j;
while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j;
}
return j ? m : undefined;
};
d3.median = function(array, f) {
if (arguments.length > 1) array = array.map(f);
array = array.filter(d3_number);
return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined;
};
d3.min = function(array, f) {
var i = -1,
n = array.length,
Expand Down Expand Up @@ -100,6 +105,9 @@ d3.max = function(array, f) {
}
return a;
};
function d3_number(x) {
return x != null && !isNaN(x);
}
d3.sum = function(array, f) {
var s = 0,
n = array.length,
Expand Down
4 changes: 2 additions & 2 deletions d3.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/core/mean.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ d3.mean = function(array, f) {
i = -1,
j = 0;
if (arguments.length === 1) {
while (++i < n) if (!isNaN(a = array[i]) && a != null) m += (a - m) / ++j;
while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j;
} else {
while (++i < n) if (!isNaN(a = f.call(array, array[i], i)) && a != null) m += (a - m) / ++j;
while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j;
}
return j ? m : undefined;
};
5 changes: 5 additions & 0 deletions src/core/median.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
d3.median = function(array, f) {
if (arguments.length > 1) array = array.map(f);
array = array.filter(d3_number);
return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined;
};
3 changes: 3 additions & 0 deletions src/core/number.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function d3_number(x) {
return x != null && !isNaN(x);
}
43 changes: 43 additions & 0 deletions test/core/median-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require("../env");
require("../../d3");

var vows = require("vows"),
assert = require("assert");

var suite = vows.describe("d3.median");

suite.addBatch({
"median": {
topic: function() {
return d3.median;
},
"returns the median value for numbers": function(median) {
assert.equal(median([1]), 1);
assert.equal(median([5, 1, 2, 3, 4]), 3);
assert.equal(median([20, 3]), 11.5);
assert.equal(median([3, 20]), 11.5);
},
"ignores null, undefined and NaN": function(median) {
assert.equal(median([NaN, 1, 2, 3, 4, 5]), 3);
assert.equal(median([1, 2, 3, 4, 5, NaN]), 3);
assert.equal(median([10, null, 3, undefined, 5, NaN]), 5);
},
"can handle large numbers without overflowing": function(median) {
assert.equal(median([Number.MAX_VALUE, Number.MAX_VALUE]), Number.MAX_VALUE);
assert.equal(median([-Number.MAX_VALUE, -Number.MAX_VALUE]), -Number.MAX_VALUE);
},
"returns undefined for empty array": function(median) {
assert.isUndefined(median([]));
assert.isUndefined(median([null]));
assert.isUndefined(median([undefined]));
assert.isUndefined(median([NaN]));
assert.isUndefined(median([NaN, NaN]));
},
"applies the optional accessor function": function(median) {
assert.equal(d3.median([[1, 2, 3, 4, 5], [2, 4, 6, 8, 10]], function(d) { return d3.median(d); }), 4.5);
assert.equal(d3.median([1, 2, 3, 4, 5], function(d, i) { return i; }), 2);
}
}
});

suite.export(module);

0 comments on commit 31cb66f

Please sign in to comment.