Public
Edited
Apr 27
Insert cell
Insert cell
Insert cell
d3 = require("d3@7")
Insert cell
Insert cell
data = FileAttachment("CO2_emission.csv").csv({ typed: true })
Insert cell
Insert cell
// Let's log a sample to understand the structure
data.slice(0, 5)

Insert cell
Insert cell
{
const selectedCountries = ["USA", "CHN", "IND", "AUS", "BRA"]; // Example: USA, China, India, Australia, Brazil
const filteredData = data.filter(d => selectedCountries.includes(d["country_code"]));

const years = Object.keys(data[0]).filter(key => !isNaN(key)).map(Number);

// Prepare the data for easier use later
const processedData = filteredData.map(d => {
const obj = { country: d["Country Name"] };
years.forEach(year => {
obj[year] = d[year];
});
return obj;
});

return processedData;
}

Insert cell
Insert cell
selectedCountries = ["USA", "CHN", "IND", "AUS", "BRA"]

Insert cell
Insert cell
years = Object.keys(data[0]).filter(key => !isNaN(key)).map(Number)
Insert cell
Insert cell
processedData = {
const filteredData = data.filter(d => selectedCountries.includes(d["country_code"]));
return filteredData.map(d => {
const obj = { country: d["Country Name"] };
years.forEach(year => {
obj[year] = d[year];
});
return obj;
});
}

Insert cell
Insert cell
lineChart = {
const width = 800, height = 500, margin = {top: 50, right: 150, bottom: 50, left: 60};

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height]);

// Convert years into real Date objects (otherwise it gives error)
const yearDates = years.map(y => new Date(y, 0, 1));

// Line scales
const x = d3.scaleTime()
.domain(d3.extent(yearDates))
.range([margin.left, width - margin.right - 100]); // leave space for legend

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

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

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

// Draw axix
svg.append("g").call(xAxis)
.append("text")
.attr("x", (width - margin.left - margin.right) / 2 + margin.left)
.attr("y", 40)
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("Year");

svg.append("g").call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -margin.left + 20)
.attr("x", -(height - margin.top - margin.bottom) / 2 - margin.top)
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("CO₂ Emissions (metric tons per capita)");

// Color scale
const color = d3.scaleOrdinal(d3.schemeCategory10)
.domain(processedData.map(d => d.country));

// Line generator
const lineGen = d3.line()
.x(d => x(d.year))
.y(d => y(d.value));

// Draw lines
processedData.forEach(country => {
svg.append("path")
.datum(years.map((year, i) => ({year: yearDates[i], value: country[year]})))
.attr("fill", "none")
.attr("stroke", color(country.country))
.attr("stroke-width", 2)
.attr("d", lineGen);
});

// Legend
const legend = svg.append("g")
.attr("transform", `translate(${width - margin.right + 20},${margin.top})`);

processedData.forEach((country, i) => {
const legendRow = legend.append("g")
.attr("transform", `translate(0, ${i * 20})`);

legendRow.append("rect")
.attr("width", 12)
.attr("height", 12)
.attr("fill", color(country.country));

legendRow.append("text")
.attr("x", 18)
.attr("y", 10)
.text(country.country)
.attr("font-size", 12)
.attr("alignment-baseline", "middle");
});

return svg.node();
}

Insert cell
Insert cell
donutChart = {
const width = 500, height = 450, radius = Math.min(width, height) / 2;

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height]);

const g = svg.append("g")
.attr("transform", `translate(${width / 2 - 50},${height / 2})`);

// Color scale
const color = d3.scaleOrdinal(d3.schemeCategory10)
.domain(processedData.map(d => d.country));

const pieData = d3.pie()
.sort(null)
.value(d => d[2019])(processedData);

const arcGen = d3.arc()
.innerRadius(radius * 0.5)
.outerRadius(radius * 0.8);

const labelArc = d3.arc()
.innerRadius(radius * 0.85)
.outerRadius(radius * 0.85);

// donut slices
g.selectAll("path")
.data(pieData)
.join("path")
.attr("d", arcGen)
.attr("fill", d => color(d.data.country))
.attr("stroke", "white")
.attr("stroke-width", 1.5);

// Labels
g.selectAll("text")
.data(pieData)
.join("text")
.attr("transform", d => `translate(${labelArc.centroid(d)})`)
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("font-size", 10)
.text(d => d.data.country);

// Title
svg.append("text")
.attr("x", width / 2 - 50)
.attr("y", 30)
.attr("text-anchor", "middle")
.attr("font-size", 16)
.attr("font-weight", "bold")
.text("CO₂ Emission Share (2019)");

return svg.node();
}

Insert cell
Insert cell
parallelCoordinates = {
const width = 800, height = 500, margin = {top: 50, right: 150, bottom: 50, left: 60};

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height]);

const dimensions = [1990, 2000, 2010, 2019];

const color = d3.scaleOrdinal(d3.schemeCategory10)
.domain(processedData.map(d => d.country));

// Create a y scale
const y = {};
for (const dim of dimensions) {
y[dim] = d3.scaleLinear()
.domain([0, d3.max(processedData, d => d[dim])])
.range([height - margin.bottom, margin.top]);
}

// create X scale
const x = d3.scalePoint()
.domain(dimensions)
.range([margin.left, width - margin.right - 70]); // a little tighter

// Draw vertical axis
svg.append("g")
.selectAll("g")
.data(dimensions)
.join("g")
.attr("transform", d => `translate(${x(d)})`)
.each(function(d) { d3.select(this).call(d3.axisLeft(y[d])); })
.append("text")
.attr("y", margin.top - 30)
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("font-size", 12)
.text(d => d);

// line generator
const lineGen = d3.line()
.x(d => d[0])
.y(d => d[1]);

// Group to hold all lines
const lineGroup = svg.append("g");

// Draw country lines
const lines = lineGroup.selectAll("path")
.data(processedData)
.join("path")
.attr("fill", "none")
.attr("stroke", d => color(d.country))
.attr("stroke-width", 2)
.attr("stroke-opacity", 0.7)
.attr("d", d => lineGen(dimensions.map(p => [x(p), y[p](d[p])])))
.on("mouseover", function(event, d) {
lines.attr("stroke-opacity", 0.1); // fade all lines
d3.select(this)
.attr("stroke-opacity", 1) // highlight hovered line
.attr("stroke-width", 4); // make hovered line thicker
})
.on("mouseout", function(event, d) {
lines.attr("stroke-opacity", 0.7) // reset opacity
.attr("stroke-width", 2); // reset thickness
});

// Legend code
const legend = svg.append("g")
.attr("transform", `translate(${width - margin.right + 20},${margin.top})`);

processedData.forEach((country, i) => {
const legendRow = legend.append("g")
.attr("transform", `translate(0, ${i * 20})`);

legendRow.append("rect")
.attr("width", 12)
.attr("height", 12)
.attr("fill", color(country.country));

legendRow.append("text")
.attr("x", 18)
.attr("y", 10)
.text(country.country)
.attr("font-size", 12)
.attr("alignment-baseline", "middle");
});

// Chart Title
svg.append("text")
.attr("x", (width - margin.left - margin.right) / 2 + margin.left)
.attr("y", 30)
.attr("text-anchor", "middle")
.attr("font-size", 18)
.attr("font-weight", "bold")
.text("CO₂ Emissions Across Years (Parallel Coordinates)");

return svg.node();
}

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