European Covid data exploration
Exploring which countries have had the highest and lowest covid numbers in Europe
Importing and preparing the data
We will be looking at data from the following countries:
- Italy
- Austria
- Germany
- Belgium
- France
- United Kingdom
We begin by importing the data, and adding calculating some new features so that we can compare the data from different countries. For example we calculate 'confirmed cases per 100k population', 'deaths per 100k' and 'new cases' since these are not initially in the dataset.
from covid19dh import covid19
import altair as alt
import datetime
countries = ["Italy",
"Austria",
"Germany",
"Belgium",
"France",
"United Kingdom",
"Switzerland"
]
yesterday = datetime.date.today() - datetime.timedelta(days=1)
x, src = covid19(countries, raw=True, verbose=False, end=yesterday, cache=False)
x_small = x.loc[:, ['administrative_area_level_1', 'date', 'vaccines', 'confirmed','tests', 'recovered', 'deaths', 'population']]
x_small.rename(columns={'administrative_area_level_1': 'id'}, inplace=True)
x_small['confirmed_per'] = 100000 * x_small['confirmed'] / x_small['population']
x_small['deaths_per'] = 100000 * x_small['deaths'] / x_small['population']
x_small['ratio'] = 100 * (x_small['deaths']) / (x_small['confirmed'])
x_small['tests_per'] = 100000 * (x_small['tests']) / (x_small['population'])
x_small['vaccines_per'] = x_small['vaccines'] / x_small['population']
x_small['new_cases']=x_small.groupby('id').confirmed.diff().fillna(0)
x_small['new_cases_per']=x_small.groupby('id').confirmed_per.diff().fillna(0)
Here is a random sample of 5 rows from the dataset.
x_small.tail()
We will first look at the total numbers of cases and deaths in each country, before moving on to cases and deaths per 100k population.
leg_selection = alt.selection_multi(fields=['id'], bind='legend')
alt.Chart(x_small).mark_line().encode(
x=alt.X("yearmonthdate(date):T", axis=alt.Axis(title='Date')),
y=alt.Y("confirmed_per:Q", axis=alt.Axis(title='Confirmed per 100k')),
tooltip=['id', 'confirmed_per'],
color=alt.Color('id', legend=alt.Legend(title="Countries")),
opacity=alt.condition(leg_selection, alt.value(1), alt.value(0.2))
).add_selection(leg_selection).properties(title='Total number of cases per 100,000 population for selected European Countries', width=600).interactive()
alt.Chart(x_small).mark_line().encode(
x=alt.X("yearmonthdate(date):T", axis=alt.Axis(title='Date')),
y=alt.Y("deaths_per:Q", axis=alt.Axis(title='Deaths per 100k')),
tooltip='id',
color=alt.Color('id', legend=alt.Legend(title="Countries")),
opacity=alt.condition(leg_selection, alt.value(1), alt.value(0.2))
).add_selection(leg_selection).properties(title='Number of deaths per 100,000 population for selected European Countries', width=600).interactive()
brush = alt.selection(type='interval', encodings=['x'])
base = alt.Chart(x_small).mark_line().transform_window(
rolling_mean='sum(new_cases_per)',
frame=[-7, 0],
groupby=['id']
).encode(
x=alt.X("yearmonthdate(date):T",
axis=alt.Axis(title='Date')
),
y=alt.Y("rolling_mean:Q",
axis=alt.Axis(title='Incidence rate')
),
tooltip=['id', 'rolling_mean:Q'],
color=alt.Color('id', legend=alt.Legend(title="Countries")),
opacity=alt.condition(leg_selection, alt.value(1), alt.value(0.2))
).add_selection(leg_selection).properties(
width=600,
height=400,
title='Number of new cases per 100,000 over two weeks for selected countries'
)
upper = base.encode(
alt.X('yearmonthdate(date):T',axis=alt.Axis(title='Date'),
scale=alt.Scale(domain=brush))
)
lower = base.properties(
height=60
).add_selection(brush)
upper & lower
The ratio of confirmed cases and deaths gives an indication of what the case fatality rate is - it seems to be between 2 and 3%, assuming that the countries listed here are catching all positive cases (which they probably aren't, so it's likely lower than this).
base = alt.Chart(x_small).mark_line().encode(
x=alt.X("yearmonthdate(date):T", axis=alt.Axis(title='Date')),
y=alt.Y("ratio:Q", axis=alt.Axis(title='Ratio of deaths per case')),
tooltip='id',
color=alt.Color('id', legend=alt.Legend(title="Countries")),
opacity=alt.condition(leg_selection, alt.value(1), alt.value(0.2))
).add_selection(leg_selection).properties(title='The ratio of deaths to confirmed cases (case fatality rate)', width=600)
upper = base.encode(
alt.X('yearmonthdate(date):T',axis=alt.Axis(title='Date'),
scale=alt.Scale(domain=brush))
)
lower = base.properties(
height=60
).add_selection(brush)
upper & lower
In the chart below we plot the number vaccines given per population - this means that if the number is 1, then the country has given the equivalent of 1 shot for each person in the country. Since not everyone in the countries are eligible to get the vaccine, a ratio of 1 means that many people have recieved two jabs. Note also that some kinds of vaccines (the J&J's Janssen vaccine, for example) only require 1 shot so the goal is not neccesarily to reach exactly 2 shots per person in the whole country.
alt.Chart(x_small.query("vaccines > 0")).mark_line().encode(
x=alt.X("yearmonthdate(date):T", axis=alt.Axis(title='Date')),
y=alt.Y("vaccines_per:Q", axis=alt.Axis(title='Number of vaccines given')),
tooltip=['id', 'vaccines_per'],
color=alt.Color('id', legend=alt.Legend(title="Countries")),
opacity=alt.condition(leg_selection, alt.value(1), alt.value(0.2))
).add_selection(leg_selection).properties(title='Number of vaccines given', width=600).interactive()