Public
Edited
May 1
Insert cell
Insert cell
data = FileAttachment("mean_SD_by_country_by_month.csv").csv({typed: true})
Insert cell
data
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
chart_1 = {
const margin = { top: 30, right: 10, bottom: 120, left: 115};
const cellHeight = 20;
const cellWidth = 2.7;
const paddingY = 5;

const countries = Array.from(new Set(data.map(d => d.country)));
const months = Array.from(new Set(data.map(d => +d.Month))).sort(d3.ascending);
const everySeventhMonth= months.filter((_, index) => index % 7 === 0);


const chartWidth = months.length * cellWidth + margin.left + margin.right;
const chartHeight = countries.length * (cellHeight + paddingY) + margin.top + margin.bottom;

const color = d3.scaleSequential()
.domain(d3.extent(data, d => d.mean_SD))
.interpolator(d3.interpolateBlues);

const xScale = d3.scaleTime()
.domain(d3.extent(data, d => d.Month))
.range([margin.left, chartWidth - margin.right]);

const svg = d3.create("svg")
.attr("width", chartWidth)
.attr("height", chartHeight);

const yScale = d3.scaleBand()
.domain(countries)
.range([margin.top, chartHeight - margin.bottom])
.padding(0.1);

svg.append("text")
.attr("x", chartWidth / 2)
.attr("y", margin.top / 2)
.attr("text-anchor", "middle")
.attr("font-size", 16)
.attr("font-weight", "bold")
.text("Average Snow Levels by Country (2000–2025)");

svg.append("g")
.selectAll("g")
.data(countries)
.join("g")
.attr("transform", d => `translate(0, ${yScale(d)})`)
.each(function (country) {
const countryData = data.filter(d => d.country === country);

d3.select(this)
.selectAll("rect")
.data(countryData)
.join("rect")
.attr("x", d => xScale(d.Month))
.attr("y", 0)
.attr("width", cellWidth)
.attr("height", yScale.bandwidth())
.attr("fill", d => color(d.mean_SD))

d3.select(this)
.append("text")
.attr("x", margin.left - 10)
.attr("y", yScale.bandwidth() / 2)
.attr("text-anchor", "end")
.attr("font-size", 12)
.text(country);
});

const xAxis = d3.axisBottom(xScale)
.tickValues(everySeventhMonth)
.tickFormat(d3.timeFormat("%b %Y"));

svg.append("g")
.attr("class", "x-axis")
.attr("transform", `translate(0,${chartHeight - margin.bottom})`)
.call(xAxis)
.selectAll("text")
.style("font-weight", "normal !important")
.attr("transform", "rotate(-60)")
.style("text-anchor", "end")
.attr("dx", "-0.5em")
.attr("dy", "0.1em");

svg.append("text")
.attr("x", chartWidth / 2)
.attr("y", chartHeight - margin.bottom + 90)
.attr("text-anchor", "middle")
.attr("font-size", 12)
.attr("fill", "black")
.text("Link to the data source: https://www.ecmwf.int/");

return svg.node();
}
Insert cell
Insert cell
chart_2 = {
const margin = { top: 40, right: 100, bottom: 50, left: 60 };
const width = 800;
const height = 400;

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);

const countries = Array.from(new Set(data.map(d => d.country)));

const x = d3.scaleTime()
.domain(d3.extent(data, d => d.Month))
.range([margin.left, width - margin.right]);

const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.mean_SD)]).nice()
.range([height - margin.bottom, margin.top]);

const color = d3.scaleOrdinal()
.domain(countries)
.range(d3.schemeCategory10);

const xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80));

const yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y));

svg.append("g").call(xAxis);
svg.append("g").call(yAxis);

const line = d3.line()
.x(d => x(d.Month))
.y(d => y(d.mean_SD));

const grouped = d3.groups(data, d => d.country);

svg.append("g")
.selectAll("path")
.data(grouped)
.join("path")
.attr("fill", "none")
.attr("stroke", ([country]) => color(country))
.attr("stroke-width", 1.5)
.attr("d", ([, values]) => line(values.sort((a, b) => a.Month - b.Month)));

svg.append("g")
.selectAll("text")
.data(grouped)
.join("text")
.attr("x", d => x(d[1][d[1].length - 1].Month) + 5)
.attr("y", d => y(d[1][d[1].length - 1].mean_SD))
.attr("dy", "0.35em")
.text(d => d[0])
.style("font", "12px sans-serif")
.style("fill", d => color(d[0]));

svg.append("text")
.attr("x", width / 2)
.attr("y", margin.top / 2)
.attr("text-anchor", "middle")
.attr("font-size", 16)
.attr("font-weight", "bold")
.text("Average Snow Levels by Country (2000–2025)");

return svg.node();
}

Insert cell
Insert cell
chart_3 = {
const margin = { top: 40, right: 20, bottom: 30, left: 40 };
const plotWidth = 200;
const plotHeight = 120;
const spacing = 40;

const countries = Array.from(d3.group(data, d => d.country).keys());
const columns = 3;

const svg = d3.create("svg")
.attr("width", columns * (plotWidth + spacing))
.attr("height", Math.ceil(countries.length / columns) * (plotHeight + spacing));

const xExtent = d3.extent(data, d => d.Month);
const yExtent = [0, d3.max(data, d => d.mean_SD)];

const xScale = d3.scaleTime()
.domain(xExtent)
.range([0, plotWidth]);

const yScale = d3.scaleLinear()
.domain(yExtent)
.range([plotHeight, 0]);

const line = d3.line()
.x(d => xScale(d.Month))
.y(d => yScale(d.mean_SD));

svg.append("text")
.attr("x", columns * (plotWidth + spacing) / 2)
.attr("y", 14)
.attr("text-anchor", "middle")
.attr("font-size", 16)
.attr("font-weight", "bold")
.text("Average Snow Levels by Country (2000–2025)");

countries.forEach((country, i) => {
const col = i % columns;
const row = Math.floor(i / columns);
const group = svg.append("g")
.attr("transform", `translate(${col * (plotWidth + spacing) + margin.left}, ${row * (plotHeight + spacing) + margin.top})`);

const countryData = data.filter(d => d.country === country).sort((a, b) => a.Month - b.Month);

group.append("g")
.attr("transform", `translate(0,${plotHeight})`)
.call(d3.axisBottom(xScale).ticks(4).tickFormat(d3.timeFormat("%b %y")).tickSizeOuter(0));

group.append("g")
.call(d3.axisLeft(yScale).ticks(3));

group.append("path")
.datum(countryData)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("d", line);

group.append("text")
.attr("x", 4)
.attr("y", -5)
.text(country)
.style("font", "bold 10px sans-serif");

});

return svg.node();
}
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