Public
Edited
Jul 20, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import { Table } from "@observablehq/inputs"
Insert cell
Table(data.slice(0, 5))
Insert cell
Insert cell
Insert cell
height = 600
Insert cell
Insert cell
// Specify the chart’s dimensions.
margins = ({
top: 40,
right: 20,
bottom: 40,
left: 50
})
Insert cell
Insert cell
Insert cell
// Create the positional and color scales.
x = d3.scaleLinear()
.domain(d3.extent(data, d => d.time))
.range([margins.left, width - margins.right]);
Insert cell
y = d3.scaleLinear()
.domain([
d3.min(data, d => Math.min(d.alien, d.human)),
d3.max(data, d => Math.max(d.alien, d.human))
])
.range([height - margins.bottom, margins.top]);
Insert cell
Insert cell
colors = ["#E54F6D", "#F8C630"];
Insert cell
Insert cell
Insert cell
emptyChart = {

// Create the SVG container.
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;")
.datum(data)

return svg.node()
}
Insert cell
Insert cell
Insert cell
xyChart = {

// Create the SVG container.
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;")
.datum(data);

// Create the axes.
const xAxis = svg.append("g")
.attr("transform", `translate(0,${height - margins.bottom})`)
.call(d3.axisBottom(x)
.ticks(width / 80)
.tickSizeOuter(0))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", -55)
.attr("y", 25)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("Years (in thousand)"));

const yAxis = svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - margins.left - margins.right)
.attr("stroke-opacity", 0.1))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", -margins.left)
.attr("y", -30)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Temperature (°C)"));

return svg.node();
}
Insert cell
Insert cell
Insert cell
lineChart = {

// Create the SVG container.
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;")
.datum(data);

// Create the axes.
const xAxis = svg.append("g")
.attr("transform", `translate(0,${height - margins.bottom})`)
.call(d3.axisBottom(x)
.ticks(width / 80)
.tickSizeOuter(0))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", -55)
.attr("y", 25)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("Years (in thousand)"));

const yAxis = svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - margins.left - margins.right)
.attr("stroke-opacity", 0.1))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", -margins.left)
.attr("y", -30)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Temperature (°C)"));

// Create the black solid line for alien.
const splitLineAlien = svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", d3.line()
.curve(d3.curveStep)
.x(d => x(d.time))
.y(d => y(d.alien)));

// Create the black dashed line for humans.
const splitLineHuman = svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.style("stroke-dasharray", ("15, 15"))
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", d3.line()
.curve(d3.curveStep)
.x(d => x(d.time))
.y(d => y(d.human)));
return svg.node();
}
Insert cell
Insert cell
Insert cell
splitLineChart = {

// Create the SVG container.
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;")
.datum(data);

// Create the axes.
const xAxis = svg.append("g")
.attr("transform", `translate(0,${height - margins.bottom})`)
.call(d3.axisBottom(x)
.ticks(width / 80)
.tickSizeOuter(0))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", -55)
.attr("y", 25)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("Years (in thousand)"));

const yAxis = svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - margins.left - margins.right)
.attr("stroke-opacity", 0.1))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", -margins.left)
.attr("y", -30)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Temperature (°C)"));

// Create the clipPaths.
const clipPathAbove = svg.append("clipPath")
.attr("id", "above")
.append("path")
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(0)
.y1(d => y(d.human)));

const clipPathBelow = svg.append("clipPath")
.attr("id","below")
.append("path")
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(height)
.y1(d => y(d.human)));

// Create the color areas.
const colorAbove = svg.append("path")
.attr("clip-path", "url(#above)")
.attr("fill", colors[1])
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(height)
.y1(d => y(d.alien)));

const colorBelow = svg.append("path")
.attr("clip-path", "url(#below)")
.attr("fill", colors[0])
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(0)
.y1(d => y(d.alien)));

// Create the black line for alien.
const splitLineAlien = svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", d3.line()
.curve(d3.curveStep)
.x(d => x(d.time))
.y(d => y(d.alien)));

// Create the black line for humans.
const splitLineHuman = svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.style("stroke-dasharray", ("15, 15"))
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", d3.line()
.curve(d3.curveStep)
.x(d => x(d.time))
.y(d => y(d.human)));
return svg.node();
}
Insert cell
Insert cell
Insert cell
legendChart = {

// Create the SVG container.
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;")
.datum(data);

// Create the axes.
const xAxis = svg.append("g")
.attr("transform", `translate(0,${height - margins.bottom})`)
.call(d3.axisBottom(x)
.ticks(width / 80)
.tickSizeOuter(0))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", -55)
.attr("y", 25)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("Years (in thousand)"));

const yAxis = svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - margins.left - margins.right)
.attr("stroke-opacity", 0.1))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", -margins.left)
.attr("y", -30)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Temperature (°C)"));

// Create the clipPaths.
const clipPathAbove = svg.append("clipPath")
.attr("id", "above")
.append("path")
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(0)
.y1(d => y(d.human)));

const clipPathBelow = svg.append("clipPath")
.attr("id","below")
.append("path")
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(height)
.y1(d => y(d.human)));

// Create the color areas.
const colorAbove = svg.append("path")
.attr("clip-path", "url(#above)")
.attr("fill", colors[1])
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(height)
.y1(d => y(d.alien)));

const colorBelow = svg.append("path")
.attr("clip-path", "url(#below)")
.attr("fill", colors[0])
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(0)
.y1(d => y(d.alien)));

// Create the black line for alien.
const splitLineAlien = svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", d3.line()
.curve(d3.curveStep)
.x(d => x(d.time))
.y(d => y(d.alien)));

// Create the black line for humans.
const splitLineHuman = svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.style("stroke-dasharray", ("15, 15"))
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", d3.line()
.curve(d3.curveStep)
.x(d => x(d.time))
.y(d => y(d.human)));

const legendNames = Object.keys(data[0]).slice(1)

const legend = svg.selectAll(".legend")
.data(legendNames)//data set for legends
.join("g")
.attr("class", "legend")

// Aliens
legend.append("line")//making a line for legend
.attr("x1", 110)
.attr("x2", 140)
.attr("y1", 100)
.attr("y2", 100)
.style("stroke-dasharray", "5,5")//dashed array for Aliens
.style("stroke", "black");

legend.append("text")
.attr("x", 70)
.attr("y", 100)
.attr("dy", ".35em")
.style("text-anchor", "start")
.attr("font-size", "12px")
.attr("front-weight", "bold")
.text(legendNames[0])

// Humans
legend.append("line")
.attr("x1", 200)
.attr("x2", 230)
.attr("y1", 100)
.attr("y2", 100)
.style("stroke", "black");

legend.append("text")
.attr("x", 150)
.attr("y", 100)
.attr("dy", ".35em")
.style("text-anchor", "start")
.attr("font-size", "12px")
.attr("front-weight", "bold")
.text(legendNames[1])
return svg.node();
}
Insert cell
Insert cell
Insert cell
titleChart = {

// Create the SVG container.
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;")
.datum(data);

// Create the axes.
const xAxis = svg.append("g")
.attr("transform", `translate(0,${height - margins.bottom})`)
.call(d3.axisBottom(x)
.ticks(width / 80)
.tickSizeOuter(0))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", -55)
.attr("y", 25)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("Years (in thousand)"));

const yAxis = svg.append("g")
.attr("transform", `translate(${margins.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - margins.left - margins.right)
.attr("stroke-opacity", 0.1))
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", -margins.left)
.attr("y", -30)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Temperature (°C)"));

// Create the clipPaths.
const clipPathAbove = svg.append("clipPath")
.attr("id", "above")
.append("path")
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(0)
.y1(d => y(d.human)));

const clipPathBelow = svg.append("clipPath")
.attr("id","below")
.append("path")
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(height)
.y1(d => y(d.human)));

// Create the color areas.
const colorAbove = svg.append("path")
.attr("clip-path", "url(#above)")
.attr("fill", colors[1])
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(height)
.y1(d => y(d.alien)));

const colorBelow = svg.append("path")
.attr("clip-path", "url(#below)")
.attr("fill", colors[0])
.attr("d", d3.area()
.curve(d3.curveStep)
.x(d => x(d.time))
.y0(0)
.y1(d => y(d.alien)));

// Create the black line for alien.
const splitLineAlien = svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", d3.line()
.curve(d3.curveStep)
.x(d => x(d.time))
.y(d => y(d.alien)));

// Create the black line for humans.
const splitLineHuman = svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.style("stroke-dasharray", ("15, 15"))
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("d", d3.line()
.curve(d3.curveStep)
.x(d => x(d.time))
.y(d => y(d.human)));

const legendNames = Object.keys(data[0]).slice(1)

const legend = svg.selectAll(".legend")
.data(legendNames)//data set for legends
.join("g")
.attr("class", "legend")

// Aliens
legend.append("line")//making a line for legend
.attr("x1", 110)
.attr("x2", 140)
.attr("y1", 100)
.attr("y2", 100)
.style("stroke-dasharray", "5,5")//dashed array for Aliens
.style("stroke", "black");

legend.append("text")
.attr("x", 70)
.attr("y", 100)
.attr("dy", ".35em")
.style("text-anchor", "start")
.attr("font-size", "12px")
.attr("front-weight", "bold")
.text(legendNames[0])

// Humans
legend.append("line")
.attr("x1", 200)
.attr("x2", 230)
.attr("y1", 100)
.attr("y2", 100)
.style("stroke", "black");

legend.append("text")
.attr("x", 150)
.attr("y", 100)
.attr("dy", ".35em")
.style("text-anchor", "start")
.attr("font-size", "12px")
.attr("front-weight", "bold")
.text(legendNames[1])

// Add a title
const title = svg.append("g")
.append("text")
.attr("x", width / 2)
.attr("y", (margins.top / 2))
.attr("text-anchor", "middle")
.attr("front-weight", "bold")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-size", "20px")
.text("Body temperature evolution: aliens vs humans")

// Add an annotation
const annot = svg.append("g")
.append("text")
.attr("x", width - 100)
.attr("y", height - 60)
.attr("text-anchor", "middle")
.attr("font-family", "Helvetica Neue, Arial")
.attr("font-size", "12px")
.text("* Data is completely made up")
return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import {textcolor} from "@observablehq/text-color-annotations-in-markdown"
Insert cell
import {toc} from "@jonfroehlich/collapsible-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