Skip to contents

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


fotmob Helper Functions

There are currently no helper functions for fotmob functions.


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_names. 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 leagu…¹ leagu…² seaso…³ seaso…⁴ stat_…⁵ stat_…⁶ stat  parti…⁷ parti…⁸
#>    <chr>   <chr>     <int> <chr>   <chr>   <chr>   <chr>   <chr> <chr>     <int>
#>  1 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Manche…       0
#>  2 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Liverp…       0
#>  3 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Chelsea       0
#>  4 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Manche…       0
#>  5 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… West H…       0
#>  6 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Leeds …       0
#>  7 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Leices…       0
#>  8 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Totten…       0
#>  9 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Aston …       0
#> 10 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Arsenal       0
#> 11 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Bright…       0
#> 12 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Everton       0
#> 13 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Newcas…       0
#> 14 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Southa…       0
#> 15 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Fulham        0
#> 16 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Burnley       0
#> 17 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Wolver…       0
#> 18 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… West B…       0
#> 19 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Crysta…       0
#> 20 ENG     Premie…      47 2020/2… 15382   Premie… Expect… expe… Sheffi…       0
#> # … with 10 more variables: 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>, and abbreviated variable names ¹​league_name, ²​league_id,
#> #   ³​season_name, ⁴​season_id, ⁵​stat_league_name, ⁶​stat_name, ⁷​participant_name,
#> #   ⁸​particiant_id

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"
  )
}

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. Only the current season’s matches can be retrieved with this function.

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", "390…
#> $ home_id    <chr> "9826", "9879", "8678", "8463", "10261", "8586", "8668", "8…
#> $ home_name  <chr> "Crystal Palace", "Fulham", "Bournemouth", "Leeds", "Newcas…
#> $ home_score <int> 0, 2, 2, 2, 2, 4, 0, 2, 1, 0, 2, 4, 0, 4, 2, 0, 4, 1, 2, 1,…
#> $ away_id    <chr> "9825", "8650", "10252", "8602", "10203", "8466", "8455", "…
#> $ away_name  <chr> "Arsenal", "Liverpool", "Aston Villa", "Wolves", "Nottm For…
#> $ away_score <int> 2, 2, 0, 1, 0, 1, 1, 2, 2, 2, 1, 2, 0, 0, 2, 0, 0, 0, 2, 1,…

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,005
#> Columns: 39
#> $ ccode                             <chr> "USA", "USA", "USA", "USA", "USA", "…
#> $ id                                <int> 872390, 872390, 872390, 872390, 8723…
#> $ primary_id                        <int> 130, 130, 130, 130, 130, 130, 130, 1…
#> $ name                              <chr> "Major League Soccer", "Major League…
#> $ match_id                          <int> 3561880, 3561881, 3561917, 3561882, …
#> $ match_league_id                   <int> 872390, 872390, 872390, 872390, 8723…
#> $ match_time                        <chr> "25.09.2021 21:30", "26.09.2021 01:0…
#> $ home_id                           <int> 191716, 6580, 6001, 6602, 8314, 2072…
#> $ home_score                        <int> 1, 2, 2, 4, 0, 2, 0, 2, 1, 6, 0, 0, …
#> $ home_name                         <chr> "Philadelphia", "New England", "Colu…
#> $ home_long_name                    <chr> "Philadelphia Union", "New England R…
#> $ home_pen_score                    <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, …
#> $ away_id                           <int> 773958, 267810, 161195, 722265, 5645…
#> $ away_score                        <int> 0, 1, 1, 2, 0, 0, 1, 0, 0, 1, 1, 1, …
#> $ away_name                         <chr> "Atlanta United", "Orlando", "Montre…
#> $ away_long_name                    <chr> "Atlanta United", "Orlando City", "C…
#> $ away_pen_score                    <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, …
#> $ match_eliminated_team_id          <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, …
#> $ match_status_id                   <int> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, …
#> $ match_tournament_stage            <chr> "1", "1", "1", "1", "1", "1", "1", "…
#> $ match_status_finished             <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, …
#> $ match_status_started              <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, …
#> $ match_status_cancelled            <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, F…
#> $ match_status_score_str            <chr> "1 - 0", "2 - 1", "2 - 1", "4 - 2", …
#> $ match_status_start_date_str       <chr> "Sep 25, 2021", "Sep 25, 2021", "Sep…
#> $ match_status_start_date_str_short <chr> "25. Sep.", "25. Sep.", "25. Sep.", …
#> $ short                             <chr> "FT", "FT", "FT", "FT", "FT", "FT", …
#> $ long                              <chr> "Full-Time", "Full-Time", "Full-Time…
#> $ match_status_awarded              <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, …
#> $ match_status_start_time_str       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, …
#> $ match_time_ts                     <dbl> 1.632584e+12, 1.632596e+12, 1.632598…
#> $ match_tv                          <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, …
#> $ parent_league_id                  <int> 130, 130, 130, 130, 130, 130, 130, 1…
#> $ internal_rank                     <int> 10, 10, 10, 10, 10, 10, 10, 10, 10, …
#> $ live_rank                         <int> 100, 100, 100, 100, 100, 100, 100, 1…
#> $ simple_league                     <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, F…
#> $ is_group                          <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, …
#> $ group_name                        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, …
#> $ parent_league_name                <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, …

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 × 37
#>   ccode     id primary_id name   match…¹ match…² match…³ home_id home_…⁴ home_…⁵
#>   <chr>  <int>      <int> <chr>    <int>   <int> <chr>     <int>   <int> <chr>  
#> 1 INT   875880         42 Champ… 3846346  875880 12.04.…    9823       1 Bayern…
#> 2 INT   875880         42 Champ… 3846342  875880 12.04.…    8633       2 Real M…
#> # … with 27 more variables: 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_finished <lgl>,
#> #   match_status_started <lgl>, match_status_cancelled <lgl>,
#> #   match_status_score_str <chr>, match_status_start_date_str <chr>,
#> #   match_status_start_date_str_short <chr>, short <chr>, long <chr>, …

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", "Brighton & Hove Alb…
#> $ table_short_name    <chr> "Arsenal", "Man City", "Brighton", "Man United", "…
#> $ table_id            <int> 9825, 8456, 10204, 10260, 8586, 9879, 8678, 8654, …
#> $ 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_ongoing       <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
#> $ table_played        <int> 4, 4, 3, 3, 3, 4, 4, 4, 3, 4, 3, 3, 3, 3, 3, 3, 3,…
#> $ table_wins          <int> 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,…
#> $ table_draws         <int> 0, 2, 0, 0, 2, 1, 1, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1,…
#> $ table_losses        <int> 1, 0, 1, 1, 0, 2, 2, 3, 2, 3, 1, 1, 1, 1, 1, 2, 2,…
#> $ table_scores_str    <chr> "9-3", "9-4", "5-3", "2-4", "5-3", "5-6", "4-16", …
#> $ table_goal_con_diff <int> 6, 5, 2, -2, 2, -1, -12, -2, -3, -4, -1, -1, -1, -…
#> $ table_pts           <int> 9, 8, 6, 6, 5, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1,…
#> $ 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…

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()
}

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_date_date_formatted:dplyr::last_col()) %>%
  dplyr::glimpse()
#> Rows: 2
#> Columns: 16
#> $ match_id                  <int> 3609994, 3610132
#> $ match_date_date_formatted <chr> "Sun, Oct 3 2021", "Sun, Jan 2 2022"
#> $ match_date_time_formatted <chr> "11:30 a.m.", "11:30 a.m."
#> $ tournament_id             <int> 47, 47
#> $ tournament_link           <chr> "/leagues/47/overview/premier-league", "/lea…
#> $ tournament_league_name    <chr> "Premier League", "Premier League"
#> $ tournament_round          <chr> "Round 7", "Round 21"
#> $ 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_resources/l…
#> $ 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: 7
#> $ match_id          <int> 3609994, 3609994, 3609994, 3609994, 3609994, 3609994…
#> $ title             <chr> "TOP STATS", "TOP STATS", "TOP STATS", "TOP STATS", …
#> $ stats_title       <chr> "Ball possession", "Expected goals (xG)", "Total sho…
#> $ home_value        <chr> "48", "0.78", "6", "3", "1", "423 (84%)", "10", "2",…
#> $ away_value        <chr> "52", "1.66", "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_team_stats %>%
  dplyr::filter(match_id == dplyr::first(match_id)) %>%
  dplyr::count(title)
#> # A tibble: 7 × 2
#>   title                   n
#>   <chr>               <int>
#> 1 DEFENCE                 6
#> 2 DISCIPLINE              3
#> 3 DUELS                   5
#> 4 EXPECTED GOALS (xG)     7
#> 5 PASSES                  8
#> 6 SHOTS                   8
#> 7 TOP STATS               9

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: 41
#> $ 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> "Premier League Round 7", "Premier League Rou…
#> $ parent_league_id         <int> 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 4…
#> $ parent_league_season     <chr> "2022/2023", "2022/2023", "2022/2023", "2022/…
#> $ 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…
#> $ home_team_color          <chr> "#d3171e", "#d3171e", "#d3171e", "#d3171e", "…
#> $ away_team_id             <int> 8456, 8456, 8456, 8456, 8456, 8456, 8456, 845…
#> $ away_team                <chr> "Manchester City", "Manchester City", "Manche…
#> $ away_team_color          <chr> "#69A8D8", "#69A8D8", "#69A8D8", "#69A8D8", "…
#> $ 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.04540855, 0.04835442, 0.63251555, 0.0870914…
#> $ 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…
#> $ 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: 76
#> $ 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, 2, 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", "Midfielder", "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                          <chr> "0", " 0", " 0", " 0", " 0", " 0"…
#> $ 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_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_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.04", 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.04540855, 0.04966947, 0.29863000, 0.17881796, …
#> $ 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: 31
#> $ player_id                            <chr> "292462", "292462"
#> $ stats_fot_mob_rating                 <chr> "8.85", "7.99"
#> $ stats_minutes_played                 <chr> "90", "90"
#> $ stats_goals                          <chr> " 1", " 1"
#> $ 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_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_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.39", "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"