Created
November 7, 2022 23:56
-
-
Save OTStats/562472add29a1adbeac8b1295e9419e8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -- Created by Owen Thompson (@OTStats) | |
# 2022-09-29 | |
# -- Load libraries | |
library(tidyverse) | |
library(ggtext) | |
library(ggforce) | |
library(lubridate) | |
library(worldfootballR) | |
library(glue) | |
library(ggimage) | |
library(showtext) | |
font_add_google("Lato") | |
showtext_auto() | |
# -- Import Data | |
spi_raw <- read_csv("https://projects.fivethirtyeight.com/soccer-api/club/spi_matches.csv") | |
# Prep data | |
matches <- spi_raw %>% | |
transmute(date, | |
league, | |
league_id, | |
team = team1, | |
opponent = team2, | |
teamGoal = score1, | |
oppGoal = score2, | |
result = case_when(score1 > score2 ~ "W", | |
score1 < score2 ~ "L", | |
score1 == score2 ~ "D"), | |
ha = "Home", | |
team_spi = spi1, | |
opponent_spi = spi2, | |
team_xg = xg1, | |
opponent_xg = xg2) %>% | |
bind_rows( | |
spi_raw %>% | |
transmute(date, | |
league, | |
league_id, | |
team = team2, | |
opponent = team1, | |
teamGoal = score2, | |
oppGoal = score1, | |
result = case_when(score1 < score2 ~ "W", | |
score1 > score2 ~ "L", | |
score1 == score2 ~ "D"), | |
ha = "Away", | |
team_spi = spi2, | |
opponent_spi = spi1, | |
team_xg = xg2, | |
opponent_xg = xg1)) %>% | |
mutate(game_goal_diff = teamGoal - oppGoal) %>% | |
mutate(result_points = case_when(result == "W" ~ 3, | |
result == "D" ~ 1, | |
TRUE ~ 0)) | |
doParallel::registerDoParallel() | |
dates <- seq(ymd("20220226"), ymd("20220310"), by = "day") | |
results <- fotmob_get_matches_by_date(date = dates) | |
filtered_results <- results %>% | |
dplyr::select(primary_id, ccode, league_name = name, matches) %>% | |
dplyr::filter(league_name == "Major League Soccer", ccode == "USA") | |
# one way of getting data out of the results | |
unnested_results <- filtered_results %>% | |
tidyr::unnest_longer(col = matches) %>% | |
unnest(everything()) %>% | |
unnest(cols = c("home", "away"), names_sep = "_") %>% | |
unnest(cols = status) %>% | |
janitor::clean_names() | |
match_ids <- unnested_results %>% | |
distinct() %>% | |
pull(id) | |
details_df <- fotmob_get_match_details(match_ids) | |
shots <- details_df %>% | |
unnest(shots) %>% | |
mutate(team = if_else(team_id == home_team_id, home_team, away_team)) | |
mls_team_color_df <- shots %>% | |
distinct(team, team_color, team_id) | |
mls_df <- matches %>% | |
filter(league_id == 1951) %>% | |
mutate(season = | |
case_when(between(date, ymd("20220226"), ymd("20220913")) ~ 2022, | |
# between(date, ymd("20210416"), ymd("20211107")) ~ 2021, | |
# between(date, ymd("20200229"), ymd("20200308")) ~ 2020, | |
# between(date, ymd("20200812"), ymd("20201108")) ~ 2020, | |
# between(date, ymd("20190302"), ymd("20191008")) ~ 2019, | |
TRUE ~ 0)) %>% | |
filter(season == 2022) %>% | |
arrange(team, date) %>% | |
with_groups(team, mutate, matchday = row_number()) %>% | |
mutate(half = if_else(matchday <= 17, "first", "second")) %>% | |
group_by(team, half) %>% | |
summarize(xGF = mean(team_xg, na.rm = TRUE), | |
xGC = mean(opponent_xg, na.rm = TRUE), | |
.groups = "drop") %>% | |
# pivot_wider(id_cols = team, names_from = season, values_from = c(xGF, xGC)) %>% | |
mutate(team = case_when( | |
team == "Los Angeles Galaxy" ~ "LA Galaxy", | |
team == "Atlanta United FC" ~ "Atlanta United", | |
team == "Chicago Fire" ~ "Chicago Fire FC", | |
team == "Minnesota United FC" ~ "Minnesota United", | |
team == "Houston Dynamo" ~ "Houston Dynamo FC", | |
team == "Montreal Impact" ~ "CF Montreal", | |
team == "Orlando City SC" ~ "Orlando City", | |
TRUE ~ team | |
)) %>% | |
inner_join(mls_team_color_df, by = "team") %>% | |
mutate(logo = if_else(half == "second", glue("https://images.fotmob.com/image_resources/logo/teamlogo/{team_id}.png"), | |
NULL)) | |
pacman::p_load(ggpath) | |
# library(gghighlight) | |
# p <- | |
mls_df %>% | |
ggplot() + | |
geom_link2(aes(x = xGF, | |
y = xGC, | |
# alpha = stat(index), | |
size = stat(index), | |
color = team_color, | |
group = team), n = 100, lineend = "round") + | |
scale_size(range = c(.01, 1.8)) + | |
guides(size = "none", alpha = "none") + | |
scale_color_identity() + | |
# geom_image(aes(x = xGF, y = xGC, image = logo), size = .05) + | |
geom_from_path(aes(x = xGF, y = xGC, path = logo), width = .045, alpha = 0.9) + | |
coord_fixed() + | |
# scale_y_continuous(breaks = seq(.75, 2, by = 0.25)) + | |
scale_x_continuous(breaks = seq(.75, 2.25, by = 0.25)) + | |
scale_y_reverse(breaks = seq(.75, 2, by = 0.25)) + | |
labs(title = "Team xG evolution during the 2022 season", | |
subtitle = "Start of tail: First 17 matches of 2022; End of tail: Second half of 2022 matches to date\nCreated by: Owen Thompson | @OTStats", | |
x = "Average xG Created", | |
y = "Average xG Conceded", | |
caption = glue("Data as of {format(Sys.Date()-1)}\nSource: 538")) + | |
theme_minimal() + | |
theme(text = element_text(family = "Lato"), | |
plot.title.position = "plot", | |
plot.title = element_text(size = 20), | |
plot.background = element_rect(fill = "#f2f4f5", color = "#f2f4f5")) + | |
# --- Upper Right --- | |
annotate(geom = "text", | |
x = 2, y = .98, | |
label = "There's Good...", family = "Lato", | |
hjust = 1, vjust = 0.3, alpha = 0.5, size = 3) + | |
# --- Upper Right... and there's Philly --- | |
annotate(geom = "text", | |
x = 2.2, y = 1.025, | |
label = "...and then there's Philly", family = "Lato", | |
hjust = 0.5, vjust = 0.3, alpha = 0.5, size = 3) + | |
# --- Bad, bad, bad --- | |
annotate(geom = "text", | |
x = 1.1, y = 2, | |
label = "All around bad", family = "Lato", | |
# hjust = 0.5, vjust = 0, | |
alpha = 0.5, size = 3) + | |
# --- Fun to watch --- | |
annotate(geom = "text", | |
x = 1.85, y = 1.9, | |
label = "Fun to watch", family = "Lato", | |
# hjust = 0.5, vjust = 0, | |
alpha = 0.5, size = 3) + | |
# --- Boring --- | |
annotate(geom = "text", | |
x = 1.1, y = 1.05, | |
label = "Defensively sound, can't create", family = "Lato", | |
# hjust = 0.5, vjust = 0, | |
alpha = 0.5, size = 3) | |
source("Developer/OT/gameplots/R/add_logo.R") | |
ggsave(filename = "20220929 MLS 2022 season xG evolution.png", | |
plot = last_plot(), | |
width = 9, height = 8, dpi = "retina") | |
# -- Add Leauge logo to plot | |
plot_with_logo <- add_logo(plot_path = "20220929 MLS 2022 season xG evolution.png", | |
logo_path = glue("https://images.fotmob.com/image_resources/logo/leaguelogo/130.png"), | |
logo_position = "top right", | |
logo_scale = 12) | |
magick::image_write(plot_with_logo, "20220929 MLS 2022 season xG evolution.png") | |
Author
OTStats
commented
Nov 7, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment