Unlisted
Edited
Sep 27, 2024
3 stars
Insert cell
Insert cell
viewof s2 = Plot.plot({
title: `Per-client latency over time`,
subtitle: `Hover to highlight the other quantiles for the same instance.`,
inset: 10,
marks: [
Plot.frame({ fill: "#f3f3f3" }),
Plot.gridX({ stroke: "#fff", strokeOpacity: 1 }),
Plot.gridY({ stroke: "#fff", strokeOpacity: 1 }),

Plot.line(data, {
x: "second",
y: "latencyMs",
z: "instance",
fy: "quantile",
stroke: "#ccc",
className: "linked",
sort: { fy: { reverse: true } },
tip: {
render: (index, scales, values, dimensions, context, next) => {
d3.select(context.ownerSVGElement)
.selectAll(".linked path")
.style("stroke", null)
.filter(([i]) => values.z[i] === values.z[index[0]])
.raise()
.style("stroke", "red");

// comment this out if you don't actually want to display the tip
return next(index, scales, values, dimensions, context);
}
}
})
]
})
Insert cell
s3
Insert cell
viewof s3 = Plot.plot({
title: `Per-client latency over time (dots edition)`,
subtitle: `Hover to highlight the other quantiles for the same instance.`,
inset: 10,
marks: [
Plot.frame({ fill: "#f3f3f3" }),
Plot.gridX({ stroke: "#fff", strokeOpacity: 1 }),
Plot.gridY({ stroke: "#fff", strokeOpacity: 1 }),

Plot.dot(data, {
x: "second",
y: "latencyMs",
fy: "quantile",
r: 1,
fill: "#ccc"
}),

Plot.dot(data, {
x: "second",
y: "latencyMs",
fy: "quantile",
r: 1,
fill: "#f00",
className: "linked",
sort: { fy: { reverse: true } },
channels: { instance: "instance" },
tip: {
render: (index, scales, values, dimensions, context, next) => {
const instance = values.instance;
const key = instance[index[0]];
d3.select(context.ownerSVGElement)
.selectAll(".linked circle")
.attr("fill-opacity", (i) => +(instance[i] === key));
return next(index, scales, values, dimensions, context);
}
}
Insert cell
s
Insert cell
viewof s = Plot.plot({
inset: 10,
marks: [
Plot.line(data, {
x: "second",
y: "latencyMs",
z: (d) => `${d.instance}/${d.quantile}`,
fy: "quantile",
stroke: "#ccc"
}),

Plot.line(data, {
x: "second",
y: "latencyMs",
z: (d) => `${d.instance}/${d.quantile}`,
stroke: "red",
fy: "quantile",
pointerEvents: "none",
xstrokeOpacity: 0,
channels: {
key: "instance",
facetY: "quantile",
instance: "instance"
},
render(index, scales, values, dimensions, context, next) {
const { ownerSVGElement, dispatchValue } = context;
const g = next(index, scales, values, dimensions, context);
const path = d3
.select(g)
.call((g) => {
g.insert("rect", "*")
.attr("x", dimensions.marginLeft)
.attr("y", dimensions.marginTop)
.attr(
"width",
dimensions.width -
Insert cell
{
console.clear();
let initializing = true; // set to false after the plot is initially computed
let hoverIndex = null;
const plot = Plot.plot({
marks: [
Plot.line(data, {
x: "second",
y: "latencyMs",
z: (d) => `${d.instance}/${d.quantile}`,
fy: "quantile",
stroke: "#ccc"
}),

Plot.line(
data,
Plot.pointer({
x: "second",
y: "latencyMs",
stroke: "red",
fy: "quantile",
channels: {
// todo: a link channel
key: "instance",
facetY: "quantile",
instance: "instance"
},
render(index, scales, values, dimensions, context, next) {
const { ownerSVGElement, document } = context;
ownerSVGElement.__cache ??= {};
const cache = ownerSVGElement.__cache;
hoverIndex = index;
if (initializing) {
console.log("init", index);
cache[index.fy] = svg`<g><g>`;

Insert cell
// Original attempt
Plot.plot({
marks: [
Plot.line(data, {
x: "second",
y: "latencyMs",
z: (d) => `${d.instance}/${d.quantile}`,
fy: "quantile",
stroke: (d) => `#ccc`
}),

Plot.line(
data,
Plot.pointer({
x: "second",
y: "latencyMs",
z: (d) => `${d.instance}/${d.quantile}`,
stroke: "red",
fy: "quantile",
channels: {
instance: "instance"
},
render(index, scales, values, dimensions, context, next) {
console.clear();
if (index.length > 0) {
// console.log("inst", values.channels.instance.value);
const zs = values.channels.instance.value;
const z = zs[index[0]];
index = d3.range(zs.length).filter((j) => {
return zs[j] === z;
});
}
return next(index, scales, values, dimensions, context);
}
})
)
Insert cell
// todo: prevent double-updating the facet that was just hovered
Insert cell
function unique(xs, f = identity) {
const set = uniqueSet(xs, f);
return Array.from(set);
}
Insert cell
function uniqueSet(xs, f = identity) {
const set = new Set();
for (const x of xs) set.add(f(x));
return set;
}
Insert cell
function identity(x) {
return x;
}
Insert cell
data = FileAttachment("data (7).json").json()
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