Unlisted
Edited
Sep 23, 2024
1 fork
4 stars
Insert cell
Insert cell
toc()
Insert cell
Insert cell
Insert cell
Insert cell
Plot.line(sftemp, {
x: "date",
y: {
transform: (data) => {
const values = Plot.valueof(data, "high");
const mean = d3.mean(values);
const deviation = d3.deviation(values);
return values.map((d) => (d - mean) / deviation);
}
},
stroke: "steelblue"
}).plot({
y: { grid: true, label: "σ" },
marks: [Plot.ruleY([0])]
})
Insert cell
Insert cell
Insert cell
Insert cell
{
class Dot2 extends Plot.Dot {
filter(index, channels, values) {
return index.filter((i) => values.r[i] > 3);
}
}
const MyMark = useFilter ? Dot2 : Plot.Dot;
return new MyMark(sftemp, {
x: "date",
y: "high",
r: "low"
}).plot();
}
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.frame(),
Plot.dot(sftemp, {
x: "date",
y: "high",
fill: "low"
}),
() => htl.svg`<image x="530" y="30" width="80" height="80" xlink:href="https://upload.wikimedia.org/wikipedia/commons/2/22/SVG_Simple_Logo.svg">`
]
})
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.frame(),
Plot.dot(sftemp, {
x: "date",
y: "high",
fill: "gray"
}),

// position the logo wrt the frame’s dimensions
(index, scales, values, dimensions, context) => {
const size = 80; // logo image size
const inset = 10; // separation between logo and frame
const x = dimensions.width - dimensions.marginRight;
const y = dimensions.marginTop;
return htl.svg`<image x=${x - size - inset} y=${y + inset} width=${size} height=${size} xlink:href="https://upload.wikimedia.org/wikipedia/commons/2/22/SVG_Simple_Logo.svg">`
},

// position the pointing finger wrt the scales
(index, scales, values, dimensions, context) => {
const x = scales.x(new Date("2011-04-01"));
const y = scales.y(45.5);
return svg`<g transform="translate(${x},${y})"><text text-anchor="start" font-size=20>👈 YOU ARE HERE`;
}
]
})
Insert cell
Insert cell
Insert cell
function envelopes(data, { k = 2, n, ...options } = {}) {
return Plot.marks(
d3.ticks(-1, 1, 21)
.map((j) =>
Plot.lineY(
data,
Plot.map(
{ y: Plot.bollinger({ k: k * j, n }) },
{ strokeWidth: 1 - 0.95 * Math.abs(j) ** 0.7, ...options }
)
)
)
);
}
Insert cell
Plot.plot({
marks: [
Plot.frame(),
envelopes(sftemp, {
k: 1.5,
x: "date",
y: "high"
}),
Plot.dot(sftemp, {
fill: "brown",
r: 1.5,
x: "date",
y: "high"
})
]
})
Insert cell
Insert cell
Insert cell
Insert cell
Plot.plot({
marks: [
() => htl.svg`<defs>
<linearGradient id="gradient" gradientTransform="rotate(90)">
<stop offset="15%" stop-color="purple" />
<stop offset="75%" stop-color="red" />
<stop offset="100%" stop-color="gold" />
</linearGradient>
</defs>`,
Plot.barY(alphabet, {x: "letter", y: "frequency", fill: "url(#gradient)"}),
Plot.ruleY([0])
]
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Plot.plot({
width: 560,
height: 280,
inset: 10,
projection: ({ width, height }) => d3.geoAirocean().fitSize([width, height], { type: "Sphere" }),
marks: [
Plot.graticule(),
Plot.sphere(),
Plot.geo(land, { fill: "currentColor" })
]
})
Insert cell
Insert cell
Insert cell
Plot.line(
sftemp,
Plot.mapY(
(values) => {
const mean = d3.mean(values);
const deviation = d3.deviation(values);
return values.map((d) => (d - mean) / deviation);
},
{
x: "date",
y: "high",
stroke: "steelblue"
}
)
).plot({
y: { grid: true, label: "σ" },
marks: [Plot.ruleY([0])]
})
Insert cell
Insert cell
Plot.barX(
penguins,
Plot.groupY(
{
x: (groupData) => groupData.length // equivalent of "count"!
},
{ y: "species" }
)
).plot()
Insert cell
Plot.barX(
penguins,
Plot.groupY(
{
x: (groupValues) => new Set(groupValues).size // equivalent of "distinct"!
},
{ x: "island", y: "species" }
)
).plot()
Insert cell
Insert cell
Plot.barX(
penguins,
Plot.groupY(
{
x: "count",
title: (groupData) => `Islands:\n${[...new Set(groupData.map(d => d.island))].join("\n")}`
},
{ y: "species", tip: true }
)
).plot()
Insert cell
Insert cell
Plot.barX(
penguins,
Plot.binX(
{
fill: "count",
title: (data, {x1, x2}) => `The ${x1}—${x2} bin\nincludes ${data.length} penguins.`
},
{ x: "body_mass_g", tip: true }
)
).plot({color: {scheme: "Blues"}, marginBottom: 35})
Insert cell
Insert cell
Plot.rectY(
penguins,
Plot.binX(
{
fill: "count",
y: {
reduceIndex: (I, S) => {
const index = new Set(I); // for faster look-ups
return (
d3.median(I, (i) => S[i]) /
d3.median(S, (d, i) => (index.has(i) ? NaN : d))
);
}
}
},
{ x: "body_mass_g", y: "body_mass_g", tip: true }
)
).plot({
y: { label: "body mass (relative median)" },
color: { scheme: "Blues" },
marginBottom: 35
})
Insert cell
Insert cell
Plot.plot({
y: { grid: true, nice: true },
marks: [
Plot.dot(sftemp, { x: "date", y: "high", fill: "currentColor", r: 1.5 }),
Plot.line(
sftemp,
Plot.windowY({
k: 10,
x: "date",
y: "high",
stroke: "red",
strokeWidth: 0.8
})
),
Plot.line(
sftemp,
Plot.windowY({
k: 10,
reduce: (values) => d3.mean(d3.sort(values).slice(2, -2)),
x: "date",
y: "high",
stroke: "steelblue"
})
)
]
})
Insert cell
Insert cell
Insert cell
Insert cell
toCelsius = (f) => (f - 32) * (5 / 9)
Insert cell
Insert cell
Plot.plot({
y: { grid: true, transform: toCelsius, label: "temp. (high, °C)" },
color: { legend: true, transform: toCelsius, label: "temp. (low, °C)" },
marks: [Plot.dot(sftemp, { x: "date", y: "high", fill: "low", tip: true })]
})
Insert cell
Insert cell
Plot.plot({
fx: {transform: country => ["Peru", "Ecuador"].includes(country) ? "Americas" : "Africa"},
marks: [
Plot.barY(
[
{ country: "South Africa", value: 14 },
{ country: "Zimbabwe", value: 19 },
{ country: "Peru", value: 17 },
{ country: "Ecuador", value: 4 },
{ country: "Nigeria", value: 8 }
],
{ fx: "country", fill: "country", y: "value", tip: true }
)
]
})
Insert cell
Insert cell
Insert cell
Plot.plot({
x: { transform: d3.utcWeek },
marks: [Plot.dot(sftemp.slice(-90, -10), { x: "date", y: "high" })]
})
Insert cell
Insert cell
oleron = d3.piecewise(Array.from("#192659#1b275b#1d295c#1e2a5e#202c5f#212d61#232f62#243064#263265#273367#293568#2a376a#2c386b#2d3a6d#2f3b6f#303d70#323e72#344073#354275#374376#384578#3a467a#3b487b#3d4a7d#3f4b7e#404d80#424e82#445083#455285#475387#485588#4a578a#4c588c#4d5a8d#4f5c8f#515d91#525f92#546194#566296#586497#596699#5b689b#5d699d#5e6b9e#606da0#626ea2#6470a3#6572a5#6774a7#6975a9#6b77aa#6c79ac#6e7bae#707cb0#727eb2#7380b3#7582b5#7784b7#7985b9#7a87ba#7c89bc#7e8bbe#808dc0#828ec2#8390c3#8592c5#8794c7#8996c9#8b97cb#8d99cd#8e9bce#909dd0#929fd2#94a1d4#96a3d6#98a4d7#99a6d9#9ba8db#9daadd#9facde#a1aee0#a3afe2#a5b1e4#a6b3e5#a8b5e7#aab7e8#acb9ea#aebaeb#afbcec#b1beed#b3bfee#b4c1f0#b6c3f0#b8c4f1#b9c6f2#bbc7f3#bcc9f4#becaf4#bfccf5#c0cdf5#c2cff6#c3d0f6#c5d1f7#c6d3f7#c7d4f8#c9d5f8#cad7f8#cbd8f9#cdd9f9#cedbf9#cfdcfa#d1ddfa#d2dffb#d3e0fb#d5e1fb#d6e3fc#d7e4fc#d9e5fc#dae7fd#dbe8fd#dde9fd#deebfe#dfecfe#e1edfe#e2efff#e3f0ff#e5f1ff#e6f3ff#194c00#1c4d00#1f4e00#224e00#244f00#275000#295100#2c5100#2e5200#315300#335300#355400#375500#395500#3c5600#3e5700#405700#425800#445900#465900#485a00#4a5b00#4d5b01#4f5c01#515d01#535e02#555f03#575f03#5a6004#5c6105#5e6207#606308#63640a#65650c#67670e#6a6810#6c6912#6e6a14#716c16#736d18#756e1a#78701c#7a711e#7c7221#7e7423#817525#837627#85782a#87792c#8a7b2e#8c7c30#8e7d33#907f35#928037#948239#97833c#99843e#9b8640#9d8742#9f8945#a18a47#a48c49#a68d4c#a88f4e#aa9050#ac9252#af9455#b19557#b39759#b5985c#b89a5e#ba9c60#bc9e62#bf9f65#c1a167#c3a369#c5a56c#c8a66e#caa871#ccaa73#cfac75#d1ae78#d3af7a#d5b17c#d8b37f#dab581#dcb784#deb986#e0ba89#e2bc8b#e4be8e#e6c090#e8c293#e9c495#ebc698#ecc89a#eec99d#efcb9f#f0cda2#f1cfa4#f2d1a7#f3d2a9#f4d4ac#f4d6ae#f5d7b0#f6d9b3#f6dbb5#f7dcb7#f7deba#f7e0bc#f8e1be#f8e3c0#f9e4c3#f9e6c5#f9e8c7#f9e9ca#faebcc#faecce#faeed1#fbf0d3#fbf1d5#fbf3d8#fcf5da#fcf6dc#fcf8df#fdfae1#fdfbe4#fdfde6".matchAll(/#\w+/g), ([d]) => d))
Insert cell
oleron(0.333) // a light blue color, at 1/3 of the [0, 1] domain
Insert cell
Insert cell
Plot.plot({
width: 500,
aspectRatio: 1,
color: {
interpolate: oleron,
type: "diverging",
legend: true,
ticks: 5
},
marks: [
Plot.contour({
x1: -1.5,
x2: 1.5,
y1: 2,
y2: 0,
fill: (x, y) => 0.05 + Math.atan2(y, x) * (y - x * x),
thresholds: 30
})
]
})
Insert cell
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.line(sftemp.slice(-90, -10), {
x: "date",
y: "high",
strokeWidth: 0.5,
curve: (context) => {
const curve = d3.curveCardinal(context);
const noise = d3.randomNormal.source(d3.randomLcg(42))(0, noiseAmplitude);
return {
point: (x, y) => curve.point(x + noise(), y + noise()),
lineStart: () => curve.lineStart(),
lineEnd: () => curve.lineEnd()
};
}
}),
Plot.dot(sftemp.slice(-90, -10), {
x: "date",
y: "high",
fill: "currentColor",
r: 1.5
})
]
})
Insert cell
Insert cell
Plot.rectY(
Array.from({ length: 400 }, d3.randomNormal.source(d3.randomLcg(42))(0, 3)),
Plot.binX(
{ y: "count" },
{
interval: {
floor: (d) => Math.floor(d + 0.5) - 0.5,
offset: (d, n = 1) => d + n,
range: d3.range
}
}
)
).plot()
Insert cell
Insert cell
Insert cell
Plot.dot(penguins.slice(120, 180), {
x: "body_mass_g",
y: "culmen_length_mm",
strokeWidth: 0.5,
stroke: "species",
r: (d, i) => i % 4,
symbol: {
draw: (context, size) => {
const n = 2 * Math.sqrt(size) + 1;
const r = 25;
for (let i = 0; i < 360; ++i) {
const a = (i * Math.PI) / 180;
const rho = r * Math.sin(n * a);
context[i === 0 ? "moveTo" : "lineTo"](rho * Math.cos(a), rho * Math.sin(a));
}
context.closePath();
}
}
}).plot({inset: 20, nice: true})
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.line(sftemp.slice(-90, -10), {
x: "date",
y: "high",
stroke: "red",
marker: (color) => svg`<marker viewBox="-4 -4 8 8" style="overflow:visible"><ellipse rx=20 ry=8 stroke=${color} fill=${color} fill-opacity=0.2 />`
}),
]
})
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.vector(sftemp.slice(-90, -10), {
x: "date",
y: "high",
length: 20,
anchor: "start",
r: 4,
shape: {
draw: (context, length, radius) => {
context.moveTo(0, -length / 2);
context.lineTo(0, length / 2);
context.arc(0, 0, radius, Math.PI / 2, 2.5 * Math.PI/2)
}
}
})
]
})
Insert cell
Insert cell
Plot.plot({
x: {
ticks: 20,
tickFormat: (d) => ["*", "M", "Tu", "W", "Th", "F", "Sa"][d.getDay()]
},
marks: [Plot.ruleX(sftemp.slice(-25), { x: "date" })]
})
Insert cell
Insert cell
Plot.plot({
title: md`## _Who cares about the actual chart?_`,
subtitle: htl.html`<p>when it has a <em style="border-bottom: 2px solid red">good</em> title`,
marks: [Plot.frame()],
caption: md`👆 Notice <i>anything</i> missing?`
}).outerHTML
Insert cell
Insert cell
Insert cell
Plot.raster(
airports,
Plot.geoCentroid({
fill: 1,
interpolate: function (index, width, height, X, Y, V) {
// Instantiate a spatial index with d3-quadtree, for faster lookups
const quadtree = d3.quadtree()
.x((i) => X[i])
.y((i) => Y[i])
.addAll(index);

// Create the output raster
const R = new Float32Array(width * height);

// For each point in the raster…
for (let x = 0; x < width; ++x) {
for (let y = 0; y < height; ++y) {
// …find the closest sample…
const i = quadtree.find(x, y);
// …and save the distance from the pixel to the closest sample
R[x + y * width] = Math.hypot(X[i] - x, Y[i] - y);
}
}
return R;
}
})
).plot({
projection: "albers-usa",
color: { scheme: "Cool", legend: true, domain: [0, 15], clamp: true }
})
Insert cell
import {data as airports} from "@d3/us-airports-voronoi"
Insert cell
Insert cell
Insert cell
Plot.raster(penguins, {
x: "culmen_length_mm",
y: "culmen_depth_mm",
fill: "species",
pixelSize: 0.5,
interpolate: Plot.interpolatorRandomWalk({
random: useBlue
? (() => {
const k = 64;
const blue = noise(k,k);
return (x, y, z) => {
x += 17 * z;
return (
blue[(Math.floor(x) % k) + k * (Math.floor(y) % k)] /
(k * k)
);
};
})()
: undefined
})
}).plot()
Insert cell
Insert cell
import {noise} from "@jobleonard/pseudo-blue-noise"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
replay;
await visibility();
const chart = Plot.dot(penguins, {
x: "culmen_length_mm",
y: "culmen_depth_mm",
fill: "species",
stroke: "white"
}).plot();
d3.select(chart)
.selectAll("circle")
.attr("r", 0)
.transition()
.delay(200)
.duration(1500)
.attr("r", 10);
return chart;
}
Insert cell
Insert cell
Insert cell
chartSpecies = Plot.tickX(penguins, {
x: "body_mass_g",
stroke: "species"
}).plot({ color: { scheme: "Category10", legend: true } })
Insert cell
chartMass = Plot.tickX(penguins, {x: "body_mass_g", stroke: "body_mass_g"}).plot({color: {scheme: "Reds", legend: true}})
Insert cell
combined = {
const colorMass = chartMass.scale("color").apply;
const colorSpecies = chartSpecies.scale("color").apply;
return Plot.plot({
marginLeft: 80,
marks: [
Plot.dot(penguins, {
x: "body_mass_g",
y: "species",
fill: (d) => colorSpecies(d["species"]),
dy: -3
}),
Plot.dot(penguins, {
x: "body_mass_g",
y: "species",
fill: (d) => colorMass(d["body_mass_g"]),
dy: 3
})
]
});
}
Insert cell
Insert cell
Insert cell
sftemp = FileAttachment("sf-temperatures.csv").csv({typed: true})
Insert cell
d3 = require("d3@7", "d3-geo-polygon@1")
Insert cell
countries = FileAttachment("countries-110m.json").json()
Insert cell
land = topojson.feature(countries, countries.objects.land)
Insert cell
import {toc} from "@mbostock/toc"
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