Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ Returns the mean of the given *iterable* of numbers. If the iterable contains no

Returns the median of the given *iterable* of numbers using the [R-7 method](https://en.wikipedia.org/wiki/Quantile#Estimating_quantiles_from_a_sample). If the iterable contains no numbers, returns undefined. An optional *accessor* function may be specified, which is equivalent to calling Array.from before computing the median. This method ignores undefined and NaN values; this is useful for ignoring missing data.

<a name="cumsum" href="#cumsum">#</a> d3.<b>cumsum</b>(<i>iterable</i>[, <i>accessor</i>]) · [Source](https://github.com/d3/d3-array/blob/master/src/cumsum.js), [Examples](https://observablehq.com/@d3/d3-cumsum)

Returns the cumulative sum of the given *iterable* of numbers, as a Float64Array of the same length. If the iterable contains no numbers, returns zeros. An optional *accessor* function may be specified, which is equivalent to calling Array.from before computing the cumulative sum. This method ignores undefined and NaN values; this is useful for ignoring missing data.

<a name="quantile" href="#quantile">#</a> d3.<b>quantile</b>(<i>iterable</i>, <i>p</i>[, <i>accessor</i>]) · [Source](https://github.com/d3/d3-array/blob/master/src/quantile.js), [Examples](https://observablehq.com/@d3/d3-mean-d3-median-and-friends)

Returns the *p*-quantile of the given *iterable* of numbers, where *p* is a number in the range [0, 1]. For example, the median can be computed using *p* = 0.5, the first quartile at *p* = 0.25, and the third quartile at *p* = 0.75. This particular implementation uses the [R-7 method](http://en.wikipedia.org/wiki/Quantile#Quantiles_of_a_population), which is the default for the R programming language and Excel. For example:
Expand Down
6 changes: 6 additions & 0 deletions src/cumsum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default function cumsum(values, valueof) {
var sum = 0, index = 0;
return Float64Array.from(values, valueof === undefined
? v => (sum += +v || 0)
: v => (sum += +valueof(v, index++, values) || 0));
}
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export {default as ascending} from "./ascending.js";
export {default as bisector} from "./bisector.js";
export {default as count} from "./count.js";
export {default as cross} from "./cross.js";
export {default as cumsum} from "./cumsum.js";
export {default as descending} from "./descending.js";
export {default as deviation} from "./deviation.js";
export {default as extent} from "./extent.js";
Expand Down
104 changes: 104 additions & 0 deletions test/cumsum-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
var tape = require("tape"),
arrays = require("../");

tape("cumsum(array) returns the cumulative sum of the specified numbers", function(test) {
test.deepEqual(arrays.cumsum([1]), [1]);
test.deepEqual(arrays.cumsum([5, 1, 2, 3, 4]), [5, 6, 8, 11, 15]);
test.deepEqual(arrays.cumsum([20, 3]), [20, 23]);
test.deepEqual(arrays.cumsum([3, 20]), [3, 23]);
test.end();
});

tape("cumsum(array) observes values that can be coerced to numbers", function(test) {
test.deepEqual(arrays.cumsum(["20", "3"]), [20, 23]);
test.deepEqual(arrays.cumsum(["3", "20"]), [3, 23]);
test.deepEqual(arrays.cumsum(["3", 20]), [3, 23]);
test.deepEqual(arrays.cumsum([20, "3"]), [20, 23]);
test.deepEqual(arrays.cumsum([3, "20"]), [3, 23]);
test.deepEqual(arrays.cumsum(["20", 3]), [20, 23]);
test.end();
});

tape("cumsum(array) ignores non-numeric values", function(test) {
test.deepEqual(arrays.cumsum(["a", "b", "c"]), [0, 0, 0]);
test.deepEqual(arrays.cumsum(["a", 1, "2"]), [0, 1, 3]);
test.end();
});

tape("cumsum(array) ignores null, undefined and NaN", function(test) {
test.deepEqual(arrays.cumsum([NaN, 1, 2, 3, 4, 5]), [0, 1, 3, 6, 10, 15]);
test.deepEqual(arrays.cumsum([1, 2, 3, 4, 5, NaN]), [1, 3, 6, 10, 15, 15]);
test.deepEqual(arrays.cumsum([10, null, 3, undefined, 5, NaN]), [10, 10, 13, 13, 18, 18]);
test.end();
});

tape("cumsum(array) returns zeros if there are no numbers", function(test) {
test.deepEqual(arrays.cumsum([]), []);
test.deepEqual(arrays.cumsum([NaN]), [0]);
test.deepEqual(arrays.cumsum([undefined]), [0]);
test.deepEqual(arrays.cumsum([undefined, NaN]), [0, 0]);
test.deepEqual(arrays.cumsum([undefined, NaN, {}]), [0, 0, 0]);
test.end();
});

tape("cumsum(array, f) returns the cumsum of the specified numbers", function(test) {
test.deepEqual(arrays.cumsum([1].map(box), unbox), [1]);
test.deepEqual(arrays.cumsum([5, 1, 2, 3, 4].map(box), unbox), [5, 6, 8, 11, 15]);
test.deepEqual(arrays.cumsum([20, 3].map(box), unbox), [20, 23]);
test.deepEqual(arrays.cumsum([3, 20].map(box), unbox), [3, 23]);
test.end();
});

tape("cumsum(array, f) observes values that can be coerced to numbers", function(test) {
test.deepEqual(arrays.cumsum(["20", "3"].map(box), unbox), [20, 23]);
test.deepEqual(arrays.cumsum(["3", "20"].map(box), unbox), [3, 23]);
test.deepEqual(arrays.cumsum(["3", 20].map(box), unbox), [3, 23]);
test.deepEqual(arrays.cumsum([20, "3"].map(box), unbox), [20, 23]);
test.deepEqual(arrays.cumsum([3, "20"].map(box), unbox), [3, 23]);
test.deepEqual(arrays.cumsum(["20", 3].map(box), unbox), [20, 23]);
test.end();
});

tape("cumsum(array, f) ignores non-numeric values", function(test) {
test.deepEqual(arrays.cumsum(["a", "b", "c"].map(box), unbox), [0, 0, 0]);
test.deepEqual(arrays.cumsum(["a", 1, "2"].map(box), unbox), [0, 1, 3]);
test.end();
});

tape("cumsum(array, f) ignores null, undefined and NaN", function(test) {
test.deepEqual(arrays.cumsum([NaN, 1, 2, 3, 4, 5].map(box), unbox), [0, 1, 3, 6, 10, 15]);
test.deepEqual(arrays.cumsum([1, 2, 3, 4, 5, NaN].map(box), unbox), [1, 3, 6, 10, 15, 15]);
test.deepEqual(arrays.cumsum([10, null, 3, undefined, 5, NaN].map(box), unbox), [10, 10, 13, 13, 18, 18]);
test.end();
});

tape("cumsum(array, f) returns zeros if there are no numbers", function(test) {
test.deepEqual(arrays.cumsum([].map(box), unbox), []);
test.deepEqual(arrays.cumsum([NaN].map(box), unbox), [0]);
test.deepEqual(arrays.cumsum([undefined].map(box), unbox), [0]);
test.deepEqual(arrays.cumsum([undefined, NaN].map(box), unbox), [0, 0]);
test.deepEqual(arrays.cumsum([undefined, NaN, {}].map(box), unbox), [0, 0, 0]);
test.end();
});

tape("cumsum(array, f) passes the accessor d, i, and array", function(test) {
var results = [], array = ["a", "b", "c"];
arrays.cumsum(array, function(d, i, array) { results.push([d, i, array]); });
test.deepEqual(results, [["a", 0, array], ["b", 1, array], ["c", 2, array]]);
test.end();
});

tape("cumsum(array, f) uses the global context", function(test) {
var results = [];
arrays.cumsum([1, 2], function() { results.push(this); });
test.deepEqual(results, [global, global]);
test.end();
});

function box(value) {
return {value: value};
}

function unbox(box) {
return box.value;
}