Public
Edited
Dec 18, 2023
6 forks
Importers
43 stars
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
// Use a scheme to choose the colors
Plot.legend({
color: {
type: "categorical",
domain: d3.range(10).map((d) => `Category ${d + 1}`),
scheme: "accent" // use the "accent" scheme
}
})
Insert cell
// Manually set the colors using the `range`
Plot.legend({
color: {
type: "categorical",
domain: d3.range(10).map(d => `Category ${d + 1}`),
range: ["green", "purple", "orange", "yellow", "blue", "pink", "brown", "grey", "green", "lavender"]
}
})
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
Inputs.table(data)
Insert cell
Insert cell
Insert cell
Insert cell
import {
createDemo,
intro,
plotWidth,
welcomeBox,
download,
inputStyles,
deck
} with { data, evalCodeStr } from "@observablehq/cheatsheet-utilities"
Insert cell
import {toc} from "@nebrius/indented-toc"
Insert cell
Insert cell
denverRaw = await FileAttachment("denver.csv").csv({typed: true})
Insert cell
Insert cell
data = aq.from(denverRaw)
.derive({tempBin: aq.escape(d => `${floor10(d.air_temp)} - ${floor10(d.air_temp) + 4}`)})
Insert cell
// Domain for the temperature bins
domain = data
.select("tempBin")
.dedupe()
.objects()
.sort((a, b) => +a.tempBin.split(" -")[0] - +b.tempBin.split(" -")[0])
.map((d) => d.tempBin)
Insert cell
Insert cell
function floor10(x)
{
return Math.floor(x/10)*10;
}
Insert cell
evalCodeStr = (str) => {
const func = new Function("data", "Plot", "width", "d3", "domain", `return ${str}`)
return func(data, Plot, width, d3, domain);
}
Insert cell
introParagraph = () => htl.html`<p style="margin-top:0px"><a href ="https://observablehq.com/@observablehq/plot?collection=@observablehq/plot" target="_blank">Observable Plot</a> can encode data values using a color channel. This allows you to express quantitative or qualitative data through each <a href="https://observablehq.com/@observablehq/plot-marks?collection=@observablehq/plot" target="_blank">mark's</a> <em>fill</em> or <em>stroke</em>. To configure the relationship between your input data and the output colors, set the color channel's <em>scheme</em> or <em>range</em> in your <code>Plot.plot()</code> function.</p>`
Insert cell
showScheme = (scheme, {w = width/2} = {}) => {
const categoricalLegend = Plot.legend({
color: {
scheme: scheme,
label: scheme,
ticks: 0,
tickFormat: (d) => "",
type: "categorical",
domain: d3.range(nCategories)
},
swatchWidth: w / nCategories,
swatchHeight: 25,
width: w
});
const continuousLegend =
schemes.categorical.indexOf(scheme) !== -1
? htl.html`<div style=${{ ...labelStyles }}>${scheme}</div>`
: Plot.legend({
color: {
scheme: scheme,
label: scheme,
ticks: 0,
tickFormat: (d) => null,
type: "linear"
},
columns: null,
height: 65,
width: w
});

return htl.html`<div class="schemeWrapper"><span style="margin-bottom:-21px;">${continuousLegend}</span>${categoricalLegend}</div>`;
}
Insert cell
Insert cell
quantitative = ({
controls: [
{ type: "text", value: "// Temp. over time\nPlot.plot({" },
{ type: "text", value: "marks:[Plot.dot(data, {", indent: 1 },
{ param: "fill", value: "air_temp" },
{ type: "text", value: "})],", indent: 1 },
{ type: "text", value: "color: {", indent: 1 },
{ param: "scheme", type: "select", value: "scheme", options: scheme },
{ param: "type", type: "select", options: quantScaleTypes },
{ type: "text", value: "// Clamp the domain", indent: 2 },
{ param: "domain", type: "textInput", value: `[0, 30]` },
{ type: "text", value: `// Restrict output range`, indent: 2 },
{ param: "range", type: "textInput", value: `[0, 1]` },
{ type: "text", value: "}})" }
],
plot: (config) => `Plot.plot({
marks: [Plot.dot(data, { x: "date", y: "air_temp", fill: "air_temp" })],
color: {
type: "${config.type}",
scheme: "${config.scheme}",
legend: true${config.domain ? `,\n\t\tdomain: ${config.domain}` : ''}${config.range ? `,\n\t\trange: ${config.range}` : ""}
},
width: ${plotWidth}
})`
})
Insert cell
diverging = ({
controls: [
{ type: "text", value: "// Diverging temp.\nPlot.plot({" },
{ type: "text", value: "marks:[Plot.dot(data, {", indent: 1},
{ type: "text", value: `fill: "air_temp"`, indent: 2},
{ type: "text", value: "})],", indent: 1},
{ type: "text", value: "color: {", indent: 1 },
{ param: "scheme", type: "select", value: "scheme", options: divergingScheme},
{ param: "type", type: "select", options: divergingScaleTypes },
{ param: "pivot", type: "range", min: -25, max: 25, step: 1 },
{ param: "symmetric", type: "toggle", value: true},
{ type: "text", value: "}})" }
],
plot: (config) => `Plot.plot({
marks: [
Plot.dot(data, { x: "date", y: "air_temp", fill: "air_temp" }),
Plot.ruleY([${config.pivot}], { stroke: "#d3d3d3" })
],
color: {
type: "${config.type}",
scheme: "${config.scheme}",
legend: true,
pivot: ${config.pivot},
symmetric: ${config.symmetric}
},
width: ${plotWidth}
})`
})
Insert cell
threshold = ({
controls: [
{ type: "text", value: "// Color thresholds\nPlot.plot({" },
{ type: "text", value: "marks:[Plot.dot(data, {", indent: 1 },
{ type: "text", value: `fill: "air_temp"`, indent: 2 },

{ type: "text", value: "})],", indent: 1 },
{ type: "text", value: "color: {", indent: 1 },
{ param: "type", value: "threshold" },
{ param: "scheme", type: "select", value: "scheme", options: scheme },
{
param: "domain",
type: "select",
options: ["[-10, 0, 10]", "d3.range(-15, 25, 10)", "d3.range(-20, 25, 5)"],
value: d3.range(-15, 25, 10)
},
{ type: "text", value: "}})" }
],
plot: (config) => `Plot.plot({
marks: [
Plot.dot(data, { x: "date", y: "air_temp", fill: "air_temp" }),
Plot.ruleY(${config.domain}, { stroke: "#D3D3D3" })
],
color: {
type: "threshold",
domain: ${config.domain},
scheme: "${config.scheme}",
reverse: true,
legend: true
},
width: ${plotWidth}
})`
})
Insert cell
ordinal = ({
controls: [
{ type: "text", value: "// Ordered categories\nPlot.plot({" },
{ type: "text", value: "marks:[Plot.dot(data, {", indent: 1},
{ type: "text", value: `fill: "tempBin"`, indent: 2},
{ type: "text", value: "})],", indent: 1},
{ type: "text", value: "color: {", indent: 1 },
{ param: "scheme", type: "select", value: "scheme", options: ordinalScheme},
{ type: "text", value: "}})" }
],
plot: (config) => `Plot.plot({
marks: [Plot.dot(data, { x: "date", y: "air_temp", fill: "tempBin" })],
color: {
domain: domain,
scheme: "${config.scheme}",
legend: true,
tickFormat: (d) => d + "°c"
},
width: ${plotWidth}
})`
})
Insert cell
categorical = ({
controls: [
{ type: "text", value: "// Categorical colors\nPlot.plot({" },
{ type: "text", value: "marks:[Plot.dot(data, {", indent: 1},
{ type: "text", value: `fill: "month"`, indent: 2},
{ type: "text", value: "})],", indent: 1},
{ type: "text", value: "color: {", indent: 1 },
{ param: "scheme", type: "select", value: "tableau10", options: schemeMap.get("categorical")},
{ type: "text", value: `type: "categorical"`, indent: 2},
{ type: "text", value: "}})" }
],
plot: (config) => `Plot.plot({
marks: [
Plot.dot(data, {
x: "date",
y: "air_temp",
fill: (d) => d3.utcMonth(d.date)
})
],
color: {
type: "categorical",
scheme: "${config.scheme}",
legend: true,
tickFormat: ${(d) => d3.utcFormat("%b")(d)},
columns: "50px"
},
width: ${plotWidth}
})`
})
Insert cell
divergingScaleTypes = [
"diverging",
"diverging-log",
"diverging-pow",
"diverging-sqrt",
"diverging-symlog"
]
Insert cell
schemeMap = new Map(Object.entries(schemes).map(([key, value]) => [key, value]))
Insert cell
quantSchemeMap = new Map(
Object.entries(schemes)
.filter(([key]) => key !== "categorical" && !key.match("iverging"))
.map(([key, value]) => [key, value])
)
Insert cell
divergingSchemeMap = new Map(
Object.entries(schemes)
.filter(([key]) => key.match("iverging"))
.map(([key, value]) => [key, value])
)
Insert cell
quantScaleTypes = [
"linear",
"sqrt",
"pow",
"log",
"symlog",
"sequential",
"cyclical",
"quantile"
]
Insert cell
schemes = ({
categorical: [
"accent",
"category10",
"dark2",
"paired",
"pastel1",
"pastel2",
"set1",
"set2",
"set3",
"tableau10"
],
diverging: [
"brbg",
"prgn",
"piyg",
"puor",
"rdbu",
"rdgy",
"rdylbu",
"rdylgn",
"spectral"
],
reversedDiverging: ["burd", "buylrd"],
singleHue: ["blues", "greens", "greys", "oranges", "purples", "reds"],
multiHue: [
"turbo",
"viridis",
"magma",
"inferno",
"plasma",
"cividis",
"cubehelix",
"warm",
"cool",
"bugn",
"bupu",
"gnbu",
"orrd",
"pubu",
"pubugn",
"purd",
"rdpu",
"ylgn",
"ylgnbu",
"ylorbr",
"ylorrd"
],
cyclical: ["rainbow", "sinebow"]
})
Insert cell
Insert cell
<style>
.schemeWrapper span::before {
margin-right:0px !important;
}
.schemeWrapper span {
margin-right:0px !important;
}
.schemeWrapper div {
margin-top: ${showSchemes[0] === "accent" ? "" : "-22px"};
}
</style>
Insert cell
labelStyles = ({
fontFamily:"system-ui, sans-serif",
fontSize: "10px",
"font-variant": "tabular-nums",
width:"65px",
"font-weight":700
})
Insert cell
inputStyles
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