Eurovision Song Contest (ESC): First Analyses

Timm Walker

2021/05/23

readxl::excel_sheets("eurovision_song_contest_1975_2019.xlsx")
## [1] "Data"    "Remarks" "Sources"
eurovision_raw <- readxl::read_excel("eurovision_song_contest_1975_2019.xlsx", sheet = "Data")
eurovision_proc <- eurovision_raw %>%
  janitor::clean_names() %>%
  filter(is.na(duplicate)) %>%
  select(-duplicate) %>%
  rename(show = semi_final,
         vote_type = jury_or_televoting) %>%
  mutate_at(vars(from_country), ~str_replace(., "The Netherands", "The Netherlands")) %>%
  mutate(
    show_type = as_factor(if_else(show == "f", "final", "semi-final")),
    show = fct_recode(show,
                      "final" = "f",
                      "single-semi" = "sf",
                      "semi-one" = "sf1",
                      "semi-two" = "sf2"),
    vote_type = fct_recode(vote_type,
                           "jury" = "J",
                           "televote" = "T"))
eurovision_country_code <- function(x) {
  
  code <- case_when(
    x == "Serbia & Montenegro" ~ "SCG",
    x == "Yugoslavia" ~ "YUG",
    TRUE ~ countrycode::countrycode(x, "country.name", "iso3c")
  )
  
  return(code)
  
}
eurovision_dt <- eurovision_proc %>%
  mutate_at(vars(from_country, to_country), eurovision_country_code)
## Warning in countrycode::countrycode(x, "country.name", "iso3c"): Some values were not matched unambiguously: Serbia & Montenegro, Yugoslavia

## Warning in countrycode::countrycode(x, "country.name", "iso3c"): Some values were not matched unambiguously: Serbia & Montenegro, Yugoslavia
calc_scores <- function(data) {
  
  scores <- data %>%
    group_by(to_country) %>%
    summarise(score = sum(points)) %>%
    ungroup()
  
  return(scores)
  
}

get_winner <- function(data) {
  
  winner <- data %>%
    filter(score == max(score)) %>%
    rename(winning_country = to_country, winning_score = score)
  
  return(winner)
  
}
eurovision_scores <- eurovision_dt %>% 
  group_by(year, show) %>%
  nest(data = c(vote_type, from_country, to_country, points)) %>%
  mutate(
    scores = map(data, calc_scores),
    winner = map(scores, get_winner)) %>%
  select(-data) %>%
  unnest(winner) %>%
  unnest(scores) %>%
  mutate(
    overall_winner = winning_country == to_country,
    outcome = case_when(
      overall_winner & show_type == "final" ~ "win-final",
      overall_winner & show_type == "semi-final" ~ "win-semi",
      TRUE ~ "no-win"
    )) %>%
  rename(country = to_country) %>%
  group_by(country) %>% 
  mutate(prev_wins = cumsum(overall_winner)) %>%
  ungroup() %>%
  mutate(prev_wins = if_else(prev_wins > 0, prev_wins - 1, 0))
deu_scores <- eurovision_scores %>%
  filter(country == "DEU") %>%
  filter(!(year == 1991 & winning_country == "FRA")) %>%
  select(year, DEU = score, Winner = winning_score) %>%
  pivot_longer(cols = -year, names_to = "country", values_to = "score")
p_line <- ggplot(deu_scores, aes(x = year, y = score, colour = country)) +
  geom_line() +
  geom_point() +
  labs(title = "DEU and winning country's Eurovision scores 1975-2019",
       x = "Year",
       y = "Score") +
  scale_colour_manual(values = c("Winner" = "goldenrod2", "DEU" = "dodgerblue3")) +
  theme(plot.title = element_text(lineheight = 1.1), legend.title = element_blank(), legend.position = "bottom")

p_line

library(gganimate)

anim_line <- p_line +
  transition_reveal(year)

options(gganimate.dev_args = list(width = 900, height = 900*9/16))
anim_save("eurovision_deu_winner.gif", anim_line, fps = 5)

anim_line

world_countries <- rnaturalearth::ne_countries(returnclass = "sf")

world_cropped <- sf::st_crop(world_countries, xmin = -30, xmax = 155, ymin = -90, ymax = 90)
## although coordinates are longitude/latitude, st_intersection assumes that they are planar
## Warning: attribute variables are assumed to be spatially constant throughout all
## geometries
eurovision_countries <- world_cropped %>%
  filter(iso_a3 %in% unique(eurovision_scores$country)) %>%
  left_join(eurovision_scores, by = c("iso_a3" = "country")) %>%
  mutate(relative_score = score/winning_score)
p_map1 <- ggplot(eurovision_countries %>% filter(year == 2019)) +
  geom_sf(aes(fill = relative_score), size = 0.1, colour = "grey90") +
  scale_fill_gradient(low = "dodgerblue3", high = "goldenrod2",
                      guide = guide_none()) +
  labs(
    title = "Relative scores in the Eurovision Finals: 2019",
    subtitle = "Relative score = country score / winning score"
  ) +
  theme(
    axis.text = element_blank()
  )

p_map1

p_map2 <- ggplot(eurovision_countries) +
  geom_sf(aes(fill = relative_score), size = 0.1, colour = "grey90") +
  scale_fill_gradient(low = "dodgerblue3", high = "goldenrod2", 
                      guide = guide_none()) +
  labs(
    title = "Relative scores in the Eurovision Finals: {closest_state}",
    subtitle = "Relative score = country score / winning score"
  ) +
  theme(
    axis.text = element_blank()
  )

anim_map <- p_map2 +
  transition_states(year) +
  theme(
    plot.title = element_text(size = 16),
    axis.title = element_text(size = 14),
    axis.text = element_text(size = 12),
    legend.text = element_text(size = 12)
  )

options(gganimate.dev_args = list(width = 750, height = 610))
anim_save("eurovision_timeseries", anim_map, fps = 5)

p_map2

anim_map