EURO Group Tables and Knockout Performances

data viz ggplot2 football euro 2020

Everybody loves cinderella stories and fairy-tale runs.

qntkhvn https://github.com/qntkhvn
July 3, 2021

Motivation

It all began with a suggestion from my professor, @statsinthewild:

@qntkhvn I think something like this would be cool. And you could go back to past Euros to see what this would look like.

It's possible that THREE teams who finished third in their group will be in the final 8! And no one from group F advanced to final 8! pic.twitter.com/T9fUeXuoOs

— Volume Tweeter (@StatsInTheWild) June 29, 2021

So I went back and looked at the group positions and knockout outcomes for the national teams that competed in the 6 most recent UEFA EURO tournaments - 1996, 2000, 2004, 2008, 2012, and 2016. These are competitions with 16 (1996-2012) or 24 (2016) teams, as prior to ’96, the EURO was either an 8- or 4-team tournament. I visited the Wikipedia page of each one those EUROs and quickly collected the desired data. After that, I utilized R packages ggplot2 and plotly to make a simple heatmap of how the national teams performed in past European championships.

Heatmap

As you can see, each observation in the heatmap below is represented by their group name (y-axis), group position (x-axis), tournament result (coded by different color values), and tournament year. The plot is also interactive, so you can actually reveal more information about the teams and their performances by simply moving your cursor inside the plot area. As always, you can find my code below. For this blog post, I’m not going to go over the details of the plotting process, rather I’m going to save it for a future tutorial blog post.

Show code
library(tidyverse)
library(plotly)

Team <- c(
  "England", "Netherlands", "Scotland", "Switzerland",
  "France", "Spain", "Bulgaria", "Romania",
  "Germany", "Czech Republic", "Italy", "Russia",
  "Portugal", "Croatia", "Denmark", "Turkey",
  
  "Portugal", "Romania", "England", "Germany",
  "Italy", "Turkey", "Belgium", "Sweden",
  "Spain", "Yugoslavia", "Norway", "Slovenia",
  "Netherlands", "France", "Czech Republic", "Denmark",
  
  "Portugal", "Greece", "Spain", "Russia",
  "France", "England", "Croatia", "Switzerland",
  "Sweden", "Denmark", "Italy", "Bulgaria",
  "Czech Republic", "Netherlands", "Germany", "Latvia",
  
  "Portugal", "Turkey", "Czech Republic", "Switzerland",
  "Croatia", "Germany", "Austria", "Poland",
  "Netherlands", "Italy", "Romania", "France",
  "Spain", "Russia", "Sweden", "Greece",
  
  "Czech Republic", "Greece", "Russia", "Poland",
  "Germany", "Portugal", "Denmark", "Netherlands",
  "Spain", "Italy", "Croatia", "Ireland",
  "England", "France", "Ukraine", "Sweden",
  
  "France", "Switzerland", "Albania", "Romania",
  "Wales", "England", "Slovakia", "Russia",
  "Germany", "Poland", "Northern Ireland", "Ukraine",
  "Croatia", "Spain", "Turkey", "Czech Republic",
  "Italy", "Belgium", "Ireland", "Sweden",
  "Hungary", "Iceland", "Portugal", "Austria"
)

Outcome <- c(
  "SF", "QF", "G", "G", "SF", "QF", "G", "G", "W", "F", "G", "G", "QF", "QF", "G", "G", # 96 
  "SF", "QF", "G", "G", "F", "QF", "G", "G", "QF", "QF", "G", "G", "SF", "W", "G", "G", # 00
  "F", "W", "G", "G", "QF", "QF", "G", "G", "QF", "QF", "G", "G", "SF", "SF", "G", "G", # 04
  "QF", "SF", "G", "G", "QF", "F", "G", "G", "QF", "QF", "G", "G", "W", "SF", "G", "G", # 08
  "QF", "QF", "G", "G", "SF", "SF", "G", "G", "W", "F", "G", "G", "QF", "QF", "G", "G", # 12
  "F", "R16", "G", "G", "SF", "R16", "R16", "G", "SF", "QF", "R16", "G", # 16 
  "R16", "R16", "G", "G", "QF", "QF", "R16", "G", "R16", "QF", "W", "G"
)

euro <- tibble(
  Year = c(rep(seq(1996, 2012, 4), each = 16), rep(2016, 24)),
  Team,
  Group = c(rep(rep(c("A", "B", "C", "D"), each = 4), 6), 
            rep(c("E", "F"), each = 4)),
  Place = rep(1:4, 26),
  Outcome
) %>% 
  mutate(Outcome = factor(Outcome, levels = c("G", "R16", "QF", "SF", "F", "W")))

p <- euro %>% 
  mutate(Group = fct_rev(Group)) %>% 
  ggplot(aes(x = Place, y = Group, fill = Outcome, group = Team)) +
  geom_tile(color = "white") +
  facet_wrap(~ Year, scales = "free_y") +
  ggtitle("EURO Performances and Group Positions") +
  scale_fill_brewer(palette = "PRGn") +
  theme_minimal() +
  theme(
    plot.title = element_text(hjust = 0.5),
    legend.title = element_blank(),
    axis.title = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major = element_blank()
  )

p %>%
  ggplotly() %>%
  layout(legend = list(
    x = 0.25,
    y = -0.1,
    orientation = "h"
  ))

Thoughts

There are a couple of things I wanted to highlight here:

What’s Next?

There are many other situations where we could apply this type of plot to. In regard to sports, heatmap is a great visual tool when we have data on rankings, or seedings; for example, grand slam tennis, NCAA March Madness, or even weekly tables for football leagues. I will eventually write up a blog post on how to make heatmaps with ggplot2. Stay tuned!

Citation

For attribution, please cite this work as

qntkhvn (2021, July 3). The Q: EURO Group Tables and Knockout Performances. Retrieved from https://qntkhvn.netlify.app/posts/2021-07-03-euro-groups/

BibTeX citation

@misc{qntkhvn2021euro,
  author = {qntkhvn, },
  title = {The Q: EURO Group Tables and Knockout Performances},
  url = {https://qntkhvn.netlify.app/posts/2021-07-03-euro-groups/},
  year = {2021}
}