Public
Edited
May 3
Insert cell
Insert cell
data = await FileAttachment("CO2_emission.csv").csv()
Insert cell
Insert cell
{
//filtering out top 10 CO2 emissions for 2019
const top2019 = data.filter(row => row["2019"] !== "")
.map(row => ({
country: row["Country Name"],
emissions: parseFloat(row["2019"])
}))
.sort((a, b) => b.emissions - a.emissions)
.slice(0,10);
//filtering out top 10 CO2 emissions for 1990
const top1990 = data.filter(row => row["1990"] !== "")
.map(row => ({
country: row["Country Name"],
emissions: parseFloat(row["1990"])
}))
.sort((a, b) => b.emissions - a.emissions)
.slice(0,10);

const width = 400;
const height = 400;
const marginTop = 20;
const marginRight = 30;
const marginBottom = 50;
const marginLeft = 150;

// 1990 data
const svg1990 = d3.create("svg")
.attr("width", width)
.attr("height", height);
// x scale: linear value, numerical values of emissions
const x = d3.scaleLinear()
.domain([0, d3.max(top2019, d => d.emissions)])
.range([marginLeft, width - marginRight]);
// y scale: category names, country names
const y1990 = d3.scaleBand()
.domain(top1990.map(d => d.country))
.range([marginTop, height - marginBottom])
.padding(0.1);

// add bars
svg1990.append("g")
.selectAll("rect")
.data(top1990)
.join("rect")
.attr("x", x(0))
.attr("y", d => y1990(d.country))
.attr("width", d => x(d.emissions) - x(0))
.attr("height", y1990.bandwidth())
.attr("fill", "green");

// x axis
svg1990.append("g")
.attr("transform", `translate(0,${height-marginBottom})`)
.call(d3.axisBottom(x));
// y axis
svg1990.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y1990));

// x axis label
svg1990.append("text")
.attr("text-anchor", "middle")
.attr("x", (width / 2) + marginBottom)
.attr("y", height - 10)
.text("1990 CO2 Emissions (metric tons per capita)")
.attr("font-size", "13.5px");

// y axis label
svg1990.append("text")
.attr("text-anchor", "middle")
.attr("transform", 'rotate(-90)')
.attr("x", -marginTop - (marginLeft))
.attr("y", 20)
.text("Country")
.attr("font-size", "13.5px")

// title
svg1990.append("text")
.attr("text-anchor", "middle")
.attr("x", (width / 2) + marginBottom)
.attr("y", 15)
.text("Top CO2 Emitters in 1990")
.attr("font-size", "13.5px");


// 2019 data
const svg2019 = d3.create("svg")
.attr("width", width)
.attr("height", height);
// y scale: category names, country names
const y2019 = d3.scaleBand()
.domain(top2019.map(d => d.country))
.range([marginTop, height - marginBottom])
.padding(0.1);

// add bars
svg2019.append("g")
.selectAll("rect")
.data(top2019)
.join("rect")
.attr("x", x(0))
.attr("y", d => y2019(d.country))
.attr("width", d => x(d.emissions) - x(0))
.attr("height", y2019.bandwidth())
.attr("fill", "darkorange");
// x axis
svg2019.append("g")
.attr("transform", `translate(0,${height-marginBottom})`)
.call(d3.axisBottom(x));
// y axis
svg2019.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y2019));

// x axis label
svg2019.append("text")
.attr("text-anchor", "middle")
.attr("x", (width / 2) + marginBottom)
.attr("y", height - 10)
.text("2019 CO2 Emissions (metric tons per capita)")
.attr("font-size", "13.5px");

// y axis label
svg2019.append("text")
.attr("text-anchor", "middle")
.attr("transform", 'rotate(-90)')
.attr("x", -marginTop - (marginLeft))
.attr("y", 20)
.text("Country")
.attr("font-size", "13.5px")
// title
svg2019.append("text")
.attr("text-anchor", "middle")
.attr("x", (width / 2) + marginBottom)
.attr("y", 15)
.text("Top CO2 Emitters in 2019")
.attr("font-size", "13.5px");
//formatting
const charts = document.createElement("div")
charts.style.display = "flex";
charts.style.gap = "10px";
charts.appendChild(svg1990.node());
charts.appendChild(svg2019.node());
return charts;
}
Insert cell
Insert cell
{
//filtering out top 10 countries with highest CO2 emissions in 2019
const top10countries = data.filter(row => row["2019"] !== "")
.map(row => ({
country: row["Country Name"],
values: row
}))
.sort((a, b) => parseFloat(b.values["2019"] - a.values["2019"]))
.slice(0,10);
// creating array of years to use in heatmap
const years = [];
for (let year = 1990; year <= 2019; year++){
years.push(year);
}

//rearranging data to use in heat map (used ChatGPT to learn flattening of data for heat maps)
const heatMapData = top10countries.flatMap(d =>
years.map(year => ({
country: d.country,
year: year,
value: parseFloat(d.values[year]) || 0
}))
);

const width = 600;
const height = 300;
const marginTop = 30;
const marginRight = 30;
const marginBottom = 60;
const marginLeft = 150;
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
// x scale: band scale for heat map
const x = d3.scaleBand()
.domain(years)
.range([marginLeft, width - marginRight])
.padding(0.1);
// y scale: band scale for heat map
const y = d3.scaleBand()
.domain(top10countries.map(d => d.country))
.range([marginTop, height - marginBottom])
.padding(0.1);
// used blue color scale, dark blue for larger values, light for smaller
const color = d3.scaleSequential()
.interpolator(d3.interpolateBlues)
.domain([0, d3.max(heatMapData, d => d.value)]);

// creating the map squares
svg.append("g")
.selectAll("rect")
.data(heatMapData)
.join("rect")
.attr("x", d => x(d.year))
.attr("y", d => y(d.country))
.attr("width", x.bandwidth())
.attr("height", y.bandwidth())
.attr("fill", d => color(d.value));

// x axis
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x).tickValues(years.filter(y => y % 5 === 0)));

// y axis
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y));
// x axis label
svg.append("text")
.attr("text-anchor", "middle")
.attr("x", (width / 2) + marginBottom)
.attr("y", height - 10)
.text("2019 CO2 Emissions (metric tons per capita)")
.attr("font-size", "13.5px");

// y axis label
svg.append("text")
.attr("text-anchor", "middle")
.attr("transform", 'rotate(-90)')
.attr("x", -marginTop - 100)
.attr("y", 20)
.text("Country")
.attr("font-size", "13.5px")

// title
svg.append("text")
.attr("x", (width / 2) + 55)
.attr("y", marginTop - 10)
.attr("text-anchor", "middle")
.text("Top 10 Countries in 2019: CO2 Emissions Per Capita (1990–2019)")
.attr("font-size", "16px");


return svg.node();
}
Insert cell
Though South Asia has the smallest stream, and is barely noticeable. It starts of tiny and has only continued to grown. East Asia and the Pacifics have also slightly grown since the 1990's, and I think this largely reflects demands for oil with rapid industrialization. North America had a surprisingly smaller amount of CO2 Emissions over time that I had originally expected, and total metric tons per year have decreased. This might be due to a transition towards clean energy or new policy changes in the last few years. Europe and Central Asia had the most out of all, which makes sense because many of the top CO2 emitting countries were from Europe and Central Asia, and were extremely oil-rich. Though, they also started with a large number of emissions and slowly have been decreasing since. It makes sense that there isn't as much fluctuation as I had originally thought. Many of these emissions are largely resource based, and since some countries are more oil-rich than others and have economies centered around such, it makes sense that they emit larger amounts of CO2 per capita.
Insert cell
{
// creating array of years to use in heatmap
const years = [];
for (let year = 1990; year <= 2019; year++){
years.push(year);
}
// filtering out the regions from the dataset
const regions = Array
.from(new Set(
data.map(d => d["Region"])))
.filter(Boolean);

// aggregate emissions by region per year (used the help of chatGPT for this syntax)
const streamData = years.map(year => {
const row = { year: +year };
for (const region of regions) {
const total = data
.filter(d => d["Region"] === region)
.reduce((sum, d) => sum + (parseFloat(d[year]) || 0), 0);
row[region] = total;
}
return row;
});

// stacking data for stream graph (used chatGPT for help with implementation of stack + streamgraph)
const stack = d3.stack()
.keys(regions)
.offset(d3.stackOffsetWiggle);

const series = stack(streamData);

const width = 800;
const height = 300;
const marginTop = 20;
const marginRight = 200;
const marginBottom = 40;
const marginLeft = 50;

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);
// x scale: linear, numerical year values
const x = d3.scaleLinear()
.domain(d3.extent(streamData, d => d.year))
.range([marginLeft, width - marginRight]);
// y scale: linear, emission totals
const y = d3.scaleLinear()
.domain([
d3.min(series, s => d3.min(s, d => d[0])),
d3.max(series, s => d3.max(s, d => d[1]))
])
.range([height - marginBottom, marginTop]);

//used distinct colors to show clear separation between regions.
const color = d3.scaleOrdinal()
.domain(regions)
.range(d3.schemePaired);

//generated areas per region
const area = d3.area()
.x(d => x(d.data.year))
.y0(d => y(d[0]))
.y1(d => y(d[1]));

//drawing out the stream + filling
svg.selectAll("path")
.data(series)
.join("path")
.attr("fill", d => color(d.key))
.attr("d", area)
.append("title")
.text(d => d.key);

// x axis
svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x).ticks(10).tickFormat(d3.format("d")));

// y axis
svg.append("g")
.attr("transform", `translate(${marginLeft},0)`)
.call(d3.axisLeft(y));

// x axis label
svg.append("text")
.attr("text-anchor", "middle")
.attr("x", (width / 2) - 80)
.attr("y", height - 10)
.text("Year")
.attr("font-size", "13.5px");

// y axis label
svg.append("text")
.attr("text-anchor", "middle")
.attr("transform", 'rotate(-90)')
.attr("x", -marginTop - 125)
.attr("y", 20)
.text("CO2 Emissions (total metric tons)")
.attr("font-size", "13.5px");

// title
svg.append("text")
.attr("x", width /2 - 50)
.attr("y", marginTop - 8)
.attr("text-anchor", "middle")
.text("CO2 Emissions Over Time Per Region (1990-2019)")
.attr("font-size", "16px");
//creating a legend to label each color
const legend = svg.append("g")
.attr("transform", `translate(${width - 190}, ${marginTop +10})`);

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

// creating the color boxes
legendRow.append("rect")
.attr("width", 12)
.attr("height", 12)
.attr("fill", color(region));

// labels for the legend
legendRow.append("text")
.attr("x", 16)
.attr("y", 10)
.text(region)
.attr("font-size", "12px")
.attr("alignment-baseline", "middle");
});

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