Published
Edited
Apr 22, 2021
6 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
dms = {
let dimensions = {
width: width,
heightTall: Math.max(width, 900),
heightShort: width * (9 / 16),
marginTop: 30,
marginLeft: 60
};

// dimensions.marginLeft = dimensions.marginTop;
dimensions.marginBottom = dimensions.marginTop;
dimensions.marginRight = dimensions.marginLeft;

dimensions.chartWidth =
dimensions.width - dimensions.marginLeft - dimensions.marginRight;
dimensions.chartHeightTall =
dimensions.heightTall - dimensions.marginTop - dimensions.marginBottom;
dimensions.chartHeightShort =
dimensions.heightShort - dimensions.marginTop - dimensions.marginBottom;

return dimensions;
}
Insert cell
Insert cell
timeParser = d3.timeParse("%Y-%0m-%0d")
Insert cell
timeFormatter = function(date, year = true) {
const months = [
"Jan.",
"Feb.",
"March",
"April",
"May",
"June",
"July",
"Aug.",
"Sept.",
"Oct.",
"Nov.",
"Dec."
];
const monthIndex = +d3.timeFormat('%m')(date) - 1;
const day = +d3.timeFormat('%e')(date);
let remainingDate = "";

year
? (remainingDate = d3.timeFormat(`${months[monthIndex]} %-e, %Y`)(date))
: (remainingDate = d3.timeFormat(`${months[monthIndex]} %-e`)(date));

return remainingDate;
}
Insert cell
Insert cell
Insert cell
crosswalk = {
const attachment = await FileAttachment("CrosswalkIncomeBracket.csv").text();

const incomeBracket = d3.rollup(
d3.csvParse(attachment).filter(d => d["Region"].length > 0),
v => v,
d => d["Country Code"]
);

const crosswalk = Array.from(dataGDP.values())
.map(d => {
return {
name: d["name"],
iso: d["iso"],
region:
incomeBracket.get(d["iso"]) == undefined
? undefined
: incomeBracket.get(d["iso"])[0]["Region"],
income:
incomeBracket.get(d["iso"]) == undefined
? undefined
: incomeBracket.get(d["iso"])[0]["IncomeGroup"]
};
})
.filter(d => d["region"] != undefined);

// return crosswalk;

return d3.rollup(crosswalk, v => v[0], d => d["iso"]);
}
Insert cell
dataGDP = {
const attachment = await FileAttachment("GDP@1.csv").text();

const attachmentProcessed = d3.csvParse(attachment).map(d => {
let gdpAll = [];
for (let i = 1961; i <= 2019; i++) {
+d[i] > 0 ? gdpAll.push(+d[i]) : null;
}

return {
name: d["Country Name"],
iso: d["Country Code"],
gdp: { 2019: d["2019"], 2018: d["2018"], 2017: d["2017"] },
gdpLatest: gdpAll[gdpAll.length - 1]
};
});

return d3.rollup(attachmentProcessed, v => v[0], d => d["iso"]);
}
Insert cell
dataPop = {
const attachment = await FileAttachment("Population-UN.csv").text();

const attachmentProcessed = d3
.csvParse(attachment)
.map(d => {
return {
name: d["entity"],
iso: d["iso_code"],
population: d["population"]
};
})
.filter(f => f["iso"].length > 0 && f["iso"].length <= 3);

return d3.rollup(attachmentProcessed, v => v[0], d => d["iso"]);
}
Insert cell
Insert cell
dataVaccinationsRAW = {
const attachment = await FileAttachment(
"country-vaccinations-historical-combined (2).csv"
).text();

const attachmentProcessed = d3.csvParse(attachment).map(d => {
d["date"] = timeParser(d["date"]);
return d;
});

return attachmentProcessed;
}
Insert cell
dataVaccinations = {
const input = dataVaccinationsRAW.sort((a, b) =>
d3.ascending(a["iso"], b["iso"])
);

return d3.rollup(
input,
v => {
const array = v.sort((a, b) => d3.ascending(a["date"], b["date"]));

const arrayFilled = array.map((d, i, arr) => {
// Filling in all the blanks in the vaccinated people data:
if (i > 0) {
d["vaccPeopleTotal"] =
+d["vaccPeopleTotal"] > 0
? +d["vaccPeopleTotal"]
: +arr[i - 1]["vaccPeopleTotal"];
} else {
d["vaccPeopleTotal"] = +d["vaccPeopleTotal"];
}

// Filling in all the blanks in the vaccine doses data:
if (i > 0) {
d["vaccDosesTotal"] =
+d["vaccDosesTotal"] > 0
? +d["vaccDosesTotal"]
: +arr[i - 1]["vaccDosesTotal"];
} else {
d["vaccDosesTotal"] = +d["vaccDosesTotal"];
}

d["dateFormatted"] = timeFormatter(d["date"]);
d["name"] = d["country"];
d["iso"] = d["countryIso"];
d["population"] = +d["populationTotal"];

return d;
});

return arrayFilled;
},
d => d["countryIso"]
);
}
Insert cell
Insert cell
dataCombined = {
// I had to filter the World Bank countries array so that I could
// match it to UN population data. I also had to filter out SARs
// in China because we lump that data in with China in the
// vaccination data.
const countriesAll = Array.from(crosswalk.keys())
.sort(d3.ascending)
.filter(f => dataPop.get(f) != undefined)
.filter(f => crosswalk.get(f)["name"].includes("SAR, China") == false);

// The filter on this data isn't really doing anything right now
// because it
const countriesWithVaccines = Array.from(dataVaccinations.keys())
.sort(d3.ascending)
.filter(f => countriesAll.includes(f));

return countriesAll.map(d => {
const obj = {
name: crosswalk.get(d)["name"],
iso: crosswalk.get(d)["iso"],
income: crosswalk.get(d)["income"],
gdp: dataGDP.get(d)["gdpLatest"],
population:
dataVaccinations.get(d) != undefined &&
dataVaccinations.get(d)["population"] > 0
? dataVaccinations.get(d)["population"]
: +dataPop.get(d)["population"],
vaccPeopleTotal:
dataVaccinations.get(d) == undefined
? 0
: dataVaccinations.get(d)[dataVaccinations.get(d).length - 1][
"vaccPeopleTotal"
],
vaccDosesTotal:
dataVaccinations.get(d) == undefined
? 0
: dataVaccinations.get(d)[dataVaccinations.get(d).length - 1][
"vaccDosesTotal"
]
};

obj["gdpPerCapita"] = obj["gdp"] / obj["population"];
obj["vaccPeoplePer100"] =
(obj["vaccPeopleTotal"] / obj["population"]) * 100;
obj["vaccDosesPer100k"] =
(obj["vaccDosesTotal"] / obj["population"]) * 100000;

return obj;
});
// .filter(f => f["population"] > 1000000);
// .filter(f => crosswalk.get(f["iso"])["income"] == "High income");
}
Insert cell
dataNodes = {
const nodeData = dataCombined;

return nodeData.map(d => {
d["x"] = dms.chartWidth / 2;

// d["x"] = dms.width / 2;
if (d["vaccDosesPer100k"] > 0) {
d["y"] = scaleDosesVacc(d["vaccDosesPer100k"]);
} else {
d["y"] = scaleDosesVacc(0) - (dms.chartHeightTall * 1) / 10;
}
return d;
});
}
Insert cell
Insert cell
scalePopVacc = d3
.scaleLinear()
.domain([0, d3.max(dataCombined.map(d => d["vaccPeoplePer100"]))])
// .domain([0, 100])
.range([0 + dms.chartHeightTall / 5, dms.chartHeightTall])
Insert cell
scaleDosesVacc = d3
.scaleLinear()
.domain([0, d3.max(dataCombined.map(d => d["vaccDosesPer100k"]))])
// .domain([0, 100])
.range([0 + dms.chartHeightTall / 5, dms.chartHeightTall])
Insert cell
scaleGDP = d3
.scaleLinear()
.domain([0, d3.max(dataCombined.map(d => d["gdp"]))])
.range([0, 20])
Insert cell
scaleGDPPerCapita = d3
.scaleLinear()
.domain([0, d3.max(dataCombined.map(d => d["gdpPerCapita"]))])
.range([0, 20])
Insert cell
scalePop = d3
.scaleLinear()
.domain([
d3.min(dataCombined.map(d => d["population"])),
d3.max(dataCombined.map(d => d["population"]))
])
.range([7, 700])
Insert cell
scaleIncome = d3
.scaleOrdinal()
.domain([
"Low income",
"Lower middle income",
"Upper middle income",
"High income"
])
.range(["#FBC654", "#D89D0F", "#A7CE95", "#64974B"])
Insert cell
Insert cell
scaleScatterGDP = d3
.scalePow()
.exponent(0.5)
.domain([0, d3.max(dataCombined.map(d => d["gdpPerCapita"]))])
.range([0, dms.chartWidth])
Insert cell
scaleScatterVacc = d3
.scaleLinear()
.domain([0, d3.max(dataCombined.map(d => d["vaccDosesPer100k"]))])
.range([dms.chartHeightShort, 0])
Insert cell
Insert cell
viewof ticks = Scrubber(d3.range(101), {
autoplay: false,
loop: false,
alternate: false,
initial: 100
})
Insert cell
Insert cell
Insert cell
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