Public
Edited
Feb 20, 2024
16 forks
36 stars
Plot: A few custom time axesAccess to Family planningPlot: MarimekkoAutoplot matrixWhat do people buy on Bandcamp?Plot: colorContrast transformA Timeline of Democratic Presidential CandidatesPlot: Diverging stacked barsPlot: Horizon ChartPlot: Ridgeline PlotTwo-Tone Pseudo Color Scales with Observable Plot & Vega-LiteRecreating Östling’s regression visualizationsError barsPlot: regressionSkies PlotHorizon graph & Barcode with PlotTemporal AliasingPlot for mathematiciansPlot Isotype dot plotCOVID-19 Netherlands Reproduction Number TutorialPlot Mountain SunsetSpine chartsGreenhouse gas emission projectionsRaincloud Plots with Observable PlotUpset Plots with Observable PlotExploring CIELChabNature variant waves graphicWhen Presidents Fade AwayThe Coronavirus landscapeOur World In Data charts visualisationThe normal model
Visualizing The New York Times’s Mini Crossword
Distributions and summary statistics - a collection of Plot examplesVariants of SARS-Cov-2 in EuropeLaid off: 6 million jobs and countingConcentration values vs. TimeTopological subsamplingPlot of plotsCyclical time scale, v2Bullet graph IIPlot: Bullet graphCyclical time scaleRoad traffic under COVID restrictions (France)K-means binningMatrix diagramELD ViewerHappy anniversary, Project Gutenberg!ROC curvePlot stargazer historyPlot HyperboloidPhone bar plotsLog-scale histogramDatabase Outage PlotBertin’s hotelIreland’s top trade partnersBump chartBubble MatrixRenko ChartGitHub BurndownPlot: Grid choroplethCandlestick Chart
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
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tokens = [
// ["annie", "ANNIE_TOKEN_HERE"],
// ["bryan", "BRYAN_TOKEN_HERE"],
// ["cobus", "COBUS_TOKEN_HERE"]
]
Insert cell
Insert cell
Insert cell
// Show liveData if tokens array is filled out, or
// show savedData plus your data if input is filled out, or
// show savedData (the default when you open the page)
data = tokens.length
? liveData
: yourToken
? [...savedData, ...(await getData([yourName, yourToken]))]
: savedData
Insert cell
savedData = (await FileAttachment("data@3.json").json()).map((d) => {
d.puzzle.date = utcParse(d.puzzle.print_date);
return d;
})
Insert cell
liveData = (refresh, (await Promise.all(tokens.map(getData))).flat())
Insert cell
getData = async ([name, token]) =>
(await solutions(token, "mini")).map((solution) => ({
...solution,
name
}))
Insert cell
Insert cell
adjusted = {
// straightforward: opened, solved
// adjustable: checked, cleared
const confusing = new Set(["revealed", "timerReset"]);
return data
.filter((d) => !Object.keys(d.firsts).some((s) => confusing.has(s)))
.map((d) => {
// match up secondsSpentSolving and the maximum timestamp and extrapolate backwards
const delta =
d.calcs.secondsSpentSolving - d3.max(d.board.cells, (c) => c.timestamp);
return {
...d,
board: {
cells: d.board.cells.map((c) =>
c.blank
? c
: {
...c,
timestamp: c.timestamp + delta
}
)
}
};
});
}
Insert cell
Insert cell
confusing = {
const confusing = new Set(["revealed", "timerReset"]);
return data.filter((d) =>
Object.keys(d.firsts).some((s) => confusing.has(s))
);
}
Insert cell
Insert cell
solutions = async (token, type) =>
Promise.all(
(await puzzles(token, type))
.filter((d) => d.solved)
.map((puzzle) =>
nyt(
`svc/crosswords/v6/game/${puzzle.puzzle_id}.json`,
token,
localStorage
).then((json) => ({ ...json, puzzle }))
)
)
Insert cell
Insert cell
puzzles = async (token, type) =>
(
await Promise.all(
quarters.map(([start, end]) =>
nyt(
`svc/crosswords/v3/undefined/puzzles.json?publish_type=${type}&date_start=${start}&date_end=${end}`,
token
).then((json) => json.results)
)
)
)
.flat()
.map((puzzle) => ({ ...puzzle, date: utcParse(puzzle.print_date) }))
Insert cell
utcParse = d3.utcParse("%Y-%m-%d")
Insert cell
utcFormat = d3.utcFormat("%Y-%m-%d")
Insert cell
Insert cell
quarters = {
const dateFormat = d3.utcFormat("%Y-%m-%d");
const starts = d3.utcMonth
.every(3)
.range(new Date("2021-01-01"), new Date("2022-01-01"));
return starts.map((start) =>
[start, d3.utcDay.offset(d3.utcMonth.offset(start, 3), -1)].map(dateFormat)
);
}
Insert cell
Insert cell
nyt = async (path, token, storage) => {
const key = String([path, token]);
if (storage && storage.getItem(key)) return JSON.parse(storage.getItem(key));
const json = await fetch(`https://nyt-games-prd.appspot.com/${path}`, {
headers: { "nyt-s": token }
}).then((res) => res.json());
if (storage) storage.setItem(key, JSON.stringify(json));
return json;
}
Insert cell
localStorage.length
Insert cell
Insert cell
frame = Plot.frame({ stroke: "lightgray" })
Insert cell
color = Plot.scale({color: {type:"ordinal", domain: names}})
Insert cell
tickFormat = (d) => {
const minutes = Math.floor(d / 60);
const seconds = d % 60;
return minutes === 0
? `${seconds}`
: seconds === 0
? `${minutes}m`
: `${minutes}:${seconds.toFixed(0).padStart(2, "0")}`;
}
Insert cell
function ordinalSuffix(n) {
switch (n % 100) {
case 11:
case 12:
case 13:
return `${n}th`;
}
switch (n % 10) {
case 1:
return `${n}st`;
case 2:
return `${n}nd`;
case 3:
return `${n}rd`;
default:
return `${n}th`;
}
}
Insert cell
Insert cell
Insert cell
import {localStorage} from "@mbostock/safe-local-storage"
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