Skip to content

Commit

Permalink
Optimise d3.mean.
Browse files Browse the repository at this point in the history
Originally we were using Welford’s algorithm, but this is primarily
useful when computing the variance in a numerically stable manner, since
Welford’s approach requires an incremental mean.

I’ve removed a test for the mean of more than one instance of
Number.MAX_VALUE as this is unlikely to occur in practice; most likely
this was the reason I used Welford’s algorithm in the first place.

There’s a paper [1] comparing various algorithms for computing the mean,
and Welford’s is actually slightly less accurate than the naïve
approach.  There are some more accurate approaches but I think it’s
overkill for d3.mean.

[1] Youngs, Edward A., and Elliot M. Cramer. "Some results relevant to
choice of sum and sum-of-product algorithms." Technometrics 13.3 (1971):
657-665.

Related: d3#1842.
  • Loading branch information
jasondavies committed Apr 11, 2014
1 parent 624f21c commit c0e84e2
Show file tree
Hide file tree
Showing 4 changed files with 11 additions and 15 deletions.
8 changes: 4 additions & 4 deletions d3.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@
return x != null && !isNaN(x);
}
d3.mean = function(array, f) {
var n = array.length, a, m = 0, i = -1, j = 0;
var s = 0, n = array.length, a, i = -1, j = n;
if (arguments.length === 1) {
while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j;
while (++i < n) if (d3_number(a = array[i])) s += a; else --j;
} else {
while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j;
while (++i < n) if (d3_number(a = f.call(array, array[i], i))) s += a; else --j;
}
return j ? m : undefined;
return j ? s / j : undefined;
};
d3.quantile = function(values, p) {
var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
Expand Down
2 changes: 1 addition & 1 deletion d3.min.js

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions src/arrays/mean.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import "../math/number";

d3.mean = function(array, f) {
var n = array.length,
var s = 0,
n = array.length,
a,
m = 0,
i = -1,
j = 0;
j = n;
if (arguments.length === 1) {
while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j;
while (++i < n) if (d3_number(a = array[i])) s += a; else --j;
} else {
while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j;
while (++i < n) if (d3_number(a = f.call(array, array[i], i))) s += a; else --j;
}
return j ? m : undefined;
return j ? s / j : undefined;
};
4 changes: 0 additions & 4 deletions test/arrays/mean-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ suite.addBatch({
assert.equal(mean([1, 2, 3, 4, 5, NaN]), 3);
assert.equal(mean([10, null, 3, undefined, 5, NaN]), 6);
},
"can handle large numbers without overflowing": function(mean) {
assert.equal(mean([Number.MAX_VALUE, Number.MAX_VALUE]), Number.MAX_VALUE);
assert.equal(mean([-Number.MAX_VALUE, -Number.MAX_VALUE]), -Number.MAX_VALUE);
},
"returns undefined for empty array": function(mean) {
assert.isUndefined(mean([]));
assert.isUndefined(mean([null]));
Expand Down

0 comments on commit c0e84e2

Please sign in to comment.