Public
Edited
Jun 28, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = () => {
// Scaffold
const svg = d3.create("svg");
svg.append("style").html(css);
const g = svg.append("g");

// Axes
const xaxis = g.append("g")
.attr("class", "axis x-axis");

const yaxis = g.append("g")
.attr("class", "axis y-axis");

// Marks
const line = g.selectAll(".line")
.data(data)
.join("path")
.attr("class", "line")
.attr("stroke", d => color(d.lead_time));

const label = g.selectAll(".label")
.data(data)
.join("text")
.attr("class", "label")
.attr("fill", d => color(d.lead_time))
.html((d, i, e) => `<tspan>${d.lead_time}${i === e.length - 1 ? "-day</tspan><tspan>forecast" : " days"}</tspan>`)

return Object.assign(svg.node(), {
resize(ww) {
// Resize: Dimensions
const margin = { left: 33, right: 70, top: 4, bottom: 24};
const basewidth = Math.min(640, ww);
const width = basewidth - margin.left - margin.right;
const height = Math.max(300, basewidth * 9 / 16) - margin.top - margin.bottom;

// Resize: Scales
x.range([0, width]);
y.range([height, 0]);

// Resize: Axes
xaxisGenerator.tickSize(height + 10)
xaxis
// .attr("transform", `translate(0, ${height})`)
.call(xaxisGenerator);

yaxisGenerator.tickSize(width + 10)
yaxis
.attr("transform", `translate(${width})`)
.call(yaxisGenerator)

// Resize: Scaffold
svg
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);

g
.attr("transform", `translate(${[margin.left, margin.top]})`);

// Resize: Marks
line
.attr("d", d => lineGenerator(d.errors));

label
.attr("y", d => y(d.errors[d.errors.length - 1].error))
.selectAll("tspan")
.attr("dx", 5)
.attr("dy", (d, i) => 5 + i * 17)
.attr("x", width)
}
})
}
Insert cell
display.resize(width)
Insert cell
Insert cell
color = days => {
return (
days === 7 ? "#3870ab" :
days === 5 ? "#2da6b8" :
"#96d1b1"
);
}
Insert cell
css = `
.axis .domain {
display: none;
}
.axis .tick line {
stroke: #eaeaea;
}
.axis .tick text {
fill: #666666;
font-family: ${franklinLight};
font-size: 14px;
}

.line {
fill: none;
stroke-width: 2.5px;
}
.label {
font-family: ${franklinLight};
font-weight: bold;
}
`
Insert cell
Insert cell
lineGenerator = d3.line()
.x(d => x(d.year))
.y(d => y(d.error));
Insert cell
xaxisGenerator = d3.axisBottom(x)
.tickFormat(d => d)
.tickSize(10)
.tickValues([1972, ...d3.range(1980, 2020, 10), 2023])
Insert cell
yaxisGenerator = d3.axisLeft(y)
.tickFormat(d => `${d}°F`)
.tickValues(d3.range(2, 8))
Insert cell
Insert cell
x = d3.scaleLinear()
.domain(d3.extent(flat, d => d.year))
Insert cell
y = d3.scaleLinear()
.domain([2, 7.5])
Insert cell
Insert cell
historic_accuracy = FileAttachment("historic_accuracy.json").json()
Insert cell
dataAll = Object
.keys(historic_accuracy[0])
.filter(d => d.includes("Day"))
.map(d => +d.replace("Day ", ""))
.map(lead_time => ({
lead_time,
errors: historic_accuracy
.map(d => ({ year: d.Year, error: d[`Day ${lead_time}`]}))
.filter(d => d.error)
}))
Insert cell
data = dataAll.filter(d => [3, 5, 7].includes(d.lead_time))
Insert cell
flat = data.map(d => d.errors).flat()
Insert cell
Insert cell
import { franklinLight } from "@climatelab/fonts@46"
Insert cell
import { toc } from "@climatelab/toc@45"
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