
Extracting data from fotmob
Tony ElHabr
2023-05-13
Source:vignettes/extract-fotmob-data.Rmd
extract-fotmob-data.Rmd
Overview
This package is designed to allow users to extract various world football results and player statistics from the following popular football (soccer) data sites:
Installation
You can install the CRAN version of worldfootballR
with:
install.packages("worldfootballR")
You can install the released version of worldfootballR
from GitHub
with:
# install.packages("devtools")
devtools::install_github("JaseZiv/worldfootballR")
Usage
Package vignettes have been built to help you get started with the package.
- For functions to extract data from FBref, see here
- For functions to extract data from Transfermarkt, see here
- For functions to extract data from Understat, see here
- For functions to extract data for international matches from FBref, see here
- For functions to load pre-scraped data, see here
This vignette will cover the functions to extract data from fotmob.com
League Season-Level Data
fotmob has data for just about every league that you can think of, including all of the Big 5 leagues.
Team Stats
Use fotmob_get_season_stats
to retrieve values for a
specified stat, season, and league. See the docs for the function for a
complete list of stat_name
s. Note that some stats are only
available for either team or player, and that some names are different
for teams and players (e.g. "Expected goals"
for
"team"
and "Expected goals (xG)"
for
"player"
).
epl_team_xg_2021 <- fotmob_get_season_stats(
country = "ENG",
league_name = "Premier League",
season_name = "2020/2021",
stat_name = "Expected goals",
team_or_player = "team"
)
epl_team_xg_2021 %>%
dplyr::select(
league_id,
league_name,
season_id,
season_name,
team_id,
team_name = participant_name,
matches_played,
xg = stat_value,
g = sub_stat_value
) %>%
dplyr::glimpse()
#> Rows: 20
#> Columns: 9
#> $ league_id <int> 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,…
#> $ league_name <chr> "Premier League", "Premier League", "Premier League", "…
#> $ season_id <chr> "15382", "15382", "15382", "15382", "15382", "15382", "…
#> $ season_name <chr> "2020/2021", "2020/2021", "2020/2021", "2020/2021", "20…
#> $ team_id <int> 8456, 8650, 8455, 10260, 8654, 8463, 8197, 8586, 10252,…
#> $ team_name <chr> "Manchester City", "Liverpool", "Chelsea", "Manchester …
#> $ matches_played <int> 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,…
#> $ xg <dbl> 68.9, 68.4, 62.9, 60.9, 56.7, 56.4, 56.1, 53.8, 53.8, 5…
#> $ g <dbl> 83, 68, 58, 73, 62, 62, 68, 68, 55, 55, 40, 47, 46, 47,…
If you know the league_id
, you can use it instead of
passing in values for country
and
league_name
.
fotmob_get_season_stats(
league_id = 47,
season_name = "2020/2021",
stat_name = "Expected goals",
team_or_player = "team"
)
#> # A tibble: 20 × 20
#> country league_name league_id season_name season_id stat_league_name
#> <chr> <chr> <int> <chr> <chr> <chr>
#> 1 ENG Premier League 47 2020/2021 15382 Premier League
#> 2 ENG Premier League 47 2020/2021 15382 Premier League
#> 3 ENG Premier League 47 2020/2021 15382 Premier League
#> 4 ENG Premier League 47 2020/2021 15382 Premier League
#> 5 ENG Premier League 47 2020/2021 15382 Premier League
#> 6 ENG Premier League 47 2020/2021 15382 Premier League
#> 7 ENG Premier League 47 2020/2021 15382 Premier League
#> 8 ENG Premier League 47 2020/2021 15382 Premier League
#> 9 ENG Premier League 47 2020/2021 15382 Premier League
#> 10 ENG Premier League 47 2020/2021 15382 Premier League
#> 11 ENG Premier League 47 2020/2021 15382 Premier League
#> 12 ENG Premier League 47 2020/2021 15382 Premier League
#> 13 ENG Premier League 47 2020/2021 15382 Premier League
#> 14 ENG Premier League 47 2020/2021 15382 Premier League
#> 15 ENG Premier League 47 2020/2021 15382 Premier League
#> 16 ENG Premier League 47 2020/2021 15382 Premier League
#> 17 ENG Premier League 47 2020/2021 15382 Premier League
#> 18 ENG Premier League 47 2020/2021 15382 Premier League
#> 19 ENG Premier League 47 2020/2021 15382 Premier League
#> 20 ENG Premier League 47 2020/2021 15382 Premier League
#> # ℹ 14 more variables: stat_name <chr>, stat <tibble[,1]>,
#> # participant_name <chr>, particiant_id <int>, team_id <int>,
#> # team_color <chr>, stat_value <dbl>, sub_stat_value <dbl>,
#> # minutes_played <int>, matches_played <int>, stat_value_count <int>,
#> # rank <int>, participant_country_code <chr>, team_name <lgl>
You can retrieve data for multiple leagues, seasons, or stat types at the same time.
team_xgs_2021 <- fotmob_get_season_stats(
country = c("ITA", "ESP"),
league_name = c("Serie A", "LaLiga"),
season_name = c("2020/2021", "2021/2022"),
stat_name = c("Expected goals", "xG conceded"),
team_or_player = "team"
)
## 2 leagues x 20 teams x 2 seasons x 2 stats = 160 rows
team_xgs_2021 %>% nrow()
#> [1] 160
International tournament data can be retrieved, during the right time of year.
## Can only check on CL after group stages and briefly after the final.
m <- lubridate::month(Sys.Date())
if(m >= 1 && m <= 5) {
fotmob_get_season_stats(
league_id = 42,
season_name = "2020/2021",
stat_name = "Expected goals",
team_or_player = "team"
)
}
#> # A tibble: 32 × 20
#> country league_name league_id season_name season_id stat_league_name
#> <chr> <chr> <int> <chr> <chr> <chr>
#> 1 INT Champions League 42 2020/2021 15237 Champions League
#> 2 INT Champions League 42 2020/2021 15237 Champions League
#> 3 INT Champions League 42 2020/2021 15237 Champions League
#> 4 INT Champions League 42 2020/2021 15237 Champions League
#> 5 INT Champions League 42 2020/2021 15237 Champions League
#> 6 INT Champions League 42 2020/2021 15237 Champions League
#> 7 INT Champions League 42 2020/2021 15237 Champions League
#> 8 INT Champions League 42 2020/2021 15237 Champions League
#> 9 INT Champions League 42 2020/2021 15237 Champions League
#> 10 INT Champions League 42 2020/2021 15237 Champions League
#> # ℹ 22 more rows
#> # ℹ 14 more variables: stat_name <chr>, stat <tibble[,1]>,
#> # participant_name <chr>, particiant_id <int>, team_id <int>,
#> # team_color <chr>, stat_value <dbl>, sub_stat_value <dbl>,
#> # minutes_played <int>, matches_played <int>, stat_value_count <int>,
#> # rank <int>, participant_country_code <chr>, team_name <lgl>
You can potentially get cup data for leagues in the future, although at this time Fotmob does not have this data for most leagues, seasons, or stats.
## Not run to avoid error.
fotmob_get_season_stats(
league_id = 47,
season_name = "2020/2021",
stat_name = "Expected goals",
team_or_player = "team",
stat_league_name = "FA Cup"
)
At this time, only one team_or_player
can be retrieved
at a time.
Player Stats
You can also use fotmob_get_season_stats
to retrieve
stats for players.
epl_player_xg_2021 <- fotmob_get_season_stats(
country = "ENG",
league_name = "Premier League",
season = "2020/2021",
stat_name = "Expected goals (xG)",
team_or_player = "player"
)
epl_player_xg_2021 %>%
dplyr::select(
league_id,
league_name,
season_id,
season_name,
team_id,
## NOTE: particiant_id is a typo on behalf of fotmob! We leave it as is.
player_id = particiant_id,
player_name = participant_name,
minutes_played,
matches_played,
xg = stat_value,
g = sub_stat_value
) %>%
dplyr::glimpse()
#> Rows: 433
#> Columns: 11
#> $ league_id <int> 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,…
#> $ league_name <chr> "Premier League", "Premier League", "Premier League", "…
#> $ season_id <chr> "15382", "15382", "15382", "15382", "15382", "15382", "…
#> $ season_name <chr> "2020/2021", "2020/2021", "2020/2021", "2020/2021", "20…
#> $ team_id <int> 8586, 8197, 8650, 8463, 8668, 10260, 10252, 8650, 8654,…
#> $ player_id <int> 194165, 286119, 292462, 302241, 612150, 422685, 540088,…
#> $ player_name <chr> "Harry Kane", "Jamie Vardy", "Mohamed Salah", "Patrick …
#> $ minutes_played <int> 3085, 2844, 3079, 3062, 2874, 3105, 3329, 2810, 1983, 2…
#> $ matches_played <int> 35, 34, 37, 38, 33, 37, 37, 35, 26, 33, 33, 26, 31, 36,…
#> $ xg <dbl> 20.2, 19.9, 19.3, 18.3, 16.3, 16.1, 14.6, 14.2, 13.6, 1…
#> $ g <dbl> 23, 15, 22, 17, 16, 18, 14, 11, 10, 8, 12, 12, 13, 9, 6…
Match Results
Matches for one or multiple leagues can be retrieved with
fotmob_get_league_matches
.
league_matches <- fotmob_get_league_matches(
country = c("ENG", "ESP" ),
league_name = c("Premier League", "LaLiga")
)
league_matches_unnested <- league_matches %>%
dplyr::select(match_id = id, home, away) %>%
tidyr::unnest_wider(c(home, away), names_sep = "_")
dplyr::glimpse(league_matches_unnested)
#> Rows: 760
#> Columns: 7
#> $ match_id <chr> "3900932", "3900933", "3900934", "3900935", "3900937", …
#> $ home_name <chr> "Crystal Palace", "Fulham", "AFC Bournemouth", "Leeds U…
#> $ home_shortName <chr> "Crystal Palace", "Fulham", "Bournemouth", "Leeds", "Ne…
#> $ home_id <chr> "9826", "9879", "8678", "8463", "10261", "8586", "8668"…
#> $ away_name <chr> "Arsenal", "Liverpool", "Aston Villa", "Wolverhampton W…
#> $ away_shortName <chr> "Arsenal", "Liverpool", "Aston Villa", "Wolves", "Nottm…
#> $ away_id <chr> "9825", "8650", "10252", "8602", "10203", "8466", "8455…
Data can even be retrieved for past seasons.
fotmob_get_league_matches(
country = "GER",
league_name = "1. Bundesliga",
season = "2020/2021"
)
#> # A tibble: 306 × 7
#> round round_name page_url id home$name away$name status$utcTime
#> <lgl> <lgl> <chr> <chr> <chr> <chr> <chr>
#> 1 NA NA /match/3399144/mat… 3399… Bayern M… Schalke … 2020-09-18T16…
#> 2 NA NA /match/3399150/mat… 3399… 1. FC Kö… TSG Hoff… 2020-09-19T11…
#> 3 NA NA /match/3399148/mat… 3399… Eintrach… Arminia … 2020-09-19T11…
#> 4 NA NA /match/3399151/mat… 3399… Werder B… Hertha B… 2020-09-19T11…
#> 5 NA NA /match/3399149/mat… 3399… Union Be… FC Augsb… 2020-09-19T11…
#> 6 NA NA /match/3399152/mat… 3399… VfB Stut… SC Freib… 2020-09-19T11…
#> 7 NA NA /match/3399145/mat… 3399… Borussia… Borussia… 2020-09-19T14…
#> 8 NA NA /match/3399146/mat… 3399… RB Leipz… Mainz 05 2020-09-20T11…
#> 9 NA NA /match/3399147/mat… 3399… Wolfsburg Bayer Le… 2020-09-20T14…
#> 10 NA NA /match/3399169/mat… 3399… Hertha B… Eintrach… 2020-09-25T16…
#> # ℹ 296 more rows
#> # ℹ 9 more variables: home$shortName <chr>, $id <chr>, away$shortName <chr>,
#> # $id <chr>, status$finished <lgl>, $started <lgl>, $cancelled <lgl>,
#> # $scoreStr <chr>, $reason <df[,2]>
fotmob also lists data for all leagues by date. Use
fotmob_get_matches_by_date
to select matches occurring on
specific day(s) and filter down to the league(s) that you care about.
Since fotmob_get_league_matches
does not allow for
selecting seasons, the only way to get matches from prior seasons is to
use fotmob_get_matches_by_date
results <- fotmob_get_matches_by_date(date = c("20210925", "20210926"))
dplyr::glimpse(results)
#> Rows: 1,011
#> Columns: 34
#> $ ccode <chr> "ENG", "ENG", "ENG", "ENG", "ENG", "ENG", "EN…
#> $ id <int> 47, 47, 47, 47, 47, 47, 47, 87, 87, 87, 87, 5…
#> $ primary_id <int> 47, 47, 47, 47, 47, 47, 47, 87, 87, 87, 87, 5…
#> $ name <chr> "Premier League", "Premier League", "Premier …
#> $ match_id <int> 3609981, 3609986, 3609983, 3609984, 3609985, …
#> $ match_league_id <int> 47, 47, 47, 47, 47, 47, 47, 87, 87, 87, 87, 5…
#> $ match_time <chr> "25.09.2021 13:30", "25.09.2021 13:30", "25.0…
#> $ home_id <int> 8455, 10260, 8668, 8463, 8197, 9817, 9937, 98…
#> $ home_score <int> 0, 0, 2, 1, 2, 1, 3, 1, 1, 2, 0, 1, 1, 3, 6, …
#> $ home_name <chr> "Chelsea", "Man United", "Everton", "Leeds", …
#> $ home_long_name <chr> "Chelsea", "Manchester United", "Everton", "L…
#> $ home_pen_score <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ away_id <int> 8456, 10252, 9850, 8654, 8191, 10261, 8650, 9…
#> $ away_score <int> 1, 1, 0, 2, 2, 1, 3, 0, 1, 0, 0, 0, 1, 1, 0, …
#> $ away_name <chr> "Man City", "Aston Villa", "Norwich", "West H…
#> $ away_long_name <chr> "Manchester City", "Aston Villa", "Norwich Ci…
#> $ away_pen_score <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ match_eliminated_team_id <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ match_status_id <int> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, …
#> $ match_tournament_stage <chr> "6", "6", "6", "6", "6", "6", "6", "7", "7", …
#> $ match_status_utc_time <chr> "2021-09-25T11:30:00.000Z", "2021-09-25T11:30…
#> $ match_status_finished <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRU…
#> $ match_status_started <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRU…
#> $ match_status_cancelled <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FAL…
#> $ match_status_score_str <chr> "0 - 1", "0 - 1", "2 - 0", "1 - 2", "2 - 2", …
#> $ match_status_awarded <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ match_time_ts <dbl> 1.632569e+12, 1.632569e+12, 1.632578e+12, 1.6…
#> $ internal_rank <int> 30, 30, 30, 30, 30, 30, 30, 10, 10, 10, 10, 2…
#> $ live_rank <int> 101, 101, 101, 101, 101, 101, 101, 100, 100, …
#> $ simple_league <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FAL…
#> $ parent_league_id <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ is_group <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ group_name <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ parent_league_name <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
You should see international tournaments in the output from
fotmob_get_matches_by_date()
.
results <- fotmob_get_matches_by_date("20220412")
results %>%
dplyr::filter(name == "Champions League Final Stage", ccode == "INT")
#> # A tibble: 2 × 32
#> ccode id primary_id name match_id match_league_id match_time home_id
#> <chr> <int> <int> <chr> <int> <int> <chr> <int>
#> 1 INT 875880 42 Champions… 3846346 875880 12.04.202… 9823
#> 2 INT 875880 42 Champions… 3846342 875880 12.04.202… 8633
#> # ℹ 24 more variables: home_score <int>, home_name <chr>, home_long_name <chr>,
#> # away_id <int>, away_score <int>, away_name <chr>, away_long_name <chr>,
#> # match_eliminated_team_id <int>, match_status_id <int>,
#> # match_tournament_stage <chr>, match_status_utc_time <chr>,
#> # match_status_finished <lgl>, match_status_started <lgl>,
#> # match_status_cancelled <lgl>, match_status_score_str <chr>,
#> # match_status_aggregated_str <chr>, match_time_ts <dbl>, …
Standings
Standings for one or multiple leagues can be retrieved with
fotmob_get_league_tables
. Currently only the current
season’s standings can be retrieved.
league_tables <- fotmob_get_league_tables(
country = c("ENG", "ESP" ),
league_name = c("Premier League", "LaLiga")
)
# or
# league_tables <- fotmob_get_league_tables(league_id = c(47, 87))
away_league_tables <- league_tables %>%
dplyr::filter(table_type == "away")
dplyr::glimpse(away_league_tables)
#> Rows: 40
#> Columns: 18
#> $ league_id <int> 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47…
#> $ page_url <chr> "/leagues/47/overview/premier-league", "/leagues/4…
#> $ table_type <chr> "away", "away", "away", "away", "away", "away", "a…
#> $ table_name <chr> "Arsenal", "Manchester City", "Newcastle United", …
#> $ table_short_name <chr> "Arsenal", "Man City", "Newcastle", "Brighton", "M…
#> $ table_id <int> 9825, 8456, 10261, 10204, 10260, 10252, 9879, 8586…
#> $ table_page_url <chr> "/teams/9825/overview/arsenal", "/teams/8456/overv…
#> $ table_deduction <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
#> $ table_played <int> 18, 16, 18, 16, 18, 18, 17, 17, 17, 17, 18, 17, 18…
#> $ table_wins <int> 12, 10, 8, 7, 7, 6, 6, 5, 5, 5, 4, 5, 4, 2, 4, 4, …
#> $ table_draws <int> 3, 3, 7, 4, 3, 4, 2, 5, 4, 4, 7, 2, 4, 8, 2, 2, 3,…
#> $ table_losses <int> 3, 3, 3, 5, 8, 8, 9, 7, 8, 8, 7, 10, 10, 8, 12, 12…
#> $ table_scores_str <chr> "35-17", "30-14", "30-17", "30-26", "21-33", "17-2…
#> $ table_goal_con_diff <int> 18, 16, 13, 4, -12, -7, -1, -7, -4, -6, -7, -23, -…
#> $ table_pts <int> 39, 33, 31, 25, 24, 22, 20, 20, 19, 19, 19, 17, 16…
#> $ table_idx <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,…
#> $ table_qual_color <chr> "#2AD572", "#2AD572", "#2AD572", "#2AD572", "#0046…
#> $ table_ongoing <df[,14]> <data.frame[26 x 14]>
Note that the output of fotmob_get_league_tables
for a
tournament has more columns.
m <- lubridate::month(Sys.Date())
if(m >= 1 && m <= 5) {
cl_table <- fotmob_get_league_tables(league_id = 42)
cl_table %>%
dplyr::filter(table_type == "all") %>%
dplyr::glimpse()
}
#> Rows: 32
#> Columns: 23
#> $ league_id <int> 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42…
#> $ page_url <chr> "/leagues/42/overview/champions-league", "/leagues…
#> $ ccode <chr> "INT", "INT", "INT", "INT", "INT", "INT", "INT", "…
#> $ group_id <int> 879535, 879535, 879535, 879535, 879536, 879536, 87…
#> $ group_page_url <chr> "/leagues/879535/overview/grp-a?season=2022-2023",…
#> $ group_name <chr> "Grp. A", "Grp. A", "Grp. A", "Grp. A", "Grp. B", …
#> $ ongoing <list> [], [], [], [], [], [], [], [], [], [], [], [], […
#> $ table_type <chr> "all", "all", "all", "all", "all", "all", "all", "…
#> $ table_name <chr> "Napoli", "Liverpool", "Ajax", "Rangers", "FC Port…
#> $ table_short_name <chr> "Napoli", "Liverpool", "Ajax", "Rangers", "FC Port…
#> $ table_id <int> 9875, 8650, 8593, 8548, 9773, 8342, 8178, 9906, 98…
#> $ table_page_url <chr> "/teams/9875/overview/napoli", "/teams/8650/overvi…
#> $ table_deduction <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
#> $ table_ongoing <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
#> $ table_played <int> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,…
#> $ table_wins <int> 5, 5, 2, 0, 4, 3, 1, 1, 6, 3, 2, 0, 3, 3, 2, 2, 4,…
#> $ table_draws <int> 0, 0, 0, 0, 0, 2, 2, 2, 0, 1, 1, 0, 2, 1, 1, 0, 1,…
#> $ table_losses <int> 1, 1, 4, 6, 2, 1, 3, 3, 0, 2, 3, 6, 1, 2, 3, 4, 1,…
#> $ table_scores_str <chr> "20-6", "17-6", "11-16", "2-22", "12-7", "7-4", "4…
#> $ table_goal_con_diff <int> 14, 11, -5, -20, 5, 3, -4, -4, 16, 3, 0, -19, 2, -…
#> $ table_pts <int> 15, 15, 6, 0, 12, 11, 5, 5, 18, 10, 7, 0, 11, 10, …
#> $ table_idx <int> 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1,…
#> $ table_qual_color <chr> "#2AD572", "#2AD572", "#FFD908", NA, "#2AD572", "#…
Match-Level Data
To get general info for a match, use the
fotmob_get_match_info()
function.
fotmob_matches <- c(3609994, 3610132)
match_info <- fotmob_get_match_info(fotmob_matches)
match_info %>%
dplyr::select(match_id, match_time_utc:dplyr::last_col()) %>%
dplyr::glimpse()
#> Rows: 2
#> Columns: 24
#> $ match_id <int> 3609994, 3610132
#> $ match_time_utc <chr> "Sun, Oct 3, 2021, 15:30 UTC", "Sun, Jan …
#> $ home_team_id <int> 8650, 8455
#> $ home_team <chr> "Liverpool", "Chelsea"
#> $ away_team_id <int> 8456, 8650
#> $ away_team <chr> "Manchester City", "Liverpool"
#> $ match_date_utc_time <chr> "2021-10-03T15:30:00.000Z", "2022-01-02T1…
#> $ tournament_id <int> 47, 47
#> $ tournament_parent_league_id <int> 47, 47
#> $ tournament_link <chr> "/leagues/47/overview/premier-league?seas…
#> $ tournament_league_name <chr> "Premier League", "Premier League"
#> $ tournament_round_name <chr> "Round 7", "Round 21"
#> $ tournament_round <chr> "7", "21"
#> $ tournament_selected_season <chr> "2021/2022", "2021/2022"
#> $ tournament_is_current_season <lgl> FALSE, FALSE
#> $ stadium_name <chr> "Anfield", "Stamford Bridge"
#> $ stadium_city <chr> "Liverpool", "London"
#> $ stadium_country <chr> "England", "England"
#> $ stadium_lat <dbl> 53.43083, 51.48171
#> $ stadium_long <dbl> -2.960853, -0.190975
#> $ referee_img_url <chr> "https://images.fotmob.com/image_resource…
#> $ referee_text <chr> "Paul Tierney", "Anthony Taylor"
#> $ referee_country <chr> "England", "England"
#> $ attendance <int> 53102, 40072
Team-level Stats
To get team-level team stats for a match, use the
fotmob_get_match_team_stats()
function.
match_team_stats <- fotmob_get_match_team_stats(fotmob_matches)
match_team_stats %>%
dplyr::select(match_id, title:dplyr::last_col()) %>%
dplyr::glimpse()
#> Rows: 92
#> Columns: 9
#> $ match_id <int> 3609994, 3609994, 3609994, 3609994, 3609994, 3609994…
#> $ title <chr> "Top stats", "Top stats", "Top stats", "Top stats", …
#> $ key <chr> "top_stats", "top_stats", "top_stats", "top_stats", …
#> $ stats_title <chr> "Ball possession", "Expected goals (xG)", "Total sho…
#> $ stats_key <chr> "BallPossesion", "expected_goals", "total_shots", "b…
#> $ home_value <chr> "48", "0.90", "6", "3", "1", "423 (84%)", "10", "2",…
#> $ away_value <chr> "52", "1.24", "12", "1", "1", "443 (84%)", "12", "5"…
#> $ stats_type <chr> "graph", "text", "text", "text", "text", "text", "te…
#> $ stats_highlighted <chr> "away", "away", "away", "home", "equal", "away", "ho…
Stats are categorized into one of several groups, by
title
.
Match Shooting Locations
To get shooting locations for an individual match along with expected
goals (xG), expected goals on target (xGoT), etc., use the
fotmob_get_match_details()
function.
match_details <- fotmob_get_match_details(fotmob_matches)
dplyr::glimpse(match_details)
#> Rows: 43
#> Columns: 40
#> $ match_id <int> 3609994, 3609994, 3609994, 3609994, 3609994, …
#> $ match_round <chr> "7", "7", "7", "7", "7", "7", "7", "7", "7", …
#> $ league_id <int> 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 4…
#> $ league_name <chr> "Premier League", "Premier League", "Premier …
#> $ league_round_name <chr> "Round 7", "Round 7", "Round 7", "Round 7", "…
#> $ parent_league_id <int> 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 4…
#> $ parent_league_season <chr> "2021/2022", "2021/2022", "2021/2022", "2021/…
#> $ match_time_utc <chr> "Sun, Oct 3, 2021, 15:30 UTC", "Sun, Oct 3, 2…
#> $ home_team_id <int> 8650, 8650, 8650, 8650, 8650, 8650, 8650, 865…
#> $ home_team <chr> "Liverpool", "Liverpool", "Liverpool", "Liver…
#> $ away_team_id <int> 8456, 8456, 8456, 8456, 8456, 8456, 8456, 845…
#> $ away_team <chr> "Manchester City", "Manchester City", "Manche…
#> $ id <dbl> 2338869639, 2338876703, 2338881401, 233888382…
#> $ event_type <chr> "AttemptSaved", "AttemptSaved", "AttemptSaved…
#> $ team_id <int> 8650, 8456, 8456, 8456, 8456, 8456, 8456, 845…
#> $ player_id <int> 292462, 312765, 815006, 169200, 169200, 16920…
#> $ player_name <chr> "Mohamed Salah", "Jack Grealish", "Phil Foden…
#> $ x <dbl> 92.50000, 91.50000, 100.35345, 97.57017, 97.0…
#> $ y <dbl> 50.72688, 49.53580, 42.00375, 46.31286, 28.95…
#> $ min <int> 8, 15, 21, 24, 34, 38, 40, 40, 50, 59, 63, 67…
#> $ min_added <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ is_blocked <lgl> TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, TRUE, …
#> $ is_on_target <lgl> TRUE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE, F…
#> $ blocked_x <dbl> 94.48246, 93.40000, 101.87069, 104.05172, NA,…
#> $ blocked_y <dbl> 48.20459, 47.57401, 39.54500, 0.00000, NA, 28…
#> $ goal_crossed_y <dbl> 34.6100000, 34.8387500, 32.7800000, 0.3279621…
#> $ goal_crossed_z <dbl> 1.21999999, 1.21999999, 0.36600000, 0.5329473…
#> $ expected_goals <dbl> 0.01901395, 0.02792695, 0.42046532, 0.0984095…
#> $ expected_goals_on_target <dbl> NA, NA, 0.6931, NA, NA, NA, NA, NA, 0.0412, 0…
#> $ shot_type <chr> "LeftFoot", "RightFoot", "LeftFoot", "LeftFoo…
#> $ situation <chr> "FromCorner", "RegularPlay", "RegularPlay", "…
#> $ period <chr> "FirstHalf", "FirstHalf", "FirstHalf", "First…
#> $ is_own_goal <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FAL…
#> $ on_goal_shot_x <dbl> 0.8386243, 0.7781085, 1.3227513, 2.0000000, 1…
#> $ on_goal_shot_y <dbl> 0.32275132, 0.32275132, 0.09682540, 0.0158275…
#> $ on_goal_shot_zoom_ratio <dbl> 1.0000000, 1.0000000, 1.0000000, 0.1122593, 0…
#> $ first_name <chr> "Mohamed", "Jack", "Phil", "Kevin", "Kevin", …
#> $ last_name <chr> "Salah", "Grealish", "Foden", "De Bruyne", "D…
#> $ full_name <chr> "Mohamed Salah", "Jack Grealish", "Phil Foden…
#> $ team_color <chr> "#d3171e", "#69A8D8", "#69A8D8", "#69A8D8", "…
Be very careful with interpreting x
and y
from on_goal_shot
. They’re not on the same scale as
x
and y
in the top-level of the result
returned from fotmob_get_match_details
!
Players
You can also extract players from matches with the
fotmob_get_match_players()
function.
players <- fotmob_get_match_players(fotmob_matches)
dplyr::glimpse(players)
#> Rows: 80
#> Columns: 77
#> $ match_id <int> 3609994, 3609994, 3609994, 360999…
#> $ team_id <int> 8650, 8650, 8650, 8650, 8650, 865…
#> $ team_name <chr> "Liverpool", "Liverpool", "Liverp…
#> $ id <chr> "319784", "38807", "171698", "209…
#> $ using_opta_id <lgl> FALSE, FALSE, FALSE, FALSE, FALSE…
#> $ first_name <chr> "Alisson", "James", "Joel", "Virg…
#> $ last_name <chr> "Becker", "Milner", "Matip", "van…
#> $ image_url <chr> "https://images.fotmob.com/image_…
#> $ page_url <chr> "/players/319784/alisson-becker",…
#> $ shirt <chr> "1", "7", "32", "4", "26", "14", …
#> $ is_home_team <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRU…
#> $ time_subbed_on <int> NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ time_subbed_off <int> NA, 78, NA, NA, NA, NA, NA, NA, N…
#> $ usual_position <int> 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, …
#> $ position_row <int> 0, 1, 1, 1, 1, 3, 3, 3, 5, 5, 5, …
#> $ role <chr> "Keeper", "Defender", "Defender",…
#> $ is_captain <lgl> FALSE, FALSE, FALSE, FALSE, FALSE…
#> $ subbed_out <int> NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ g <int> NA, NA, NA, NA, NA, NA, NA, NA, 1…
#> $ rating_num <chr> "5.7", "7.2", "7.3", "6.6", "7.2"…
#> $ rating_bgcolor <chr> "#f08022", "#1ec853", "#1ec853", …
#> $ is_top_rating <lgl> FALSE, FALSE, FALSE, FALSE, FALSE…
#> $ is_match_finished <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRU…
#> $ fantasy_score_num <chr> "1", "1", "1", "1", "1", "2", "1"…
#> $ fantasy_score_bgcolor <chr> "purple", "purple", "purple", "pu…
#> $ home_team_id <int> 8650, 8650, 8650, 8650, 8650, 865…
#> $ home_team_color <chr> "#d3171e", "#d3171e", "#d3171e", …
#> $ away_team_id <int> 8456, 8456, 8456, 8456, 8456, 845…
#> $ away_team_color <chr> "#69A8D8", "#69A8D8", "#69A8D8", …
#> $ stats_fot_mob_rating <chr> "5.67", "7.17", "7.32", "6.55", "…
#> $ stats_minutes_played <chr> "90", "78", "90", "90", "90", "90…
#> $ stats_saves <chr> "1", NA, NA, NA, NA, NA, NA, NA, …
#> $ stats_goals_conceded <chr> "2", NA, NA, NA, NA, NA, NA, NA, …
#> $ stats_x_got_faced <chr> "1.35", NA, NA, NA, NA, NA, NA, N…
#> $ stats_accurate_passes <chr> "48/55 (87%)", "27/34 (79%)", "81…
#> $ stats_accurate_long_balls <chr> "9/15 (60%)", "1/3 (33%)", "4/8 (…
#> $ stats_diving_save <chr> "0", NA, NA, NA, NA, NA, NA, NA, …
#> $ stats_saves_inside_box <chr> "1", NA, NA, NA, NA, NA, NA, NA, …
#> $ stats_acted_as_sweeper <chr> "0", NA, NA, NA, NA, NA, NA, NA, …
#> $ stats_punches <chr> "1", NA, NA, NA, NA, NA, NA, NA, …
#> $ stats_throws <chr> "4", NA, NA, NA, NA, NA, NA, NA, …
#> $ stats_high_claim <chr> "1", NA, NA, NA, NA, NA, NA, NA, …
#> $ stats_recoveries <chr> "5", " 6", " 5", " 4", " 7", " 9"…
#> $ stats_fantasy_points <chr> "1", " 1", " 1", " 1", " 1", " 2"…
#> $ stats_touches <chr> "64", "68", "101", "100", "70", "…
#> $ stats_goals <chr> NA, " 0", " 0", " 0", " 0", " 0",…
#> $ stats_assists <chr> NA, " 0", " 0", " 0", " 0", " 0",…
#> $ stats_total_shots <chr> NA, " 0", " 0", " 0", " 0", " 0",…
#> $ stats_chances_created <chr> NA, " 1", " 1", " 0", " 0", " 0",…
#> $ stats_expected_assists_x_a <chr> NA, "0.04", "0.03", "0.01", "0.01…
#> $ stats_passes_into_final_third <chr> NA, " 6", "11", " 9", " 8", "10",…
#> $ stats_accurate_crosses <chr> NA, "2/8 (25%)", NA, NA, "0/4 (0%…
#> $ stats_corners <chr> NA, " 2", NA, NA, " 1", NA, NA, N…
#> $ stats_dispossessed <chr> NA, " 0", " 0", " 0", " 0", " 2",…
#> $ stats_tackles_won <chr> NA, " 0", "3/3 (100%)", " 0", "2/…
#> $ stats_blocks <chr> NA, " 1", " 2", " 1", " 1", NA, N…
#> $ stats_clearances <chr> NA, " 2", " 1", " 2", " 5", NA, N…
#> $ stats_interceptions <chr> NA, " 2", " 4", " 1", " 3", " 2",…
#> $ stats_ground_duels_won <chr> NA, "3/6 (50%)", "3/3 (100%)", "1…
#> $ stats_aerial_duels_won <chr> NA, "1/1 (100%)", " 0", " 0", "2/…
#> $ stats_was_fouled <chr> NA, " 3", " 0", " 0", " 1", " 0",…
#> $ stats_fouls_committed <chr> NA, " 3", " 0", " 0", " 0", " 0",…
#> $ stats_headed_clearance <chr> NA, NA, " 1", " 1", " 2", NA, NA,…
#> $ stats_successful_dribbles <chr> NA, NA, NA, "1/1 (100%)", NA, "1/…
#> $ stats_dribbled_past <chr> NA, NA, NA, " 1", NA, " 3", " 5",…
#> $ stats_offsides <chr> NA, NA, NA, NA, " 1", " 1", NA, N…
#> $ stats_shotmap <chr> NA, NA, NA, NA, NA, NA, "TRUE", N…
#> $ stats_expected_goals_x_g <chr> NA, NA, NA, NA, NA, NA, "0.23", N…
#> $ stats_shot_accuracy <chr> NA, NA, NA, NA, NA, NA, "1/1 (100…
#> $ stats_big_chance_missed <chr> NA, NA, NA, NA, NA, NA, " 1", NA,…
#> $ stats_expected_goals_on_target_x_got <chr> NA, NA, NA, NA, NA, NA, NA, NA, "…
#> $ stats_blocked_shots <chr> NA, NA, NA, NA, NA, NA, NA, NA, "…
#> $ stats_clearance_off_the_line <chr> NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ shotmap <list> <NULL>, <NULL>, <NULL>, <NULL>, …
#> $ is_starter <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRU…
#> $ stats_error_led_to_goal <chr> NA, NA, NA, NA, NA, NA, NA, NA, N…
#> $ stats_last_man_tackle <chr> NA, NA, NA, NA, NA, NA, NA, NA, N…
salah <- players %>% dplyr::filter(id == "292462")
salah_shotmap <- salah %>%
dplyr::select(player_id = id, shotmap) %>%
tidyr::unnest(shotmap)
dplyr::glimpse(salah_shotmap)
#> Rows: 7
#> Columns: 24
#> $ player_id <chr> "292462", "292462", "292462", "292462", "292462"…
#> $ id <dbl> 2338869639, 2338938757, 2338947917, 2371191153, …
#> $ eventType <chr> "AttemptSaved", "AttemptSaved", "Goal", "Attempt…
#> $ teamId <int> 8650, 8650, 8650, 8650, 8650, 8650, 8650
#> $ playerId <int> 292462, 292462, 292462, 292462, 292462, 292462, …
#> $ playerName <chr> "Mohamed Salah", "Mohamed Salah", "Mohamed Salah…
#> $ x <dbl> 92.50000, 80.57039, 101.87069, 90.00000, 101.396…
#> $ y <dbl> 50.72688, 45.26191, 24.27949, 33.31375, 24.13936…
#> $ min <int> 8, 63, 76, 6, 26, 36, 57
#> $ minAdded <lgl> NA, NA, NA, NA, NA, NA, NA
#> $ isBlocked <lgl> TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE
#> $ isOnTarget <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE
#> $ blockedX <dbl> 94.48246, 90.80000, NA, 97.76316, NA, 93.10000, …
#> $ blockedY <dbl> 48.20459, 40.97381, NA, 33.69500, NA, 30.22000, …
#> $ goalCrossedY <dbl> 34.61000, 33.39000, 37.27875, 34.68625, 32.01750…
#> $ goalCrossedZ <dbl> 1.220000, 1.220000, 1.136526, 0.854000, 1.014526…
#> $ expectedGoals <dbl> 0.01901395, 0.05450234, 0.13671082, 0.17167990, …
#> $ expectedGoalsOnTarget <dbl> NA, NA, 0.9744, 0.1107, 0.2217, NA, 0.2100
#> $ shotType <chr> "LeftFoot", "LeftFoot", "RightFoot", "RightFoot"…
#> $ situation <chr> "FromCorner", "FreeKick", "RegularPlay", "Regula…
#> $ period <chr> "FirstHalf", "SecondHalf", "SecondHalf", "FirstH…
#> $ isOwnGoal <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE
#> $ onGoalShot <df[,3]> <data.frame[7 x 3]>
#> $ teamColor <chr> "#d3171e", "#d3171e", "#d3171e", "#d3171e", "#d3…
Note that stats_
columns are returned as characters
(changed in 0.5.9
).
salah_stats <- salah %>%
dplyr::select(player_id = id, tidyselect::vars_select_helpers$starts_with("stats_")) %>%
janitor::remove_empty(which = "cols")
dplyr::glimpse(salah_stats)
#> Rows: 2
#> Columns: 32
#> $ player_id <chr> "292462", "292462"
#> $ stats_fot_mob_rating <chr> "8.85", "7.99"
#> $ stats_minutes_played <chr> "90", "90"
#> $ stats_accurate_passes <chr> "16/19 (84%)", "27/30 (90%)"
#> $ stats_recoveries <chr> " 1", " 2"
#> $ stats_fantasy_points <chr> "10 + 3", " 7"
#> $ stats_touches <chr> "49", "41"
#> $ stats_goals <chr> " 1", " 1"
#> $ stats_assists <chr> " 1", " 0"
#> $ stats_total_shots <chr> " 3", " 4"
#> $ stats_chances_created <chr> " 2", " 2"
#> $ stats_expected_assists_x_a <chr> "0.29", "0.04"
#> $ stats_passes_into_final_third <chr> " 2", " 1"
#> $ stats_accurate_crosses <chr> "1/2 (50%)", NA
#> $ stats_dispossessed <chr> " 1", " 1"
#> $ stats_tackles_won <chr> "3/4 (75%)", " 0"
#> $ stats_clearances <chr> " 1", NA
#> $ stats_interceptions <chr> " 1", " 1"
#> $ stats_ground_duels_won <chr> "10/15 (67%)", "1/5 (20%)"
#> $ stats_aerial_duels_won <chr> "1/2 (50%)", " 0"
#> $ stats_was_fouled <chr> " 1", " 0"
#> $ stats_fouls_committed <chr> " 0", " 1"
#> $ stats_headed_clearance <chr> " 1", NA
#> $ stats_successful_dribbles <chr> "5/8 (63%)", "1/3 (33%)"
#> $ stats_dribbled_past <chr> " 1", NA
#> $ stats_offsides <chr> NA, " 1"
#> $ stats_shotmap <chr> "TRUE", "TRUE"
#> $ stats_expected_goals_x_g <chr> "0.21", "0.71"
#> $ stats_shot_accuracy <chr> "1/1 (100%)", "3/3 (100%)"
#> $ stats_big_chance_missed <chr> NA, " 1"
#> $ stats_expected_goals_on_target_x_got <chr> "0.97", "0.54"
#> $ stats_blocked_shots <chr> " 2", " 1"
Momentum
You can extract momentum data with the
fotmob_get_match_momentum()
function. At time of writing,
momentum is provided in two sets: (1) "main"
(“Using xT
SA-version”) and (2) "alternateModels"
(“Using xT
SA-version without rolling”).
momentum <- fotmob_get_match_momentum(3901251)
dplyr::glimpse(momentum)
#> Rows: 186
#> Columns: 5
#> $ match_id <int> 3901251, 3901251, 3901251, 3901251, 3901251, 3901251, 3901…
#> $ minute <dbl> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, …
#> $ value <int> 0, 5, 5, -5, 38, 76, 100, 54, 33, 16, 16, 11, 54, 38, 33, …
#> $ debug_title <chr> "Using xT SA-version", "Using xT SA-version", "Using xT SA…
#> $ type <chr> "main", "main", "main", "main", "main", "main", "main", "m…
Note that matches before the 2022/23 season do not have momentum available. (And, for the 2022/23 season, momentum only seems available for matches going back to Oct. 18, 2022.)