Public
Edited
Dec 4, 2023
Insert cell
Insert cell
Insert cell
viridis = d3.interpolateViridis;
Insert cell
{
const offset = 0.5;

return Plot.plot({
width: Math.max(width, 700),
marginBottom: 20,
projection: {
type: "azimuthal-equidistant",
rotate: [0, -90],
domain: d3.geoCircle().center([0, 90]).radius(1.22)()
},
facet: {
data: points,
x: "fx",
y: "fy",
axis: null
},
marks: [
//Facet name
Plot.text(points, Plot.selectFirst({text: "year", frameAnchor: "bottom", fontWeight: "400", fontSize: 14})),
//grey discs
Plot.geo([1.0, 0.8, 0.6, 0.4, 0.2], {
geometry: (r) => d3.geoCircle().center([0, 90]).radius(r)(),
stroke: "black",
fill: "black",
strokeOpacity: 0.2,
fillOpacity: 0.02,
strokeWidth: 0.5
}),

//white axes
Plot.link(longitude.domain(), {
x1: longitude,
y1: 90 - 0.8,
x2: 0,
y2: 90,
stroke: "white",
strokeOpacity: 0.5,
strokeWidth: 2.5
}),

//tick labels
Plot.text([0.4, 0.6, 0.8], {
fx: 0, fy: 0,
x: 180,
y: (d) => 90 - d,
dx: 2,
textAnchor: "start",
text: (d) => ( d == 0.8 ? `${100 * d}th percentile` : `${100 * d}th`),
fill: "currentColor",
stroke: "white",
fontSize: 12
}),

//axes labels
Plot.text(longitude.domain(), {
fx: 0, fy: 0,
x: longitude,
y: 90 - 1.07,
text: d => abbrScale(d),
lineWidth: 5,
fontSize: 12
}),

//axes labels, initials
Plot.text(longitude.domain(), {
fx: 0, fy: 0, facet: "exclude",
x: longitude,
y: 90 - 1.09,
text: d => "",
lineWidth: 5
}),
//areas
Plot.area(points, {
x1: ({ key, country }) => country === 'Sweden' ? longitude(key) - offset : longitude(key) + offset,
y1: ({ value }) => 90 - value,
x2: 0,
y2: 90,
fill: ({ country }) => country === 'Sweden' ? 'lightblue' : 'lightgreen',
fillOpacity: 0.25,
stroke: ({ country }) => country === 'Sweden' ? 'blue' : 'green',
curve: "cardinal-closed"
}),

//points
Plot.dot(points, {
x: ({ key, country }) => country === 'Sweden' ? longitude(key) - offset : longitude(key) + offset,
y: ({ value }) => 90 - value,
fill: ({ country }) => country === 'Sweden' ? 'blue' : 'green',
stroke: "white",
r: 3 // Increased radius for accentuation
}),

//interactive labels
Plot.text(
points,
Plot.pointer({
x: ({ key }) => longitude(key),
y: ({ value }) => 90 - value,
text: (d) => `${d.country === 'Sweden' ? 'Sweden' : 'United States'}: ${d.raw}\n(${Math.round(100 * d.value)}%)`,
textAnchor: "start",
dx: 4,
fill: "currentColor",
stroke: "white",
maxRadius: 10,
fontSize: 12
})
)
]
})
}
Insert cell
abbrScale = d3.scaleOrdinal(["TPP", "PPE", "MC", "TTL"], ["Trips/Person", "Passengers/Employee", "Maintenance/KM", "Track/Land"])
Insert cell
Insert cell
cars = FileAttachment("FinalDataTrim9.csv").csv({typed: true})
Insert cell
Insert cell
longitude = d3.scalePoint(new Set(Plot.valueof(points, "key")), [180, -180]).padding(0.5).align(1)
Insert cell
points = {
const points = d3.sort(cars, d => d.Price).flatMap(({ name, ...values }, i) =>
Object.entries(values).map(([key, raw]) => ({
name,
country: name.split(" ")[0],
year: name.split(" ")[1],
key,
raw,
fx: (1 + i%7) % 4, // trellis (facets); we leave facet <0,0> empty for the legend
fy: Math.floor((1 + i%7) / 4)
}))
);
for (const [, g] of d3.group(points, d => d.key)) {
const m = d3.max(g, d => d.raw);
for (const d of g) d.value = d.raw / m;
}
return points;
}
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