Skip to content

Commit 789ea0e

Browse files
committed
Plot.movingAverage
1 parent dff59a8 commit 789ea0e

File tree

6 files changed

+1786
-5
lines changed

6 files changed

+1786
-5
lines changed

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ export {Text, text, textX, textY} from "./marks/text.js";
1616
export {TickX, TickY, tickX, tickY} from "./marks/tick.js";
1717
export {bin1, bin2} from "./transforms/bin.js";
1818
export {group1, group2} from "./transforms/group.js";
19+
export {movingAverage} from "./transforms/movingAverage.js";
1920
export {stackX, stackY} from "./transforms/stack.js";

src/mark.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,18 @@ function Channel(data, {scale, type, value}) {
8383
return {
8484
scale,
8585
type,
86-
value: typeof value === "function" ? Array.from(data, value) : arrayify(value),
86+
value: valueof(data, value),
8787
label: value ? value.label : undefined
8888
};
8989
}
9090

9191
// This allows transforms to behave equivalently to channels.
92-
export function valueof(data, value) {
93-
return typeof value === "string" ? Array.from(data, field(value))
94-
: typeof value === "function" ? Array.from(data, value)
95-
: arrayify(value);
92+
export function valueof(data, value, type) {
93+
const array = type === undefined ? Array : type;
94+
return typeof value === "string" ? array.from(data, field(value))
95+
: typeof value === "function" ? array.from(data, value)
96+
: value && typeof value.transform === "function" ? arrayify(value.transform(data), type)
97+
: arrayify(value, type); // preserve undefined type
9698
}
9799

98100
export const field = label => Object.assign(d => d[label], {label});

src/transforms/movingAverage.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {field, identity, valueof} from "../mark.js";
2+
3+
// TODO allow partially-defined data
4+
export function movingAverage(N, value = identity) {
5+
if (!((N = Math.floor(N)) > 0)) throw new Error("invalid N");
6+
if (typeof value === "string") value = field(value);
7+
else if (typeof value !== "function") throw new Error("invalid value");
8+
return {
9+
transform(data) {
10+
let i = 0;
11+
let sum = 0;
12+
const values = valueof(data, value, Float64Array);
13+
const means = new Float64Array(values.length);
14+
for (let n = Math.min(N - 1, values.length); i < n; ++i) {
15+
means[i] = NaN;
16+
sum += values[i];
17+
}
18+
for (let n = values.length; i < n; ++i) {
19+
sum += values[i];
20+
means[i] = sum / N;
21+
sum -= values[i - N + 1];
22+
}
23+
means.subarray(0, N >> 1).reverse();
24+
means.subarray(N >> 1).reverse();
25+
means.reverse();
26+
return means;
27+
}
28+
};
29+
}

0 commit comments

Comments
 (0)