Public
Edited
Apr 15
Insert cell
Insert cell
import {vl} from "@vega/vega-lite-api-v5"
Insert cell
import {printTable} from "@uwdata/data-utilities"
Insert cell
climate_raw = FileAttachment("climate-change.csv").csv()
Insert cell
metadata_country = FileAttachment("metadata-country.csv").csv()
Insert cell
metadata_indicator = FileAttachment("metadata-indicator.csv").csv()
Insert cell
climate = climate_raw
.filter(d => d["Country Code"] && d["Year"])
.map(d => {
const cleaned = { ...d };
for (const key in d) {
if (key.startsWith("average_value_")) {
cleaned[key] = d[key] === "" || d[key] == null ? null : +d[key];
}
}
cleaned.Year = +d.Year;
return cleaned;
})
Insert cell
countries_only = climate.filter(d => {
const match = metadata_country.find(c => c["Country Code"] === d["Country Code"]);
return match && match.Region && match.IncomeGroup;
})
Insert cell
Insert cell
vl.markBar()
.data(
Object.entries(climate[0])
.filter(([k, _]) => k.startsWith("average_value_"))
.map(([k, _]) => {
let count = climate.filter(d => d[k] === null).length;
return { indicator: k.replace("average_value_", ""), missing: count };
})
)
.encode(
vl.y()
.fieldN('indicator')
.sort('-x')
.axis({
title: 'Indicator',
}),
vl.x()
.fieldQ('missing')
.axis({
title: 'Missing',
}),
vl.color().fieldQ('missing').scale({ scheme: 'reds' }),
vl.tooltip(['indicator', 'missing'])
)
.width(600)
.height(700)
.title('Missing Data by Indicator')
.render();

Insert cell
Insert cell
euCountries = [
"Austria", "Belgium", "Bulgaria", "Croatia", "Cyprus", "Czech Republic", "Denmark",
"Estonia", "Finland", "France", "Germany", "Greece", "Hungary", "Ireland", "Italy",
"Latvia", "Lithuania", "Luxembourg", "Malta", "Netherlands", "Poland", "Portugal",
"Romania", "Slovak Republic", "Slovenia", "Spain", "Sweden"
]

Insert cell
euAggregated = {
const data = countries_only.filter(d => euCountries.includes(d["Country Name"]));
const rolled = d3.rollups(
data,
v => d3.sum(v, d => d["average_value_CO2 emissions (kt)"]),
d => d.Year
);
return rolled.map(([year, total]) => ({
"Country Name": "European Union",
"Year": year,
"average_value_CO2 emissions (kt)": total
}));
}

Insert cell
selectedCountries = ["United States", "China", "India", "Japan", "Russian Federation"]
Insert cell
chartData = countries_only
.filter(d => selectedCountries.includes(d["Country Name"]))
.concat(euAggregated.filter(d => d.Year <= 2018))

Insert cell
vl.markLine()
.data(chartData)
.encode(
vl.x().fieldN('Year')
.axis({
tickCount: 14,
values: [1960, 1965, 1970, 1975, 1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2020],
title: 'Year'
}),
vl.y().fieldQ('average_value_CO2 emissions (kt)').title('CO2 Emissions (kt)'),
vl.color().fieldN('Country Name'),
vl.tooltip(['Country Name', 'Year', 'average_value_CO2 emissions (kt)'])
)
.width(900)
.height(550)
.title('CO2 Emissions Over Time (Top 5 + European Union)')
.render()
Insert cell
Insert cell
highlighted_countries_by_year = {
const alpha3ToNumeric = {
"AFG": "4",
"ALA": "248",
"ALB": "8",
"DZA": "12",
"ASM": "16",
"AND": "20",
"AGO": "24",
"AIA": "660",
"ATA": "10",
"ATG": "28",
"ARG": "32",
"ARM": "51",
"ABW": "533",
"AUS": "36",
"AUT": "40",
"AZE": "31",
"BHS": "44",
"BHR": "48",
"BGD": "50",
"BRB": "52",
"BLR": "112",
"BEL": "56",
"BLZ": "84",
"BEN": "204",
"BMU": "60",
"BTN": "64",
"BOL": "68",
"BES": "535",
"BIH": "70",
"BWA": "72",
"BVT": "74",
"BRA": "76",
"IOT": "086",
"BRN": "096",
"BGR": "100",
"BFA": "854",
"BDI": "108",
"CPV": "132",
"KHM": "116",
"CMR": "120",
"CAN": "124",
"CYM": "136",
"CAF": "140",
"TCD": "148",
"CHL": "152",
"CHN": "156",
"CXR": "162",
"CCK": "166",
"COL": "170",
"COM": "174",
"COG": "178",
"COD": "180",
"COK": "184",
"CRI": "188",
"CIV": "384",
"HRV": "191",
"CUB": "192",
"CUW": "531",
"CYP": "196",
"CZE": "203",
"DNK": "208",
"DJI": "262",
"DMA": "212",
"DOM": "214",
"ECU": "218",
"EGY": "818",
"SLV": "222",
"GNQ": "226",
"ERI": "232",
"EST": "233",
"SWZ": "748",
"ETH": "231",
"FLK": "238",
"FRO": "234",
"FJI": "242",
"FIN": "246",
"FRA": "250",
"GUF": "254",
"PYF": "258",
"ATF": "260",
"GAB": "266",
"GMB": "270",
"GEO": "268",
"DEU": "276",
"GHA": "288",
"GIB": "292",
"GRC": "300",
"GRL": "304",
"GRD": "308",
"GLP": "312",
"GUM": "316",
"GTM": "320",
"GGY": "831",
"GIN": "324",
"GNB": "624",
"GUY": "328",
"HTI": "332",
"HMD": "334",
"VAT": "336",
"HND": "340",
"HKG": "344",
"HUN": "348",
"ISL": "352",
"IND": "356",
"IDN": "360",
"IRN": "364",
"IRQ": "368",
"IRL": "372",
"IMN": "833",
"ISR": "376",
"ITA": "380",
"JAM": "388",
"JPN": "392",
"JEY": "832",
"JOR": "400",
"KAZ": "398",
"KEN": "404",
"KIR": "296",
"PRK": "408",
"KOR": "410",
"KWT": "414",
"KGZ": "417",
"LAO": "418",
"LVA": "428",
"LBN": "422",
"LSO": "426",
"LBR": "430",
"LBY": "434",
"LIE": "438",
"LTU": "440",
"LUX": "442",
"MAC": "446",
"MDG": "450",
"MWI": "454",
"MYS": "458",
"MDV": "462",
"MLI": "466",
"MLT": "470",
"MHL": "584",
"MTQ": "474",
"MRT": "478",
"MUS": "480",
"MYT": "175",
"MEX": "484",
"FSM": "583",
"MDA": "498",
"MCO": "492",
"MNG": "496",
"MNE": "499",
"MSR": "500",
"MAR": "504",
"MOZ": "508",
"MMR": "104",
"NAM": "516",
"NRU": "520",
"NPL": "524",
"NLD": "528",
"NCL": "540",
"NZL": "554",
"NIC": "558",
"NER": "562",
"NGA": "566",
"NIU": "570",
"NFK": "574",
"MNP": "580",
"NOR": "578",
"OMN": "512",
"PAK": "586",
"PLW": "585",
"PSE": "275",
"PAN": "591",
"PNG": "598",
"PRY": "600",
"PER": "604",
"PHL": "608",
"PCN": "612",
"POL": "616",
"PRT": "620",
"PRI": "630",
"QAT": "634",
"REU": "638",
"ROU": "642",
"RUS": "643",
"RWA": "646",
"BLM": "652",
"SHN": "654",
"KNA": "659",
"LCA": "662",
"MAF": "663",
"SPM": "666",
"VCT": "670",
"WSM": "882",
"SMR": "674",
"STP": "678",
"SAU": "682",
"SEN": "686",
"SRB": "688",
"SYC": "690",
"SLE": "694",
"SGP": "702",
"SXM": "534",
"SVK": "703",
"SVN": "705",
"SLB": "90",
"SOM": "706",
"ZAF": "710",
"SGS": "239",
"SSD": "728",
"ESP": "724",
"LKA": "144",
"SDN": "729",
"SUR": "740",
"SJM": "744",
"SWE": "752",
"CHE": "756",
"SYR": "760",
"TWN": "158",
"TJK": "762",
"TZA": "834",
"THA": "764",
"TLS": "626",
"TGO": "768",
"TKL": "772",
"TON": "776",
"TTO": "780",
"TUN": "788",
"TUR": "792",
"TKM": "795",
"TCA": "796",
"TUV": "798",
"UGA": "800",
"UKR": "804",
"ARE": "784",
"GBR": "826",
"USA": "840",
"URY": "858",
"UZB": "860",
"VUT": "548",
"VEN": "862",
"VNM": "704",
"VGB": "92",
"VIR": "850",
"WLF": "876",
"ESH": "732",
"YEM": "887",
"ZMB": "894",
"ZWE": "716"
};
return countries_only
.filter(d => d.Year === selectedYear && d["average_value_CO2 emissions (kt)"] != null)
.map(d => ({
id: alpha3ToNumeric[d["Country Code"]] || d["Country Code"],
name: d["Country Name"],
co2: d["average_value_CO2 emissions (kt)"]
}));
}

Insert cell
max_co2_emissions = d3.max(countries_only, d => d["average_value_CO2 emissions (kt)"])

Insert cell
viewof selectedYear = Inputs.range([1960, 2018], {
step: 1,
value: 2018,
label: "Year"
})

Insert cell
vl.layer(
vl.markGeoshape({ filled: true })
.data({
url: 'https://raw.githubusercontent.com/vega/vega-datasets/main/data/world-110m.json',
format: { type: 'topojson', feature: 'countries' }
})
.encode(
vl.color().value('lightgray'),
vl.stroke().value('#333'),
vl.strokeWidth().value(0.5)
),

vl.markGeoshape({ filled: true })
.data({
url: 'https://raw.githubusercontent.com/vega/vega-datasets/main/data/world-110m.json',
format: { type: 'topojson', feature: 'countries' }
})
.transform(
vl.lookup('id')
.from(vl.data(highlighted_countries_by_year).key('id').fields(['name', 'co2']))
.as(['name', 'co2']),
{ calculate: "datum.co2 > 0 ? datum.co2 : null", as: "validCO2" }
)
.encode(
// vl.color()
// .fieldQ('validCO2')
// .title('CO₂ Emissions (kt)')
// .scale({
// type: 'log',
// scheme: 'oranges',
// domain: [1, max_co2_emissions]
// }),

vl.color()
.fieldQ('validCO2')
.title('CO₂ Emissions (kt)')
.scale({
type: 'log',
domain: [1, max_co2_emissions],
range: ['#00FF00', '#FFFF00', '#FF0000']
}),

vl.tooltip([
{ field: 'name', title: 'Country' },
{ field: 'co2', title: 'CO₂ Emissions (kt)', format: ',' }
]),
vl.stroke().value('#333'),
vl.strokeWidth().value(0.5)
)
)
.width(900)
.height(600)
.config({ projection: { type: "equalEarth" } })
.title(`CO₂ Emissions by Country (${selectedYear})`)
.render()

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