COVID vaccination effectiveness

library(tidyverse)
library(dplyr)
library(magrittr)
library(lubridate)
library(kableExtra)
library(ggplot2)
library(RColorBrewer)

Objective

Is Astra-Zeneca doses administered (at least a single dose) - as a proportion of all COVID vaccination administered - related to a national reduction in COVID-19 cases and death?

Data

# from "Our World in Data"
owid = read_csv(
  "https://covid.ourworldindata.org/data/owid-covid-data.csv",
  col_names=TRUE,
  col_types="ccc?nnnnnnnnnnnnnnnnnnnnnnnnnnnnncnnnnnnnnnnnnnnnnnnnnnnnnn",
  skip_empty_rows = FALSE
)

# European Center for Disease Prevention and Control, Week 19 of 2021
# used to derive use of different vaccine brands in Hungary
hungary_vax <- read_csv("~/Rprojects/coHealthKensington/Hungary_COVIDvax_dataset_2021-W19.csv")
germany_vax <- read_csv("~/Rprojects/coHealthKensington/Germany_COVIDvax_dataset_2021-W19.csv")

Country selection

Selection criteria:

  • high rate of overall coverage (35% as of May 2021), including vaccines which are not Astra-Zeneca
  • the availability of COVID case and death data at least two (preferably three) weeks after 35% single-dose COVID vaccination coverage achieved
  • high rates of COVID-19 cases/mortality in a December 2020 to January 2021 peak
    • At least 50 deaths daily (seven day rolling average)
owid %>%
  select(location, date, people_vaccinated_per_hundred) %>%
  filter(people_vaccinated_per_hundred >= 35, date <= (Sys.Date() - weeks(2))) %>%
  # at least 35% vaccination, at least 2 weeks before today
  group_by(location) %>% #
  arrange(people_vaccinated_per_hundred) %>%
  slice(1) %>% # the earliest date of reaching 35%
  ungroup() %>%
  inner_join(
    owid %>%
      select(location, date, new_deaths_smoothed) %>%
      filter(date >= as.Date("2020-12-01") & date <= as.Date("2021-01-31")) %>%
      filter(new_deaths_smoothed >= 50) %>%
      group_by(location) %>%
      arrange(desc(new_deaths_smoothed)) %>%
      slice(1) %>% # choose the maximum death rate from Dec 20 to Jan 21
      ungroup() %>%
      select(location, new_deaths_smoothed)
  ) %>%
  select(location, date, new_deaths_smoothed) %>%
  kbl(
    caption = "Countries with greater than 35% vaccine coverage as of May 2021 and significant mortality in Dec 20/Jan 21",
    col.names = c("Country", "Date of 35% coverage", "Peak deaths Dec 20/Jan 21"),
    digits = 1
  ) %>%
  kable_styling()
Countries with greater than 35% vaccine coverage as of May 2021 and significant mortality in Dec 20/Jan 21
Country Date of 35% coverage Peak deaths Dec 20/Jan 21
Canada 2021-05-05 152.9
Chile 2021-03-31 74.1
Germany 2021-05-12 894.4
Hungary 2021-04-21 176.4
Israel 2021-01-30 64.9
United Kingdom 2021-03-13 1253.0
United States 2021-04-10 3432.6

Canada, Chile and Hungary experienced additional ‘peaks’ after January 2021.

Canada achieved 35% single-dose coverage on 5th May 2021. Germany achieved 35% single-dose coverage on 12th May 2021. (As of 25th May 2021, it is not quite yet three weeks since either of those two dates)

Daily number of cases (seven day rolling average), daily number of deaths (seven day rolling average) and proportion of population vaccinated derived from Our World in Data.

Timepoints

  • Day of maximum cases in December 2020 to January 2021
  • Day of maximum deaths in December 2020 to January 2021
  • Day of 35% single-dose COVID vaccination coverage (any COVID-19 vaccine)
  • At least two, preferably three, weeks after the day of 35% single-dose COVID vaccination coverage
    • vaccines are considered effective one to two weeks after delivery of dose
    • COVID deaths tend to lag case-finding by one to two weeks
countries <- owid %>%
  select(location, date, new_cases_smoothed, new_deaths_smoothed, people_vaccinated_per_hundred) %>%
  filter(
    location == "Canada" | location == "Israel" |
      location == "United States" | location == "United Kingdom" |
      location == "Hungary" | location == "Germany" | location == "Chile")

# Iceland only had approximately 10 cases per month in Dec-January 2021
# (and even less cases at the time of 35% vaccination)
# Finland had less than 10 deaths per months in Dec-January 2021
# Cyprus reached 35% vaccination 14th May 2021, and had
# less than 10 deaths per day in Dec-January 2021
# Unable to find data on Uruguay vaccine brand numbers,
# though in Uruguay peak deaths in Dec-Jan 2021 was also approximately 10/day

cases <- countries[FALSE,] # 'empty' version of countries data

for (i in unique(countries$location)) {
  country <- countries %>% filter(location == i)
  
  # days of max cases and deaths in December/January 2021
  max_cases <- country %>%
    filter(date >= ymd("2020-12-01") & date <= ymd("2021-01-31")) %>%
    slice_max(new_cases_smoothed)
  max_deaths <- country %>%
    filter(date >= ymd("2020-12-01") & date <= ymd("2021-01-31")) %>%
    slice_max(new_deaths_smoothed)
  
  # day of 35% single-dose vaccination coverage, and three weeks after that
  vax_35_date <- country %>%
    filter(people_vaccinated_per_hundred >= 35 & people_vaccinated_per_hundred < 40) %>%
    slice_min(people_vaccinated_per_hundred)
  post35_3week <- country %>%
    filter(date == nth(date, which.min(abs(date - (vax_35_date$date + weeks(3))))))
  
  cases <- cases %>% 
    add_row(as.data.frame(max_cases)) %>%
    add_row(as.data.frame(max_deaths)) %>%
    add_row(as.data.frame(vax_35_date)) %>%
    add_row(as.data.frame(post35_3week))
}

cases <- cases %>%
  rename(
    country = location,
    cases = new_cases_smoothed, 
    # 7-day rolling average
    deaths = new_deaths_smoothed, 
    # 7-day rolling average
    vaccinated = people_vaccinated_per_hundred 
    # Total number of people who received at least one vaccine dose per 100 people in the total population
  )
cases %>%
  arrange(country, date) %>%
  group_by(country, date) %>%
  slice(1) %>% # remove duplicates
  ungroup() %>%
  kbl(
    caption = "December/January peak cases/deaths (7-day smoothed) and 3-weeks after 35% single-dose coverage",
    col.names = c("Country", "Date", "Daily cases", "Daily deaths", "Vaccinated (at least one dose) per hundred"),
    digits = 1
  ) %>%
  kable_styling()
December/January peak cases/deaths (7-day smoothed) and 3-weeks after 35% single-dose coverage
Country Date Daily cases Daily deaths Vaccinated (at least one dose) per hundred
Canada 2021-01-09 9626.9 152.9 0.8
Canada 2021-05-05 7904.4 46.7 35.5
Canada 2021-05-25 3694.0 38.6 52.8
Chile 2021-01-25 4204.0 64.6 0.3
Chile 2021-01-31 3999.9 74.1 0.3
Chile 2021-03-31 6822.1 104.7 35.6
Chile 2021-04-21 6733.7 115.0 41.0
Germany 2020-12-23 25757.0 662.3 NA
Germany 2021-01-13 21809.1 894.4 1.1
Germany 2021-05-12 11391.3 187.1 35.4
Germany 2021-05-25 6667.4 151.7 40.7
Hungary 2020-12-03 5685.3 156.4 NA
Hungary 2020-12-23 2813.6 176.4 NA
Hungary 2021-04-21 3669.3 217.4 35.4
Hungary 2021-05-12 1116.7 102.1 45.8
Israel 2021-01-17 8624.3 47.7 25.5
Israel 2021-01-25 6117.1 64.9 31.5
Israel 2021-01-30 6404.0 56.7 35.0
Israel 2021-02-20 3238.1 25.0 49.4
United Kingdom 2021-01-09 59828.6 902.6 NA
United Kingdom 2021-01-23 37239.4 1253.0 9.4
United Kingdom 2021-03-13 5872.7 149.6 35.6
United Kingdom 2021-04-03 4046.7 36.4 46.4
United States 2021-01-08 251056.9 3112.4 2.0
United States 2021-01-13 245674.9 3432.6 NA
United States 2021-01-14 239591.6 3432.6 2.9
United States 2021-04-10 68192.0 985.1 35.0
United States 2021-05-01 49594.4 675.6 43.7

Reduction in cases and deaths after 35% of single-dose coverage achieved

Calculate ratio of reduction in cases and deaths (seven day rolling average) three weeks after single-dose coverage is achieved.

effectiveness <- data.frame(
  country = character(),
  max_cases = numeric(),
  max_deaths = numeric(),
  post35_3wk = ymd(),
  post35_3wk_cases = numeric(),
  post35_3wk_deaths = numeric(),
  astrazeneca_proportion = numeric()
)

for (i in unique(cases$country)) {
  country_cases <- cases %>% filter(country == i)
  peak_cases <- country_cases %>%
    filter(date >= ymd("2020-12-01") & date <= ymd("2021-01-31")) %>%
    filter(cases == max(cases)) %>%
    pull(cases)
  peak_deaths <- country_cases %>%
    filter(date >= ymd("2020-12-01") & date <= ymd("2021-01-31")) %>%
    filter(deaths == max(deaths)) %>%
    pull(deaths)
  vax_35_date <- country_cases %>%
    filter(vaccinated >= 35 & vaccinated < 36) %>%
    pull(date) # date of vaccinations between 0.35 and 0.36
  post35_3week <- country_cases[which.min(abs((vax_35_date + weeks(3)) - country_cases$date)),] 
  # closest date to 3 weeks post vax_35_date
  
  effectiveness <- effectiveness %>%
    add_row(
      country = i,
      max_cases = peak_cases,
      max_deaths = peak_deaths,
      post35_3wk = post35_3week$date,
      post35_3wk_cases = post35_3week$cases,
      post35_3wk_deaths = post35_3week$deaths
      )
}

effectiveness <- effectiveness %>%
  mutate(
    case_reduction_ratio = max_cases/post35_3wk_cases,
    death_reduction_ratio = max_deaths/post35_3wk_deaths
  ) %>%
  mutate(
    log_case_reduction = log(case_reduction_ratio),
    log_death_reduction = log(death_reduction_ratio)
  )
effectiveness %>%
  select(country, case_reduction_ratio, death_reduction_ratio) %>%
  kbl(
    caption = "Reduction in COVID-19 cases and deaths after 35% single-dose vaccination coverage",
    col.names = c("Country", "Case reduction ratio", "Death reduction ratio"),
    digits = 2
  ) %>%
  kable_styling()
Reduction in COVID-19 cases and deaths after 35% single-dose vaccination coverage
Country Case reduction ratio Death reduction ratio
Canada 2.61 3.96
Canada 2.61 3.96
Chile 0.62 0.64
Germany 3.86 5.90
Hungary 5.09 1.73
Israel 2.66 2.59
United Kingdom 14.78 34.40
United States 5.06 5.08
United States 5.06 5.08

Astra-Zeneca usage

Neither Israel or United States used Astra-Zeneca vaccines at all.

Chile received its first shipment of Astra-Zeneca vaccines on April 23, several weeks after Chile first achieved 35% coverage (March 30, 2021). Until April, Chile used CoronaVac and Pfizer COVID vaccinations.

Hungary and German vaccination brand delivery available via the European Centre for Disease and Control.

Vaccines codes (ECDC):

  • COM - Pfizer/Comirnaty
  • BECNBG - CNBG/Sinopharm
  • SPU - Sputnik V
  • AZ - Astra-Zeneca
  • MOD - Moderna
  • JANSS - Janssen

Hungary reached 35% single-dose vaccine coverage on 21st April 2021 (week 16 of 2021).

Germany reached 35% single-dose vaccine coverage on 12th May 2021 (week 19 of 2021).

effectiveness[effectiveness$country == "United States", "astrazeneca_proportion"] <- 0
effectiveness[effectiveness$country == "Israel", "astrazeneca_proportion"] <- 0
effectiveness[effectiveness$country == "Chile", "astrazeneca_proportion"] <- 0

# sourced from European Centre for Disease Prevention and Control
# week 19 of 2021
hungary_summary <- hungary_vax %>%
  filter(Week != "2021-17" & Week != "2021-18" & Week != "2021-19") %>%
  # Hungary reached 35% single-dose vaccine coverage on 21st April 2021 (week 16 of 2021).
  # so exclude week 17, 18 and 19
  select(`Vaccine brand`, `First dose`) %>%
  rename(Vaccine = `Vaccine brand`, Doses = `First dose`) %>%
  group_by(Vaccine) %>%
  summarize(Doses = sum(Doses))
# unlike most other European countries, Hungary sourced more than a third
# of its COVID vaccines from Russian or Chinese brands

effectiveness[effectiveness$country == "Hungary", "astrazeneca_proportion"] <- 
  hungary_summary[hungary_summary$Vaccine == "AZ", "Doses"]/sum(hungary_summary$Doses)

hungary_summary %>%
  arrange(desc(Doses)) %>%
  kbl(caption = "Hungary cumulative first-dose administered COVID vaccine brands, week 19 of 2021") %>%
  kable_styling()
Hungary cumulative first-dose administered COVID vaccine brands, week 19 of 2021
Vaccine Doses
COM 1521273
SPU 758476
AZ 577000
BECNBG 543357
MOD 204781
JANSS 0
# sourced from European Centre for Disease Prevention and Control
# week 19 of 2021
germany_summary <- germany_vax %>%
  select(`Vaccine brand`, `First dose`) %>%
  rename(Vaccine = `Vaccine brand`, Doses = `First dose`) %>%
  group_by(Vaccine) %>%
  summarize(Doses = sum(Doses))

effectiveness[effectiveness$country == "Germany", "astrazeneca_proportion"] <- 
  germany_summary[germany_summary$Vaccine == "AZ", "Doses"]/sum(germany_summary$Doses)

germany_summary %>%
  arrange(desc(Doses)) %>%
  kbl(caption = "Germany cumulative first-dose administered COVID vaccine brands, week 19 of 2021") %>%
  kable_styling()
Germany cumulative first-dose administered COVID vaccine brands, week 19 of 2021
Vaccine Doses
COM 21314356
AZ 7324805
MOD 2117049
JANSS 49226

The United Kingdom reached 35% single-dose vaccine coverage on 13th March 2021.

From Coronavirus Vaccine - summary of Yellow Card Reporting (data included: 09/12/2020 to 14/03/2021) - old edition accessed using Wayback Machine:

  • ‘As of 14 March, an estimated 10.9 million first doses of the Pfizer/BioNTech vaccine and 13.7 million doses of the Oxford University/AstraZeneca vaccine, had been administered, and around 1.3 million second doses, mostly the Pfizer/BioNTech vaccine, had been administered.’

Canada reached 35% single-dose vaccine coverage on 5th May 2021.

A CTV news report “Risks of rare blood clot even lower with second dose of AstraZeneca, experts say”, dated 20th May 2021, reports:

  • ‘Britain, which has had the AstraZeneca shot in circulation much longer than Canada, has administered more than 23 million first doses of the vaccine’
    • note that this report (20th May 2021) is two months after the date of the United Kingdom achieving 35% single-dose vaccine coverage.
  • ‘In Canada, approximately 2.1 million people have received one dose of the AstraZeneca vaccine’
# CTV news report dated May 20, 2021
# 'Britain, which has had the AstraZeneca shot in circulation much longer than Canada, has administered more than 23 million first doses of the vaccine'
# 'In Canada, approximately 2.1 million people have received one dose of the AstraZeneca vaccine'

effectiveness[effectiveness$country == "United Kingdom", "astrazeneca_proportion"] <- 
  13.7/(10.9 + 13.7)

effectiveness[effectiveness$country == "Canada", "astrazeneca_proportion"] <-
  2100000/(owid %>%
              filter(location == "Canada", date == as.Date("2021-05-20")) %>%
              pull(people_vaccinated))

owid %>%
  filter(location == "United Kingdom" | location == "Canada") %>%
  filter(date == as.Date("2021-05-20")) %>%
  select(location, people_vaccinated) %>%
  rename(Country = location, `Total vaccinated` = people_vaccinated) %>%
  kbl(caption = "Total vaccinated, as of 20th May 2021 (source 'Our World in Data')") %>%
  kable_styling()
Total vaccinated, as of 20th May 2021 (source ‘Our World in Data’)
Country Total vaccinated
Canada 18312021
United Kingdom 37518614

Relationship between AstraZeneca vaccine usage and reduction in COVID-19 cases and deaths

Comparing the rates of cases and deaths from peak of December 2020-January 2021 to three weeks after 35% single-dose coverage has been achieved.

ggplot(
  effectiveness,
  aes(
    x = astrazeneca_proportion, y = log_case_reduction,
    color = astrazeneca_proportion + log_case_reduction,
    label = country
  )) +
  xlim(-0.05, .75) +
  geom_point(shape = 16, size = 2, show.legend = FALSE) +
  theme_minimal() +
  theme(legend.position = "none") +
  scale_color_gradient(low = "#0091ff", high = "#f0650e") +
  geom_text(aes(label = country), hjust = -0.1, vjust = 0) +
  ggtitle("Reduction in COVID-19 cases compared to December 2020/January 2021 peak", subtitle = "Three weeks after 35% single-dose coverage") +
  xlab("Astra-Zeneca as proportion of administered vaccines") +
  ylab("Ratio of case reduction (log)")

ggplot(
  effectiveness,
  aes(
    x = astrazeneca_proportion, y = log_death_reduction,
    color = astrazeneca_proportion + log_death_reduction,
    label = country
  )) +
  xlim(-0.05, .75) +
  geom_point(shape = 16, size = 2, show.legend = FALSE) +
  theme_minimal() +
  theme(legend.position = "none") +
  scale_color_gradient(low = "#0091ff", high = "#f0650e") +
  geom_text(aes(label = country), hjust = -0.1, vjust = 0) +
  ggtitle("Reduction in COVID-19 deaths compared to December 2020/January 2021 peak", subtitle = "Three weeks after 35% single-dose coverage") +
  xlab("Astra-Zeneca as proportion of administered vaccines") +
  ylab("Ratio of death reduction (log)")

References

Full source code on Github

Associated Press Television News. 2021. “First AstraZeneca COVID Vaccines Arrive In Chile.” RepublicWorld. https://www.republicworld.com/world-news/south-america/first-astrazeneca-covid-vaccines-arrive-in-chile.html.
“Chile Receives First Shipment of Vaccines Against COVID-19 Through COVAX.” 2021. Unicef. https://www.unicef.org/lac/en/press-releases/chile-receives-first-shipment-vaccines-against-covid-19-through-covax.
“Coronavirus Vaccine - Summary of Yellow Card Reporting.” 2021. Medicines and Healthcare Products Regulatory Agency. https://www.gov.uk/government/publications/coronavirus-covid-19-vaccine-adverse-reactions/.
“COVID-19 Vaccine Rollout Overview (Week 19, 2021).” 2021. European Center for Disease Prevention and Control. https://covid19-vaccine-report.ecdc.europa.eu/#3_Uptake_of_at_least_one_vaccine_dose_among_adults.
Jane Chambers. 2021. “Chile Sees Covid Surge Despite Vaccination Success.” BBC. https://www.bbc.com/news/world-latin-america-56731801.
Max Roser, Hannah Ritchie, Esteban Ortiz-Ospina, and Joe Hasell. 2020. “Coronavirus Pandemic (COVID-19).” https://ourworldindata.org/coronavirus.
“Risks of Rare Blood Clot Even Lower with Second Dose of AstraZeneca, Experts Say.” 2021. CTV News. https://www.ctvnews.ca/health/coronavirus/risks-of-rare-blood-clot-even-lower-with-second-dose-of-astrazeneca-experts-say-1.5437073.
Ritchie, Mathieu, E., Ritchie, H., and Ortiz-Ospina, E. et al. 2021. “A Global Database of COVID-19 Vaccinations.” Nat Hum Behav. https://doi.org/10.1038/s41562-021-01122-8.
Tamás Vaski. 2021. “Hungary’s Vaccinations: Pfizer Used for Most, Sinopharm Catching Up.” Hungary Today. https://hungarytoday.hu/hungary-vaccinations-coronavirus-operative-board-statistics-vaccination-amounts-number-of-inoculated/.