Public
Edited
Nov 11, 2023
6 forks
Importers
17 stars
Insert cell
Insert cell
Plot.plot({
height: 540,
x: {axis: "top", type: "ordinal", tickFormat: "", inset: 90, label: null},
y: {axis: null, inset: 20},
marks: [
Plot.line(receipts, {x: "year", y: "receipts", z: "country"}),
d3.groups(receipts, (d) => d.year === 1970)
.map(([left, receipts]) =>
Plot.text(receipts, occlusionY({
x: "year",
y: "receipts",
text: left
? (d) => `${d.country} ${d.receipts}`
: (d) => `${d.receipts} ${d.country}`,
textAnchor: left ? "end" : "start",
dx: left ? -3 : 3,
radius: 5.5
}))
)
],
caption: "Current receipts of government as a percentage of gross domestic product"
})
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
receipts = FileAttachment("gdp-receipts.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