Unlisted
Edited
Oct 12, 2023
1 fork
Importers
20 stars
Insert cell
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.barY(data, { x: "year", y: "sales", fill: "#ccc" }),

(_, { x }, __, dimensions) =>
Plot.plot({
...dimensions,
marks: [
Plot.line(data, {
x: "year",
y: "efficiency",
stroke: "steelblue",
strokeWidth: 4
}),
Plot.dot(data, {
x: "year",
y: "efficiency",
stroke: "steelblue",
r: 4,
strokeWidth: 2,
fill: "white"
})
],
x: {
type: "identity",
transform: (v) => x(v) + x.bandwidth() / 2,
axis: null
}, // reuse scale x.
y: { axis: "right", nice: true, line: true }
})
],

marginLeft: 70,
marginRight: 50,
marginBottom: 50,
width: Math.min(width, 780),
height: 400,

x: { tickRotate: -45, tickFormat: "" },
y: { axis: "left" }
})
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.barY(data, { x: "year", y: "sales", fill: "#ccc" }),

() =>
Plot.plot({
// dimensions
marginLeft: 70,
marginRight: 50,
marginBottom: 50,
width: Math.min(width, 780),
height: 400,

marks: [
Plot.line(data, {
x: "year",
y: "efficiency",
stroke: "steelblue",
strokeWidth: 4
}),
Plot.dot(data, {
x: "year",
y: "efficiency",
stroke: "steelblue",
r: 4,
strokeWidth: 2,
fill: "white"
})
],
x: { type: "band", axis: null },
y: { axis: "right", nice: true, line: true }
})
],

marginLeft: 70,
marginRight: 50,
marginBottom: 50,
width: Math.min(width, 780),
height: 400,

x: { tickRotate: -45, tickFormat: "" },
y: { axis: "left" }
})
Insert cell
Insert cell
multiLayerX(
{
marginLeft: 70,
marginRight: 50,
marginBottom: 50,
width: Math.min(width, 780),
height: 400,

x: { tickRotate: -45, tickFormat: "" },
y: { axis: "left" }
},

// base mark
Plot.barY(data, { x: "year", y: "sales", fill: "#ccc" }),

// second layer
(x) => [
Plot.line(data, {
x: "year",
y: "efficiency",
stroke: "steelblue",
strokeWidth: 4
}),
Plot.dot(data, {
x: "year",
y: "efficiency",
stroke: "steelblue",
r: 4,
strokeWidth: 2,
fill: "white"
})
]

// ... more layers
)
Insert cell
function multiLayerX(config, base, ...layers) {
const A = Plot.plot({
...config,
marks: [base]
});
const x = A.scale("x");

return Plot.plot({
...config,
marks: [
base,
...layers.map((l) => () =>
Plot.plot({
...config,

marks: [l(x)],
x: { ...x, axis: null },
y: { axis: "right", line: true, nice: true }
})
)
]
});
}
Insert cell
Insert cell
{
const height = 400,
marginBottom = 50,
marginTop = 20,
marginLeft = 70;
const extent2 = d3.extent(data, (d) => d.efficiency);
const second = d3.scaleLinear(extent2, [height - marginBottom, marginTop]); // TODO: nice()
const remap = d3.scaleLinear(extent2, [
0 /* mapped to height - marginBottom */,
d3.max(data, (d) => d.sales) /* mapped to marginTop */
]);
const w = Math.min(width, 780);

return Plot.plot({
marginLeft,
marginRight: 50,
marginBottom,
marginTop,
width: w,
height,

x: { tickRotate: -45, tickFormat: "" },
y: { axis: "left" },
// base mark
marks: [
Plot.barY(data, { x: "year", y: "sales", fill: "#ccc" }),

[
() =>
d3
.create("svg:g")
.attr("transform", `translate(${w - marginLeft + 10})`)
.call(d3.axisRight(second))
.call((g) =>
g
.append("text")
.text("↑ efficiency")
.attr("fill", "steelblue")
.attr("transform", `translate(0,${marginTop - 10})`)
)
.node(),
Plot.line(data, {
x: "year",
y: (d) => remap(d.efficiency),
stroke: "steelblue",
strokeWidth: 2.5,
marker: "circle"
})
]
]
});
}
Insert cell
data = FileAttachment("new-passenger-cars.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