Public
Edited
Jun 5
Fork of Assignment 1
Insert cell
Insert cell
Insert cell
Insert cell
import {vl} from "@vega/vega-lite-api-v5"

Insert cell
import {fromColumns, printTable} from "@uwdata/data-utilities"
Insert cell
Insert cell
GHG@2.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
total_pop_by_country.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
//FileAttachment(...) grabs the file that has already been uploaded
// .csv({typed: true}) parses it and 'await' pauses until the file is fetched and parsed
ghg = await FileAttachment("GHG@2.csv").csv({typed: true})

Insert cell
pop = await FileAttachment("total_pop_by_country.csv").csv({typed: true})
Insert cell
merged = {

//using this list to filter out rows that list one of these phrases as a country code
const BAD = new Set(["OECDE", "OECD", "OECDA", "EU27_2020", "OECDSO"]);

/* hold World-Bank population data in the following form:
[
{code: "CHE", year: "1960", xpop: 5304247},
{code: "CHE", year: "1961", xpop: 5391428},
… one object for every country × year …
]
*/
const popLong = [];
for (const row of pop) {
const code = row["Country Code"];
for (let y = 1960; y <= 2023; ++y) {
const k = String(y);
if (row[k] !=="") popLong.push({code,year: y, xpop: +row[k]});
}
}

// map country code to its full name
const nameMap = new Map( pop.map(
row => [row["Country Code"], row["Country Name"]]
));

// map country code and year to its population
const popMap = new Map(popLong.map(
d => [`${d.code}-${d.year}`, d.xpop]
));

return ghg

//drop rows that contain one of the expressions in "BAD"
.filter(d => !BAD.has(d.REF_AREA))
.map(d => {
const code = d.REF_AREA;
const year = String(d.TIME_PERIOD);
const xpop = popMap.get(`${code}-${year}`) ?? null;
const kg_pc = +d.OBS_VALUE * 1e3;
const name = nameMap.get(code) ?? d["Reference area"] ?? code;

return {
code, name, year, kg_pc, xpop, t_total: xpop ? (kg_pc * xpop) / 1000: null
};
});
}
Insert cell
Insert cell
Inputs.table(merged);
Insert cell
Insert cell
Insert cell
bubble2020 = {

const data = merged.filter(

//get the 2020 record along with its population and total emission values
d => d.year === "2020" && d.xpop && d.t_total
);

const chart = vl.layer(
//creating the bubbles in the bubble chart
vl.markCircle({opacity: 0.45})
.data(data)
.title("National GHG Footprints (2020): Population vs. Per Capita Emissions (Bubble Area = Total Tonnes/Country)")
.encode(
//x axis: population, log scale
vl.x({field: "xpop", type: "quantitative",
scale: {type: "log"}, axis: {title: "Population (log)"}}),
//y axis: kg per-capita, log scale
vl.y({field: "kg_pc", type: "quantitative",
scale: {type: "log"}, axis: {title: "Per‑Capita GHG kg CO₂‑eq (log)"}}),
//bubble size: absolute total emissions per country
vl.size({field: "t_total", type: "quantitative",
scale: {range: [50, 1200]}, // make bubbles more / less dramatic here
legend: {title: "Total GHG, t CO₂‑eq"}}),


//tooltip on hover (shows all metrics, nicely formatted)
vl.tooltip([
{field: "name", type: "nominal", title: "Name"},
{field: "xpop", type: "quantitative", format: ","},
{field: "kg_pc", type: "quantitative", format: ".1f"},
{field: "t_total", type: "quantitative", format: ",.0f"}
]),
vl.color({
field: "t_total",
type: "quantitative",
scale: {type: "log", scheme: "viridis"}, // log because totals span 10³×
legend: {title: "Total t CO₂‑eq"}
})
),

//label layer
vl.markText({dy: -6, fontSize: 9})
.data(data)
.encode(
vl.x({field: "xpop", type: "quantitative", scale: {type: "log"}}),
vl.y({field: "kg_pc", type: "quantitative", scale: {type: "log"}}),
vl.text({field: "code", type: "nominal"})
)
)
.width(720)
.height(420)
.render(); // returns an SVG/Canvas element
return chart;
}
Insert cell
Insert cell
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