diff --git a/README.md b/README.md
index 1dbf241cfb..0e817f17b7 100644
--- a/README.md
+++ b/README.md
@@ -1890,9 +1890,9 @@ The Plot.windowX and Plot.windowY transforms compute a moving window around each
* **k** - the window size (the number of elements in the window)
* **anchor** - how to align the window: *start*, *middle*, or *end*
* **reduce** - the aggregation method (window reducer)
-* **extend** - whether to extend output values by truncating the window; defaults to false
+* **strict** - if true, disallow window truncation; defaults to false
-If the **extend** option is true, note that the resulting start values or end values or both (depending on the **anchor**) of each series may be noisy, as the window size will be truncated. For example, if **k** is 24 and **anchor** is *middle*, then the initial 11 values have effective window sizes of 13, 14, 15, … 23, and likewise the last 12 values have effective window sizes of 23, 22, 21, … 12. On the other hand, if the **extend** option is false, then some start values or end values will be undefined if **k** is greater than one.
+If the **strict** option is true, the resulting start values or end values or both (depending on the **anchor**) of each series may be undefined since there are not enough elements to create a window of size **k**. If the **strict** option is false (the default), the window will be automatically truncated as needed. For example, if **k** is 24 and **anchor** is *middle*, then the initial 11 values have effective window sizes of 13, 14, 15, … 23, and likewise the last 12 values have effective window sizes of 23, 22, 21, … 12. Values computed with a truncated window may be noiser; if you would prefer to not show this data, set the **strict** option to true.
The following window reducers are supported:
diff --git a/src/transforms/window.js b/src/transforms/window.js
index 3a4127646d..9eab504a8c 100644
--- a/src/transforms/window.js
+++ b/src/transforms/window.js
@@ -15,7 +15,7 @@ export function windowY(windowOptions = {}, options) {
export function window(options = {}) {
if (typeof options === "number") options = {k: options};
- let {k, reduce, shift, anchor, extend} = options;
+ let {k, reduce, shift, anchor, strict} = options;
if (anchor === undefined && shift !== undefined) {
anchor = maybeShift(shift);
warn(`Warning: the shift option is deprecated; please use anchor "${anchor}" instead.`);
@@ -23,7 +23,7 @@ export function window(options = {}) {
if (!((k = Math.floor(k)) > 0)) throw new Error(`invalid k: ${k}`);
const r = maybeReduce(reduce);
const s = maybeAnchor(anchor, k);
- return (extend ? extendReducer(r) : r)(k, s);
+ return (strict ? r : looseReducer(r))(k, s);
}
function maybeAnchor(anchor = "middle", k) {
@@ -66,7 +66,7 @@ function maybeReduce(reduce = "mean") {
return reduceSubarray(reduce);
}
-function extendReducer(reducer) {
+function looseReducer(reducer) {
return (k, s) => {
const reduce = reducer(k, s);
return {
@@ -75,17 +75,21 @@ function extendReducer(reducer) {
reduce.map(I, S, T);
for (let i = 0; i < s; ++i) {
const j = Math.min(n, i + k - s);
- reducer(j, i).map(I.subarray(0, j), S, T);
+ reducer(j, i).map(slice(I, 0, j), S, T);
}
for (let i = n - k + s + 1; i < n; ++i) {
const j = Math.max(0, i - s);
- reducer(n - j, i - j).map(I.subarray(j, n), S, T);
+ reducer(n - j, i - j).map(slice(I, j, n), S, T);
}
}
};
};
}
+function slice(I, i, j) {
+ return I.subarray ? I.subarray(i, j) : I.slice(i, j);
+}
+
function reduceSubarray(f) {
return (k, s) => ({
map(I, S, T) {
diff --git a/test/output/metroUnemploymentMoving.svg b/test/output/metroUnemploymentMoving.svg
index 392a4b6a3f..442f1706c0 100644
--- a/test/output/metroUnemploymentMoving.svg
+++ b/test/output/metroUnemploymentMoving.svg
@@ -66,51 +66,51 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/plots/aapl-bollinger.js b/test/plots/aapl-bollinger.js
index ad5423a123..60aa9b838f 100644
--- a/test/plots/aapl-bollinger.js
+++ b/test/plots/aapl-bollinger.js
@@ -16,5 +16,5 @@ export default async function() {
}
function bollinger(N, K) {
- return Plot.window({k: N, reduce: Y => d3.mean(Y) + K * d3.deviation(Y), anchor: "end"});
+ return Plot.window({k: N, reduce: Y => d3.mean(Y) + K * d3.deviation(Y), strict: true, anchor: "end"});
}
diff --git a/test/plots/gistemp-anomaly-moving.js b/test/plots/gistemp-anomaly-moving.js
index 1f3838bc9e..9f73329a43 100644
--- a/test/plots/gistemp-anomaly-moving.js
+++ b/test/plots/gistemp-anomaly-moving.js
@@ -16,7 +16,7 @@ export default async function() {
marks: [
Plot.ruleY([0]),
Plot.dot(data, {x: "Date", y: "Anomaly", stroke: "Anomaly"}),
- Plot.line(data, Plot.windowY({k: 24, extend: true}, {x: "Date", y: "Anomaly"}))
+ Plot.line(data, Plot.windowY({k: 24}, {x: "Date", y: "Anomaly"}))
]
});
}
diff --git a/test/plots/seattle-precipitation-sum.js b/test/plots/seattle-precipitation-sum.js
index ee7bbb6f91..2900a1f4cf 100644
--- a/test/plots/seattle-precipitation-sum.js
+++ b/test/plots/seattle-precipitation-sum.js
@@ -3,8 +3,8 @@ import * as d3 from "d3";
export default async function() {
const weather = (await d3.csv("data/seattle-weather.csv", d3.autoType)).slice(-28);
- const y = Plot.window({k: 7, reduce: "sum", anchor: "end"});
- const text = Plot.window({k: 7, reduce: V => Math.round(d3.sum(V)), anchor: "end"});
+ const y = Plot.window({k: 7, strict: true, reduce: "sum", anchor: "end"});
+ const text = Plot.window({k: 7, strict: true, reduce: V => Math.round(d3.sum(V)), anchor: "end"});
return Plot.plot({
marks: [
Plot.rectY(weather, Plot.map({y}, {x: "date", y: "precipitation", interval: d3.utcDay})),
diff --git a/test/plots/sf-temperature-band-area.js b/test/plots/sf-temperature-band-area.js
index 8730d1f111..3c03a10f6c 100644
--- a/test/plots/sf-temperature-band-area.js
+++ b/test/plots/sf-temperature-band-area.js
@@ -9,8 +9,8 @@ export default async function() {
label: "↑ Daily temperature range (°F)"
},
marks: [
- Plot.areaY(temperatures, Plot.windowY({k: 7, x: "date", y1: "low", y2: "high", curve: "step", fill: "#ccc"})),
- Plot.line(temperatures, Plot.windowY({k: 7, x: "date", y: d => (d.low + d.high) / 2, curve: "step"}))
+ Plot.areaY(temperatures, Plot.windowY({k: 7, strict: true, x: "date", y1: "low", y2: "high", curve: "step", fill: "#ccc"})),
+ Plot.line(temperatures, Plot.windowY({k: 7, strict: true, x: "date", y: d => (d.low + d.high) / 2, curve: "step"}))
],
width: 960
});
diff --git a/test/plots/sf-temperature-band.js b/test/plots/sf-temperature-band.js
index b20a1bdab0..996d7cc7c4 100644
--- a/test/plots/sf-temperature-band.js
+++ b/test/plots/sf-temperature-band.js
@@ -10,8 +10,8 @@ export default async function() {
},
marks: [
Plot.areaY(temperatures, {x: "date", y1: "low", y2: "high", curve: "step", fill: "#ccc"}),
- Plot.line(temperatures, Plot.windowY({x: "date", y: "low", k: 7, curve: "step", stroke: "blue"})),
- Plot.line(temperatures, Plot.windowY({x: "date", y: "high", k: 7, curve: "step", stroke: "red"}))
+ Plot.line(temperatures, Plot.windowY({x: "date", y: "low", k: 7, strict: true, curve: "step", stroke: "blue"})),
+ Plot.line(temperatures, Plot.windowY({x: "date", y: "high", k: 7, strict: true, curve: "step", stroke: "red"}))
],
width: 960
});
diff --git a/test/plots/travelers-covid-drop.js b/test/plots/travelers-covid-drop.js
index 1568074109..162ac1402f 100644
--- a/test/plots/travelers-covid-drop.js
+++ b/test/plots/travelers-covid-drop.js
@@ -13,7 +13,7 @@ export default async function() {
},
marks: [
Plot.lineY(travelers, {x: "date", y: d => d.current / d.previous - 1, strokeWidth: 0.25, curve: "step"}),
- Plot.lineY(travelers, Plot.windowY({x: "date", y: d => d.current / d.previous - 1, k: 7, stroke: "steelblue"}))
+ Plot.lineY(travelers, Plot.windowY({x: "date", y: d => d.current / d.previous - 1, k: 7, strict: true, stroke: "steelblue"}))
]
});
}
diff --git a/test/transforms/window-test.js b/test/transforms/window-test.js
index ede2a7a117..1fb8198076 100644
--- a/test/transforms/window-test.js
+++ b/test/transforms/window-test.js
@@ -28,130 +28,130 @@ it(`windowX(k, options) is equivalent to windowX({k, anchor: "middle", reduce: "
assert.deepStrictEqual(m1.x.transform(), m2.x.transform());
});
-it(`windowX(k, options) computes a moving average of window size k`, () => {
+it(`windowX({k, strict: true}, options) computes a moving average of window size k`, () => {
const data = range(6);
- const m1 = applyTransform(Plot.windowX(1, {x: d => d}), data);
+ const m1 = applyTransform(Plot.windowX({k: 1, strict: true}, {x: d => d}), data);
assert.deepStrictEqual(m1.x.transform(), [0, 1, 2, 3, 4, 5]);
- const m2 = applyTransform(Plot.windowX(2, {x: d => d}), data);
+ const m2 = applyTransform(Plot.windowX({k: 2, strict: true}, {x: d => d}), data);
assert.deepStrictEqual(m2.x.transform(), [0.5, 1.5, 2.5, 3.5, 4.5,, ]);
- const m3 = applyTransform(Plot.windowX(3, {x: d => d}), data);
+ const m3 = applyTransform(Plot.windowX({k: 3, strict: true}, {x: d => d}), data);
assert.deepStrictEqual(m3.x.transform(), [, 1, 2, 3, 4,, ]);
- const m4 = applyTransform(Plot.windowX(4, {x: d => d}), data);
+ const m4 = applyTransform(Plot.windowX({k: 4, strict: true}, {x: d => d}), data);
assert.deepStrictEqual(m4.x.transform(), [, 1.5, 2.5, 3.5,,, ]);
});
-it(`windowX({reduce: "mean"}) produces NaN if the current window contains NaN`, () => {
+it(`windowX({k, strict: true}) produces NaN if the current window contains NaN`, () => {
const data = [1, 1, 1, null, 1, 1, 1, 1, 1, NaN, 1, 1, 1];
- const m1 = applyTransform(Plot.windowX({reduce: "mean", k: 1, x: d => d}), data);
+ const m1 = applyTransform(Plot.windowX({k: 1, strict: true, x: d => d}), data);
assert.deepStrictEqual(m1.x.transform(), [1, 1, 1, NaN, 1, 1, 1, 1, 1, NaN, 1, 1, 1]);
- const m2 = applyTransform(Plot.windowX({reduce: "mean", k: 2, x: d => d}), data);
+ const m2 = applyTransform(Plot.windowX({k: 2, strict: true, x: d => d}), data);
assert.deepStrictEqual(m2.x.transform(), [1, 1, NaN, NaN, 1, 1, 1, 1, NaN, NaN, 1, 1,, ]);
- const m3 = applyTransform(Plot.windowX({reduce: "mean", k: 3, x: d => d}), data);
+ const m3 = applyTransform(Plot.windowX({k: 3, strict: true, x: d => d}), data);
assert.deepStrictEqual(m3.x.transform(), [, 1, NaN, NaN, NaN, 1, 1, 1, NaN, NaN, NaN, 1,, ]);
});
-it(`windowX({reduce: "mean"}) treats null as NaN`, () => {
+it(`windowX({k, strict: true}) treats null as NaN`, () => {
const data = [1, 1, 1, null, 1, 1, 1, 1, 1, null, 1, 1, 1];
- const m3 = applyTransform(Plot.windowX({reduce: "mean", k: 3, x: d => d}), data);
+ const m3 = applyTransform(Plot.windowX({k: 3, strict: true, x: d => d}), data);
assert.deepStrictEqual(m3.x.transform(), [, 1, NaN, NaN, NaN, 1, 1, 1, NaN, NaN, NaN, 1,, ]);
});
-it(`windowX({reduce: "mean", anchor}) respects the given anchor`, () => {
+it(`windowX({k, strict: true, anchor}) respects the given anchor`, () => {
const data = [0, 1, 2, 3, 4, 5];
- const mc = applyTransform(Plot.windowX({reduce: "mean", k: 3, anchor: "middle", x: d => d}), data);
+ const mc = applyTransform(Plot.windowX({k: 3, strict: true, anchor: "middle", x: d => d}), data);
assert.deepStrictEqual(mc.x.transform(), [, 1, 2, 3, 4,, ]);
- const ml = applyTransform(Plot.windowX({reduce: "mean", k: 3, anchor: "start", x: d => d}), data);
+ const ml = applyTransform(Plot.windowX({k: 3, strict: true, anchor: "start", x: d => d}), data);
assert.deepStrictEqual(ml.x.transform(), [1, 2, 3, 4,,, ]);
- const mt = applyTransform(Plot.windowX({reduce: "mean", k: 3, anchor: "end", x: d => d}), data);
+ const mt = applyTransform(Plot.windowX({k: 3, strict: true, anchor: "end", x: d => d}), data);
assert.deepStrictEqual(mt.x.transform(), [,, 1, 2, 3, 4]);
});
-it(`windowX({reduce: "mean", k, extend: true}) truncates the window at the start and end`, () => {
+it(`windowX(k) truncates the window at the start and end`, () => {
const data = range(6);
- const m1 = applyTransform(Plot.windowX({k: 1, extend: true}, {x: d => d}), data);
+ const m1 = applyTransform(Plot.windowX(1, {x: d => d}), data);
assert.deepStrictEqual(m1.x.transform(), [0, 1, 2, 3, 4, 5]);
- const m2 = applyTransform(Plot.windowX({k: 2, extend: true}, {x: d => d}), data);
+ const m2 = applyTransform(Plot.windowX(2, {x: d => d}), data);
assert.deepStrictEqual(m2.x.transform(), [0.5, 1.5, 2.5, 3.5, 4.5, 5]);
- const m3 = applyTransform(Plot.windowX({k: 3, extend: true}, {x: d => d}), data);
+ const m3 = applyTransform(Plot.windowX(3, {x: d => d}), data);
assert.deepStrictEqual(m3.x.transform(), [0.5, 1, 2, 3, 4, 4.5]);
- const m4 = applyTransform(Plot.windowX({k: 4, extend: true}, {x: d => d}), data);
+ const m4 = applyTransform(Plot.windowX(4, {x: d => d}), data);
assert.deepStrictEqual(m4.x.transform(), [1, 1.5, 2.5, 3.5, 4, 4.5]);
});
-it(`windowX({reduce: "mean", k, extend: true, anchor: "start"}) truncates the window at the end`, () => {
+it(`windowX({k, anchor: "start"}) truncates the window at the end`, () => {
const data = range(6);
- const m1 = applyTransform(Plot.windowX({k: 1, extend: true, anchor: "start"}, {x: d => d}), data);
+ const m1 = applyTransform(Plot.windowX({k: 1, anchor: "start"}, {x: d => d}), data);
assert.deepStrictEqual(m1.x.transform(), [0, 1, 2, 3, 4, 5]);
- const m2 = applyTransform(Plot.windowX({k: 2, extend: true, anchor: "start"}, {x: d => d}), data);
+ const m2 = applyTransform(Plot.windowX({k: 2, anchor: "start"}, {x: d => d}), data);
assert.deepStrictEqual(m2.x.transform(), [0.5, 1.5, 2.5, 3.5, 4.5, 5]);
- const m3 = applyTransform(Plot.windowX({k: 3, extend: true, anchor: "start"}, {x: d => d}), data);
+ const m3 = applyTransform(Plot.windowX({k: 3, anchor: "start"}, {x: d => d}), data);
assert.deepStrictEqual(m3.x.transform(), [1, 2, 3, 4, 4.5, 5]);
- const m4 = applyTransform(Plot.windowX({k: 4, extend: true, anchor: "start"}, {x: d => d}), data);
+ const m4 = applyTransform(Plot.windowX({k: 4, anchor: "start"}, {x: d => d}), data);
assert.deepStrictEqual(m4.x.transform(), [1.5, 2.5, 3.5, 4, 4.5, 5]);
});
-it(`windowX({reduce: "mean", k, extend: true, anchor: "end"}) truncates the window at the start`, () => {
+it(`windowX({k, anchor: "end"}) truncates the window at the start`, () => {
const data = range(6);
- const m1 = applyTransform(Plot.windowX({k: 1, extend: true, anchor: "end"}, {x: d => d}), data);
+ const m1 = applyTransform(Plot.windowX({k: 1, anchor: "end"}, {x: d => d}), data);
assert.deepStrictEqual(m1.x.transform(), [0, 1, 2, 3, 4, 5]);
- const m2 = applyTransform(Plot.windowX({k: 2, extend: true, anchor: "end"}, {x: d => d}), data);
+ const m2 = applyTransform(Plot.windowX({k: 2, anchor: "end"}, {x: d => d}), data);
assert.deepStrictEqual(m2.x.transform(), [0, 0.5, 1.5, 2.5, 3.5, 4.5]);
- const m3 = applyTransform(Plot.windowX({k: 3, extend: true, anchor: "end"}, {x: d => d}), data);
+ const m3 = applyTransform(Plot.windowX({k: 3, anchor: "end"}, {x: d => d}), data);
assert.deepStrictEqual(m3.x.transform(), [0, 0.5, 1, 2, 3, 4]);
- const m4 = applyTransform(Plot.windowX({k: 4, extend: true, anchor: "end"}, {x: d => d}), data);
+ const m4 = applyTransform(Plot.windowX({k: 4, anchor: "end"}, {x: d => d}), data);
assert.deepStrictEqual(m4.x.transform(), [0, 0.5, 1, 1.5, 2.5, 3.5]);
});
-it(`windowX({reduce: "mean", k, extend: true}) handles k being bigger than the data size`, () => {
+it(`windowX(k) handles k being bigger than the data size`, () => {
const data = range(6);
- const m3 = applyTransform(Plot.windowX({k: 3, extend: true}, {x: d => d}), data);
+ const m3 = applyTransform(Plot.windowX(3, {x: d => d}), data);
assert.deepStrictEqual(m3.x.transform(), [0.5, 1, 2, 3, 4, 4.5]);
- const m5 = applyTransform(Plot.windowX({k: 5, extend: true}, {x: d => d}), data);
+ const m5 = applyTransform(Plot.windowX(5, {x: d => d}), data);
assert.deepStrictEqual(m5.x.transform(), [1, 1.5, 2, 3, 3.5, 4]);
- const m6 = applyTransform(Plot.windowX({k: 6, extend: true}, {x: d => d}), data);
+ const m6 = applyTransform(Plot.windowX(6, {x: d => d}), data);
assert.deepStrictEqual(m6.x.transform(), [1.5, 2, 2.5, 3, 3.5, 4]);
- const m7 = applyTransform(Plot.windowX({k: 7, extend: true}, {x: d => d}), data);
+ const m7 = applyTransform(Plot.windowX(7, {x: d => d}), data);
assert.deepStrictEqual(m7.x.transform(), [1.5, 2, 2.5, 2.5, 3, 3.5]);
- const m8 = applyTransform(Plot.windowX({k: 8, extend: true}, {x: d => d}), data);
+ const m8 = applyTransform(Plot.windowX(8, {x: d => d}), data);
assert.deepStrictEqual(m8.x.transform(), [2, 2.5, 2.5, 2.5, 3, 3.5]);
- const m9 = applyTransform(Plot.windowX({k: 9, extend: true}, {x: d => d}), data);
+ const m9 = applyTransform(Plot.windowX(9, {x: d => d}), data);
assert.deepStrictEqual(m9.x.transform(), [2, 2.5, 2.5, 2.5, 2.5, 3]);
- const m10 = applyTransform(Plot.windowX({k: 10, extend: true}, {x: d => d}), data);
+ const m10 = applyTransform(Plot.windowX(10, {x: d => d}), data);
assert.deepStrictEqual(m10.x.transform(), [2.5, 2.5, 2.5, 2.5, 2.5, 3]);
- const m11 = applyTransform(Plot.windowX({k: 11, extend: true}, {x: d => d}), data);
+ const m11 = applyTransform(Plot.windowX(11, {x: d => d}), data);
assert.deepStrictEqual(m11.x.transform(), [2.5, 2.5, 2.5, 2.5, 2.5, 2.5]);
});
-it(`windowX({reduce: "max", k}) computes a moving maximum of window size k`, () => {
+it(`windowX({reduce: "max", k, strict: true}) computes a moving maximum of window size k`, () => {
const data = [0, 1, 2, 3, 4, 5];
- const m1 = applyTransform(Plot.windowX({reduce: "max", k: 1, x: d => d}), data);
+ const m1 = applyTransform(Plot.windowX({reduce: "max", k: 1, strict: true, x: d => d}), data);
assert.deepStrictEqual(m1.x.transform(), [0, 1, 2, 3, 4, 5]);
- const m2 = applyTransform(Plot.windowX({reduce: "max", k: 2, x: d => d}), data);
+ const m2 = applyTransform(Plot.windowX({reduce: "max", k: 2, strict: true, x: d => d}), data);
assert.deepStrictEqual(m2.x.transform(), [1, 2, 3, 4, 5,, ]);
- const m3 = applyTransform(Plot.windowX({reduce: "max", k: 3, x: d => d}), data);
+ const m3 = applyTransform(Plot.windowX({reduce: "max", k: 3, strict: true, x: d => d}), data);
assert.deepStrictEqual(m3.x.transform(), [, 2, 3, 4, 5,, ]);
- const m4 = applyTransform(Plot.windowX({reduce: "max", k: 4, x: d => d}), data);
+ const m4 = applyTransform(Plot.windowX({reduce: "max", k: 4, strict: true, x: d => d}), data);
assert.deepStrictEqual(m4.x.transform(), [, 3, 4, 5,,, ]);
});
-it(`windowX({reduce: "max"}) produces NaN if the current window contains NaN`, () => {
+it(`windowX({reduce: "max", k, strict: true}) produces NaN if the current window contains NaN`, () => {
const data = [1, 1, 1, NaN, 1, 1, 1, 1, 1, NaN, NaN, NaN, NaN, 1];
- const m3 = applyTransform(Plot.windowX({reduce: "max", k: 3, x: d => d}), data);
+ const m3 = applyTransform(Plot.windowX({reduce: "max", k: 3, strict: true, x: d => d}), data);
assert.deepStrictEqual(m3.x.transform(), [, 1, NaN, NaN, NaN, 1, 1, 1, NaN, NaN, NaN, NaN, NaN,, ]);
});
-it(`windowX({reduce: "max"}) treats null as NaN`, () => {
+it(`windowX({reduce: "max", k, strict: true}) treats null as NaN`, () => {
const data = [1, 1, 1, null, 1, 1, 1, 1, 1, null, null, null, null, 1];
- const m3 = applyTransform(Plot.windowX({reduce: "max", k: 3, x: d => d}), data);
+ const m3 = applyTransform(Plot.windowX({reduce: "max", k: 3, strict: true, x: d => d}), data);
assert.deepStrictEqual(m3.x.transform(), [, 1, NaN, NaN, NaN, 1, 1, 1, NaN, NaN, NaN, NaN, NaN,, ]);
});
-it(`windowX({reduce: "max", anchor}) respects the given anchor`, () => {
+it(`windowX({reduce: "max", k, strict: true, anchor}) respects the given anchor`, () => {
const data = [0, 1, 2, 3, 4, 5];
- const mc = applyTransform(Plot.windowX({reduce: "max", k: 3, anchor: "middle", x: d => d}), data);
+ const mc = applyTransform(Plot.windowX({reduce: "max", k: 3, strict: true, anchor: "middle", x: d => d}), data);
assert.deepStrictEqual(mc.x.transform(), [, 2, 3, 4, 5,, ]);
- const ml = applyTransform(Plot.windowX({reduce: "max", k: 3, anchor: "start", x: d => d}), data);
+ const ml = applyTransform(Plot.windowX({reduce: "max", k: 3, strict: true, anchor: "start", x: d => d}), data);
assert.deepStrictEqual(ml.x.transform(), [2, 3, 4, 5,,, ]);
- const mt = applyTransform(Plot.windowX({reduce: "max", k: 3, anchor: "end", x: d => d}), data);
+ const mt = applyTransform(Plot.windowX({reduce: "max", k: 3, strict: true, anchor: "end", x: d => d}), data);
assert.deepStrictEqual(mt.x.transform(), [,, 2, 3, 4, 5]);
});