Published
Edited
Jul 27, 2020
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
colorScales = [
{
label: "Continuous: linear",
scale: (values, binCount) => d3.scaleSequential()
.domain(d3.extent(values))
.interpolator(continuousInterpolator)
.unknown(noDataColor)
},
{
label: "Continuous: log",
scale: (values, binCount) => d3.scaleSequentialLog()
.domain(d3.extent(values.filter(v => v > 0)))
.interpolator(continuousInterpolator)
.unknown(noDataColor)
},
{
label: "Continuous: quantile",
scale: (values, binCount) => d3.scaleSequentialQuantile()
.domain(values)
.interpolator(continuousInterpolator)
},
// {
// label: "Continuous (ckmeans)",
// scale: (values, binCount) => d3.scaleSequentialQuantile()
// .domain(scaleCluster().domain(values).range(d3.range(binCount)).clusters())
// .interpolator(continuousInterpolator)
// },
{
label: "Discrete: quantile",
scale: (values, binCount) => d3.scaleQuantile()
.domain(values)
.range(discreteScale[binCount])
.unknown(noDataColor)
},
{
label: "Discrete: quantize",
scale: (values, binCount) => d3.scaleQuantize()
.domain(d3.extent(values))
.range(discreteScale[binCount])
.unknown(noDataColor)
},
{
label: "Discrete: ckmeans",
scale: (values, binCount) => d3.scaleThreshold()
.domain(scaleCluster().domain(values).range(d3.range(binCount)).clusters())
.range(discreteScale[binCount])
.unknown(noDataColor)
}
]
Insert cell
continuousInterpolator = d3.interpolateBlues
Insert cell
discreteScale = d3.schemePurples
Insert cell
colorDomainValues = colorData.map(metricAccessor).filter(v => !isNaN(v) && v != null && isFinite(v))
Insert cell
// an optimization to avoid changing colorData in case the checkbox isn't checked
{
if (_colorScaleFromCurrentDateOnly !== colorScaleFromCurrentDateOnly || colorScaleFromCurrentDateOnly) {
mutable colorData = colorScaleFromCurrentDateOnly ? owidCovidData.filter(row => row.date === date) : owidCovidData
mutable _colorScaleFromCurrentDateOnly = colorScaleFromCurrentDateOnly
}
}
Insert cell
mutable colorData = owidCovidData
Insert cell
mutable _colorScaleFromCurrentDateOnly = false
Insert cell
color = _.find(colorScales, c => c.label === colorScale).scale(
colorDomainValues,
colorBinCount
)
Insert cell
noDataColor = "#e1e1e1"
Insert cell
metrics = [
{ label: "Total deaths", accessor: d => parseFloat(d.total_deaths) },
{ label: "Total cases", accessor: d => parseFloat(d.total_cases) },
{ label: "Daily new deaths", accessor: d => parseFloat(d.new_deaths) },
{ label: "Daily new cases", accessor: d => parseFloat(d.new_cases) },
{ label: "Total deaths per million", accessor: d => parseFloat(d.total_deaths_per_million) },
{ label: "Total cases per million", accessor: d => parseFloat(d.total_cases_per_million) },
{ label: "Daily new deaths per million", accessor: d => parseFloat(d.new_deaths_per_million) },
{ label: "Daily new cases per million", accessor: d => parseFloat(d.new_cases_per_million) },
{ label: "Total tests", accessor: d => parseFloat(d.total_tests) },
{ label: "Daily new tests", accessor: d => parseFloat(d.new_tests) },
{ label: "Daily new tests (smoothed)", accessor: d => parseFloat(d.new_tests_smoothed) },
{ label: "Total tests per thousand", accessor: d => parseFloat(d.total_tests_per_thousand) },
{ label: "Daily new tests per thousand", accessor: d => parseFloat(d.new_tests_per_thousand) },
{ label: "Daily new tests (smoothed) per thousand", accessor: d => parseFloat(d.new_tests_smoothed_per_thousand) },
{ label: "Case fatality rate (%)", accessor: d => d.total_deaths && d.total_cases ? (parseFloat(d.total_deaths) / parseFloat(d.total_cases)) * 100 : undefined },
{ label: "Share of positive tests (%)", accessor: d => d.total_cases && d.total_tests ? (parseFloat(d.total_cases) / parseFloat(d.total_tests)) * 100 : undefined },
{ label: "Stringency index", accessor: d => parseFloat(d.stringency_index) },
{ label: "Life expectancy", accessor: d => parseFloat(d.life_expectancy) },
{ label: "Population", accessor: d => parseFloat(d.population) },
{ label: "Population density", accessor: d => parseFloat(d.population_density) },
{ label: "Median age", accessor: d => parseFloat(d.median_age) },
{ label: "Share aged 65+", accessor: d => parseFloat(d.aged_65_older * 100) },
{ label: "Share aged 70+", accessor: d => parseFloat(d.aged_70_older * 100) },
{ label: "GDP per capita (int.-$)", accessor: d => parseFloat(d.gdp_per_capita) },
{ label: "Share of the population in extreme poverty", accessor: d => parseFloat(d.extreme_poverty * 100) },
{ label: "Death rate from cardiovascular disease (per thousand)", accessor: d => parseFloat(d.cardiovasc_death_rate) },
{ label: "Diabetes prevalence (% of population aged 20 to 79)", accessor: d => parseFloat(d.diabetes_prevalence * 100) },
{ label: "Share of women who smoke", accessor: d => parseFloat(d.female_smokers * 100) },
{ label: "Share of men who smoke", accessor: d => parseFloat(d.male_smokers * 100) },
{ label: "Share of the population with basic handwashing facilities", accessor: d => parseFloat(d.handwashing_facilities * 100) },
{ label: "Hospital beds per thousand", accessor: d => parseFloat(d.hospital_beds_per_thousand) },
]
Insert cell
sortedDates = _.sortedUniq(_.sortBy(owidCovidData.filter(metricAccessor).map(row => row.date))).slice(50)
Insert cell
metric = _.find(metrics, d => d.label === metricLabel)
Insert cell
metricAccessor = metric.accessor
Insert cell
data = Object.assign(
new Map(
owidCovidData
.filter(d => d.date === date)
.map(d => {
const value = metricAccessor(d)
if (value != null && !isNaN(value) && isFinite(value)) {
return [d.location, value];
}
return [d.location, undefined]
})
),
{title: metricLabel}
)
Insert cell
owidCovidData = (await d3.csv("https://covid.ourworldindata.org/data/owid-covid-data.csv")).filter(row => row.location !== "World")
Insert cell
projection = d3.geoRobinson()
Insert cell
path = d3.geoPath(projection)
Insert cell
width = 975
Insert cell
height = {
const [[x0, y0], [x1, y1]] = d3.geoPath(projection.fitWidth(width, outline)).bounds(outline);
const dy = Math.ceil(y1 - y0), l = Math.min(Math.ceil(x1 - x0), dy);
projection.scale(projection.scale() * (l - 1) / l).precision(0.2);
return dy;
}
Insert cell
outline = ({type: "Sphere"})
Insert cell
countries = topojson.feature(owidMap, owidMap.objects.world)
Insert cell
owidMap = FileAttachment("owid-map.json").json()
Insert cell
scaleCluster = require('d3-scale-cluster')
Insert cell
d3.range(1, 4, 1)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more