Unlisted
Edited
Jul 15
Paused
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function decimateIndex(index, [X, Y], Z, { pixelSize = 0.5, curve } = {}) {
if (typeof curve === "string" && curve.match(/^(bump|linear|monotone|step)/))
curve = false;
const J = [];
const pixel = [];
for (const I of Z ? d3.group(index, (i) => Z[i]).values() : [index]) {
let x0;
for (const i of I) {
const x = Math.floor(X[i] / pixelSize);
if (x !== x0) pick(), (x0 = x);
pixel.push(i);
}
pick();
}
return J;

function pick() {
const n = pixel.length;
if (!n) return;
let x1 = Infinity;
let y1 = Infinity;
let x2 = -Infinity;
let y2 = -Infinity;
let ix1, ix2, iy1, iy2;
for (let j = 0; j < n; ++j) {
const x = X[pixel[j]];
const y = Y[pixel[j]];
if (x < x1) (ix1 = j), (x1 = x);
if (x > x2) (ix2 = j), (x2 = x);
if (y < y1) (iy1 = j), (y1 = y);
if (y > y2) (iy2 = j), (y2 = y);
}
for (let j = 0; j < n; ++j) {
if (
j === 0 ||
j === n - 1 ||
j === ix1 ||
j === ix2 ||
j === iy1 ||
j === iy2 ||
(curve && (j === 1 || j === n - 2))
)
J.push(pixel[j]);
}
pixel.length = 0;
}
}
Insert cell
function decimateK(k, pixelSize, options) {
if (!pixelSize) return options;
return Plot.initializer(options, function (data, facets, values, scales) {
const XY = [
values.x.scale
? Float64Array.from(values.x.value, scales[values.x.scale])
: values.x.value,
values.y
? values.y.scale
? Float64Array.from(values.y.value, scales[values.y.scale])
: values.y.value
: []
];
const Z = values.z && values.z.value;

if (k === "y") XY.reverse();
return {
data,
facets: facets.map((index) =>
decimateIndex(index, XY, Z, { pixelSize, curve: options.curve })
)
};
});
}
Insert cell
function decimateX({ pixelSize = 0.5, ...options } = {}) {
return decimateK("x", pixelSize, options);
}
Insert cell
function decimateY({ pixelSize = 0.5, ...options } = {}) {
return decimateK("y", pixelSize, options);
}
Insert cell
data = d3.cumsum({ length: N }, d3.randomNormal.source(d3.randomLcg(42))())
Insert cell
import { Plot } from "@recifs/brush-1653"
Insert cell
Insert cell
data.length > 1e5
? md`*Not even trying to render that many points*`
: visibility().then(() =>
Plot.plot({
marginLeft: 60,
grid: true,
marks: [
Plot.lineY(data),
Plot.lineY(data, Plot.brushX({ x: [], y: [] }))
]
})
)
Insert cell
Insert cell
visibility().then(() =>
Plot.plot({
marginLeft: 60,
grid: true,
marks: [Plot.lineY(data, decimateX({ tip: true }))]
})
)
Insert cell
Insert cell
visibility().then(() =>
Plot.plot({
marginLeft: 60,
grid: true,
marks: [
Plot.lineY(
data.slice(0, 12000),
decimateX({
x: (d, i) => i % 2000,
stroke: (d, i) => "a" + Math.floor(i / 2000)
})
)
]
})
)
Insert cell
visibility().then(() =>
Plot.plot({
marginLeft: 60,
grid: true,
marks: [
Plot.lineY(
data.slice(0, 12000),
decimateX({
x: (d, i) => i % 2000,
stroke: (d, i) => i % 1000,
z: (d, i) => Math.floor(i / 2000)
})
)
]
})
)
Insert cell
Insert cell
Insert cell
A = {
const A = d3
.range(11)
.map((i) => [Math.sin((i * Math.PI) / 5.5), Math.cos((i * Math.PI) / 5.5)]);
A.splice(2, 0, A[2]); // repeat one point
return A;
}
Insert cell
visibility().then(() =>
Plot.plot({
inset: 20,
grid: true,
marks: [
Plot.line(A, {
curve,
marker: true,
stroke: "orange"
}),
Plot.line(
A,
decimateX({
curve,
marker: true,
stroke: "steelblue",
mixBlendMode: "multiply"
})
)
]
})
)
Insert cell
Insert cell
B = {
const A = d3
.range(10)
.map((i) => [Math.sin((i * Math.PI) / 5), Math.cos((i * Math.PI) / 5)]);
A.splice(2, 0, A[2].slice()); // repeat one point
A[2][1] += 0.4; // modify it
return A;
}
Insert cell
visibility().then(() =>
Plot.plot({
inset: 20,
grid: true,
marks: [
Plot.line(B, {
curve: "catmull-rom-closed",
marker: true,
stroke: "orange"
}),
Plot.line(
B,
decimateX({
curve: "catmull-rom-closed",
marker: true,
stroke: "steelblue",
mixBlendMode: "multiply"
})
)
]
})
)
Insert cell
Insert cell
C = [
[0, 1],
[1, 1],
[1, 0],
[1, -1],
[1, -1],
[2, -1]
]
Insert cell
visibility().then(() =>
Plot.plot({
inset: 20,
grid: true,
marks: [
Plot.line(C, {
curve: "catmull-rom",
marker: true,
stroke: "orange"
}),
Plot.line(
C,
decimateX({
curve: "catmull-rom",
marker: true,
stroke: "steelblue",
mixBlendMode: "multiply"
})
)
]
})
)
Insert cell
Insert cell
Insert cell
Insert cell
Plot.plot({
grid: true,
marks: [
Plot.lineY(
data,
decimateX({ pixelSize, marker: true, curve: curve2 ?? undefined })
),
Plot.ruleX(
data,
decimateX({
pixelSize,
x: (d, i) => i,
y1: Plot.identity,
strokeWidth: 0.5,
strokeDasharray: 3
})
)
]
})
Insert cell
Insert cell
Plot.plot({
grid: true,
marks: [
Plot.lineX(
data,
decimateY({ pixelSize, marker: true, curve: curve2 ?? undefined })
)
]
})
Insert cell
Plot.lineY(aapl, decimateX({ x: "Date", y: "Close" })).plot({
height: 40,
width: 100,
axis: null
}).innerHTML.length
Insert cell
Plot.lineY(aapl, { x: "Date", y: "Close" }).plot({
height: 40,
width: 100,
axis: null
}).innerHTML.length
Insert cell
Plot.lineY(
d3.cumsum({ length: 1_000_000 }, d3.randomNormal()),
decimateX()
).plot().innerHTML.length
Insert cell
Plot.lineY(d3.cumsum({ length: 1_000_000 }, d3.randomNormal())).plot().innerHTML
.length
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more