The FootballData package and dashboard

Getting football data, then summarising a fan’s mood based on the teams performance

Table of Contents

Intro

A colleague made the following site in jest (https://coppingr.com/), as an outgoing present for the founding head of our department. Inspired this effort, I wondered whether you could make a service to predict a football supporters mood, by leveraging one of the many APIs out there that serve live or near live football data.

This post is an example of using the package I wrote that spawned from that idea.

Documentation: https://epijim.github.io/FootballData/

Setup environment

library(FootballData)
library(tidyverse)
library(glue)
library(emo) #devtools::install_github("hadley/emo")


# table helper - switch between html vs markdown
table_it <- function(x, type = "markdown"){
 if(type == "markdown") return(x %>% knitr::kable(escape = FALSE))
 x %>% kableExtra::kbl(escape = F) %>%
   kableExtra::kable_classic("striped", full_width = TRUE)
}

Get competitions

First I will pull all the leagues that are in the free tier, from this particular API (football-data.org) and show them on a table.

# Get competitions
competitions <- football_get_competition() %>%
  filter(
    # filter to what's in the free plan
    plan == "TIER_ONE"
    &
    # The data structures returned for country level competitions is
    # different, so let's remove it
    !country %in% c("World","Europe"))

competitions %>% select(flag,country,league) %>% table_it()
flagcountryleague
NABrazilSérie A
EnglandChampionship
EnglandPremier League
FranceLigue 1
GermanyBundesliga
ItalySerie A
NetherlandsEredivisie
PortugalPrimeira Liga
SpainPrimera Division

Dive into a league

Now I’ll pick my local premier league (Bundesliga) and pull info on it’s current standings.

# Get league info
league <- competitions %>%
  filter(country == "Germany" & league == "Bundesliga") %>%
  pull(competition_id) %>%
  football_get_standing()

league %>%
  mutate(team = paste(crest,team)) %>%
  select(team,playedGames,form,points) %>%
  table_it()
teamplayedGamesformpoints
FC Bayern München20W,W,W,W,W48
RB Leipzig20W,W,L,W,D41
VfL Wolfsburg20W,W,W,W,D38
Eintracht Frankfurt20W,W,W,D,W36
Bayer 04 Leverkusen20W,L,L,W,L35
Borussia Dortmund20L,W,L,L,D32
Borussia Mönchengladbach20L,D,W,W,D32
SC Freiburg20W,L,W,D,L30
1. FC Union Berlin20L,D,L,L,W29
VfB Stuttgart20L,W,L,L,D25
SV Werder Bremen19D,W,L,W,D22
TSG 1899 Hoffenheim20L,L,W,W,D22
FC Augsburg20L,L,W,L,L22
1. FC Köln20W,W,L,W,D21
Hertha BSC20L,L,L,L,D17
DSC Arminia Bielefeld19L,L,W,D,W17
1. FSV Mainz 0520W,L,W,L,D13
FC Schalke 0420L,D,L,L,L8

Get upcoming games for a team

From the API, I can also get more details on my local team.

team_of_interest <- "SC Freiburg"

league %>%
  filter(team == team_of_interest) %>%
  pull(team_id) %>%
  football_get_upcoming() %>%
  arrange(utcDate) %>%
  slice(1) %>%
  mutate(
    text = glue(
      "Today is {Sys.Date()}\n",
      "{team_of_interest}'s next game is on {as.Date(utcDate)}\n",
      "Home: {homeTeam$name}; Away {awayTeam$name}"
    )
  ) %>%
  pull(text)
#> Today is 2021-02-09
#> SC Freiburg's next game is on 2021-02-13
#> Home: SV Werder Bremen; Away SC Freiburg

Calculate the moods

Now that I have used the data prep functions, I can jump into the ‘fun’ bit of this package. Using the live data I can run simple rules to try and figure out what the mood of a supporter may be right now.

Below - I run the mood logic on the Bundesliga.

emoji_bad <- emo::ji("poop")
emoji_good <- emo::ji("beer")

add_bad <- function(x){
  gsub("-",emoji_bad,x = x)
}

add_good <- function(x){
  gsub("\\+",emoji_good,x = x)
}

league %>%
  football_calculate_competition_metrics() %>%
  mutate(
    team = paste(crest,team),
    metric_mood = as.character(metric_mood),
    metric_mood = case_when(
        str_detect(metric_mood,"Jubilant") ~ kableExtra::cell_spec(
            metric_mood, color = "black", background = "#98FB98"
        ),
        str_detect(metric_mood,"Optimistic") ~ kableExtra::cell_spec(
            metric_mood, color = "black", background = "#e5f5f9"
        ),
        str_detect(metric_mood,"Ambivalent") ~ kableExtra::cell_spec(
            metric_mood, color = "black", background = "#f7fcb9"
        ),
        str_detect(metric_mood,"Doldrums") ~ kableExtra::cell_spec(
            metric_mood, color = "black", background = "#ffc4c4"
        ),
        TRUE ~ metric_mood
    )
  ) %>%
  rename(mood = metric_mood,numeric_mood = metric_mood_numeric) %>%
  mutate_at(vars(matches("metric_")),add_bad) %>%
  mutate_all(add_good) %>%
  select(team,points,starts_with("metric_"),numeric_mood,mood) %>%
  table_it()
If viewing this on a standard screen - you man need to scroll right to see all the columns.
teampointsmetric_season_positionmetric_recent_winsmetric_back_of_netnumeric_moodmood
FC Bayern München48🍺🍺🍺🍺 top of the league🍺🍺🍺🍺 5 win streak🍺🍺🍺 strikers are on form11Jubilant: FC Bayern München are on a roll
RB Leipzig41🍺🍺 strong league position🍺🍺 recent wins🍺🍺🍺 strikers are on form7Jubilant: RB Leipzig are on a roll
VfL Wolfsburg38🍺🍺 strong league position🍺🍺 recent wins🍺 scored more than conceded5Optimistic: VfL Wolfsburg are doing well
Eintracht Frankfurt36🍺 safe in middle of league🍺🍺 recent wins🍺 scored more than conceded4Optimistic: Eintracht Frankfurt are doing well
Bayer 04 Leverkusen35🍺 safe in middle of league💩💩 more recent losses than wins🍺 scored more than conceded0Ambivalent: Bayer 04 Leverkusen aren’t doing well, but hey - at least they aren’t FC Schalke 04
Borussia Dortmund32🍺 safe in middle of league💩💩 more recent losses than wins🍺 scored more than conceded0Ambivalent: Borussia Dortmund aren’t doing well, but hey - at least they aren’t FC Schalke 04
Borussia Mönchengladbach32🍺 safe in middle of leaguedrew last game🍺 scored more than conceded2Optimistic: Borussia Mönchengladbach are doing well
SC Freiburg30🍺 safe in middle of league💩 lost last game🍺 scored more than conceded1Optimistic: SC Freiburg are doing well
1. FC Union Berlin29💩 so so season🍺 won last game🍺 scored more than conceded1Optimistic: 1. FC Union Berlin are doing well
VfB Stuttgart25💩 so so season💩💩 more recent losses than wins🍺 scored more than conceded-2Ambivalent: VfB Stuttgart aren’t doing well, but hey - at least they aren’t FC Schalke 04
SV Werder Bremen22💩 so so seasondrew last game💩 conceded more than scored-2Ambivalent: SV Werder Bremen aren’t doing well, but hey - at least they aren’t FC Schalke 04
TSG 1899 Hoffenheim22💩 so so seasondrew last game💩 conceded more than scored-2Ambivalent: TSG 1899 Hoffenheim aren’t doing well, but hey - at least they aren’t FC Schalke 04
FC Augsburg22💩 so so season💩💩 more recent losses than wins💩 conceded more than scored-4Ambivalent: FC Augsburg aren’t doing well, but hey - at least they aren’t FC Schalke 04
1. FC Köln21💩 so so season🍺🍺 recent wins💩 conceded more than scored0Ambivalent: 1. FC Köln aren’t doing well, but hey - at least they aren’t FC Schalke 04
Hertha BSC17💩💩 poor season💩💩 no recent wins💩 conceded more than scored-5Doldrums: Hertha BSC are doing poor. Now’s a good time to bad mouth the manager.
DSC Arminia Bielefeld17💩💩 poor season🍺 won last game💩💩💩 defense is a seive-4Ambivalent: DSC Arminia Bielefeld aren’t doing well, but hey - at least they aren’t FC Schalke 04
1. FSV Mainz 0513💩💩💩💩 relagation?drew last game💩💩💩 defense is a seive-7Doldrums: 1. FSV Mainz 05 are doing poor. Now’s a good time to bad mouth the manager.
FC Schalke 048💩💩💩💩 relagation?💩💩 no recent wins💩💩💩 defense is a seive-9Doldrums: FC Schalke 04 are doing poorly. Avoid supporters at all costs.

Shiny app

Not interested in the Bundesliga? I also wrapped the package in a shiny app available here: https://epijim.shinyapps.io/football_moods/

Exactly what are the metrics that define mood?

The current logic is contained in the following Github file. As a summary, following gist highlights just the relevant code present when this post was authored.

James Black
James Black
PhD (Cantab)

James Black. Kiwi | Epidemiologist | Data Scientist | Engineering enthusiast.

comments powered by Disqus

Related