Public
Edited
Nov 11, 2023
Paused
2 forks
16 stars
Insert cell
Insert cell
Plot.plot({
width: 800,
height: 700,
x: {
axis: "top",
type: "point",
domain: ["5 Year", "10 Year", "15 Year", "20 Year"],
inset: 60,
label: null
},
y: { axis: null, inset: 20 },
marks: [
Plot.line(cancer, { x: "year", y: "survival", z: "name" }),
d3
.groups(cancer, (d) => d.year)
.map(([year, cancer]) =>
Plot.text(
cancer,
occlusionY({
x: "year",
y: "survival",
text:
year === "5 Year"
? (d) => `${d.name} ${d.survival}`
: year === "20 Year"
? (d) => `${d.survival} ${d.name}`
: "survival",
textAnchor:
year === "5 Year"
? "end"
: year === "20 Year"
? "start"
: "middle",
fill: "currentColor",
stroke: "white",
strokeWidth: 5,
dx: year === "5 Year" ? -3 : year === "20 Year" ? 3 : 0
})
)
)
]
})
Insert cell
// OcclusionY adds an initializer that shifts nodes vertically with a tiny force simulation.
occlusionY = ({radius = 6.5, ...options} = {}) => Plot.initializer(options, (data, facets, { y: {value: Y}, text: {value: T} }, {y: sy}, dimensions, context) => {
for (const index of facets) {
const unique = new Set();
const nodes = Array.from(index, (i) => ({
fx: 0,
y: sy(Y[i]),
visible: unique.has(T[i]) // remove duplicate labels
? false
: !!unique.add(T[i]),
i
}));
d3.forceSimulation(nodes.filter((d) => d.visible))
.force("y", d3.forceY(({y}) => y)) // gravitate towards the original y
.force("collide", d3.forceCollide().radius(radius)) // collide
.stop()
.tick(20);
for (const { y, node, i, visible } of nodes) Y[i] = !visible ? NaN : y;
}
return {data, facets, channels: {y: {value: Y}}};
})
Insert cell
cancer = FileAttachment("cancer.csv").csv({ typed: true })
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