Public
Edited
Jul 8
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
keyBindings
Insert cell
import { outcomePollingAverage as graph_11 } from "@ee2dev/plot-interaction-pointer-driven-marks"
Insert cell
Insert cell
<style>
/*
some extra styling to make it my own
I personally use the built-in serif fonts on Observable, but Roboto is popular...
*/
/* @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); */
.slide {
/* font-family: Roboto, sans-serif; */
}
</style>
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
outcomePollingAverage
Insert cell
Insert cell
Insert cell
Insert cell
graph_12 = {
return Plot.plot({
height: 500,
width: 800,
x: { nice: true },
marks: [
Plot.lineY(aapl, { x: "Date", y: "Close" }),
hair({
tickFormatX: "%Y\n%b",
tickFormatY: ".0f",
intervalX: "1 day",
intervalY: 1,
stroke: "red",
strokeOpacity: 1
})
]
});
}
Insert cell
graph3 = {
return Plot.plot({
height: 640,
marginLeft: 44,
color: {
scheme: "bupu",
type: "symlog"
},
marks: [
Plot.rect(
diamonds,
Plot.bin({ fill: "count" }, { x: "carat", y: "price", thresholds: 100 })
),
hair({
tickFormatX: (d) => (Math.floor(d * 20) / 20).toFixed(2),
tickFormatY: ",d"
})
]
});
}
Insert cell
graph_12b = {
return Plot.plot({
height: 640,
marginLeft: 44,
color: {
scheme: "bupu",
type: "symlog"
},
marks: [
Plot.rect(
diamonds,
Plot.bin({ fill: "count" }, { x: "carat", y: "price", thresholds: 100 })
),
hair({
tickFormatX: (d) => (Math.floor(d * 20) / 20).toFixed(2),
tickFormatY: ",d"
})
]
});
}
Insert cell
import { hair } from "@observablehq/plot-continuous-crosshair"
Insert cell
Insert cell
Insert cell
combined
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
normalize2 = {
return Inputs.radio(["max", "median", "mean", "one"], {
value: "median",
label: "normalize"
});
}
Insert cell
import {
staggered,
data as staggered_data
} from "@observablehq/plot-staggered-axis"
Insert cell
Insert cell
Insert cell
graph_15 = {
return Plot.plot({
marginLeft: 50,
width: 928,
y: {
grid: true,
label: "↑ Unemployed (thousands)"
},
marks: [
Plot.areaY(grouped_data, {
x: "date",
y: "unemployed",
fill: "industry",
sort: { color: "-y" }
}),
Plot.ruleX(
grouped_data,
Plot.pointerX(
Plot.binX({}, { x: "date", thresholds: 100, insetTop: 20 })
)
),
Plot.tip(
grouped_data,
Plot.pointerX(
Plot.binX(
{ title: (v) => v },
{
x: "date",
thresholds: 100,
render(index, scales, values, dimensions, context) {
const g = d3.select(context.ownerSVGElement).append("g");
const [i] = index;
if (i !== undefined) {
const data = values.title[i];
g.attr(
"transform",
`translate(${Math.min(
values.x1[i],
dimensions.width - dimensions.marginRight - 200
)}, 20)`
).append(() =>
Plot.plot({
marginTop: 14,
height: 85,
width: 195,
axis: null,
y: { domain: scales.scales.color.domain },
marks: [
Plot.frame({ fill: "white", stroke: "currentColor" }),
Plot.dot(data, {
y: "industry",
fill: (d) => scales.color(d.industry), // 👉 ensure color consistency
r: 5,
frameAnchor: "left",
symbol: "square2",
dx: 7
}),
Plot.text(data, {
y: "industry",
text: "industry",
frameAnchor: "left",
dx: 14
}),
Plot.text(data, {
y: "industry",
text: "unemployed",
frameAnchor: "right",
dx: -2
}),
Plot.text([data[0]], {
frameAnchor: "top-left",
dy: -13,
text: "date"
})
]
})
);
}
return g.node();
}
}
)
)
),
Plot.ruleY([0])
]
});
}
Insert cell
import { data as grouped_data } from "@observablehq/plot-grouped-tips"
Insert cell
Insert cell
Insert cell
vegaView = {
return embed(spec);
}
Insert cell
embed = require("vega-embed@7")
Insert cell
spec = ({
$schema: "https://vega.github.io/schema/vega-lite/v5.json",
width: 500,
height: 500,
data: { url: "https://vega.github.io/vega-datasets/data/barley.json" },
params: [
{
name: "ALX_SELECTION_click_FILTER",
select: { type: "point", encodings: ["color"], fields: ["site"] }
}
],
transform: [
{
calculate:
"if(ALX_SELECTION_click_FILTER.site && datum.site === ALX_SELECTION_click_FILTER.site[0], 0, if(datum.site === 'Grand Rapids', 1, 2))",
as: "siteOrder"
}
],
mark: "bar",
encoding: {
x: { aggregate: "sum", field: "yield" },
y: { field: "variety" },
color: { field: "site" },
order: { field: "siteOrder" }
}
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
modeValue = Generators.input(modeView)
Insert cell
selectionView = modeValue === "Filter categories"
? filterLegend(colorScale_demo.domain, colorScale_demo.range)
: radioButtonLegend(colorScale_demo.domain, colorScale_demo.range)
Insert cell
selectionValue = Generators.input(selectionView)
Insert cell
colorScale_demo = {
return Plot.plot({
inset: 8,
grid: true,
marks: [
Plot.dot(penguins, {
x: "flipper_length_mm",
y: "body_mass_g",
stroke: "island"
})
]
}).scale("color");
}
Insert cell
penguins_demo = penguins.filter((d) =>
modeValue === "Filter categories" ? selectionValue.includes(d.island) : true
)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
slider_22View = {
return inputTemplate(
Inputs.form(
linkRanges(
d3.pairs([0, 25, 50, 75, 100]).map((value, i) =>
interval([0, 100], {
value,
color: colorScale2.range[i],
// Hide values
// format: (d) => htl.html.fragment``,
step: 1,
width: 800
})
)
)
),
{ label: "% with home broadband", width: 200 }
);
}
Insert cell
slider_22Value = Generators.input(slider_22View)
Insert cell
thresholds_22 = [slider_22Value[1][0], slider_22Value[2][0], slider_22Value[3][0]]
Insert cell
import { interval } from "@mootari/range-slider@1786"
Insert cell
Insert cell
Insert cell
Insert cell
graph_31 = draw_buckets(bin25_demo, distributionsValue)
Insert cell
Insert cell
Insert cell
distributionsView = select_distribution("Normal", "")
Insert cell
Insert cell
Insert cell
import { bin25 as bin25_demo } with { chunksValue as chunks } from "@d3/d3-bin"
Insert cell
Insert cell
cat1
Insert cell
graph_32 = cat1
Insert cell
import { cat1, cat2 } from "@ee2dev/dynamic-top-n-and-binning"
Insert cell
Insert cell
cat2
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Plot.plot({
inset: 8,
grid: true,
// adjust the color scale in case of "One vs all"
color: {
domain: selectionMode === "One vs all" ? [selection] : colorScale.domain,
range:
selectionMode === "One vs all"
? [colorScale.range[colorScale.domain.indexOf(selection)]]
: colorScale.range,
unknown: "lightgrey"
},
marks: [
Plot.dot(penguins_new, {
x: "flipper_length_mm",
y: "body_mass_g",
stroke: "island",
stroke: "grey",
strokeWidth: 1,
fill: "island",
// fillOpacity: 0.5,
r: 4
})
]
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
colorScale2 = {
return Plot.plot({
projection: "albers-usa",
marks: [
Plot.geo(counties, {
fill: (d) => broadband_access_2018.get(d.id),
title: (d) =>
`${d.properties.name} County \n ${percent(
broadband_access_2018.get(d.id) // Custom tooltip text
)}`
}),
Plot.geo(states, { stroke: "#fff", strokeWidth: 0.5 })
],
color: {
scheme: "Spectral",
scheme: "purples",
unknown: "#ddd",
type: "linear",
type: "threshold",
label: "% of county population with home broadband",
legend: true,
percent: true,
domain: [25, 50, 75]
}
}).scale("color");
}
Insert cell
demo2
Insert cell
thresholds = [demo2[1][0], demo2[2][0], demo2[3][0]]
Insert cell
Insert cell
counties
Insert cell
Insert cell
Insert cell
Insert cell
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