The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

ESPN: Basics

Tan Ho

2023-02-11

In this vignette, I’ll walk through how to get started with a basic dynasty value analysis on ESPN, pulling in roster data.

We’ll start by loading the packages:

  library(ffscrapr)
  library(dplyr)
  library(tidyr)

In ESPN, you can find the league ID by looking in the URL - it’s the number immediately after ?leagueId in this example URL: https://fantasy.espn.com/football/team?leagueId=899513&seasonId=2020

Let’s set up a connection to this league:

sucioboys <- espn_connect(season = 2020, league_id = 899513)

sucioboys
#> <ESPN connection 2020_899513>
#> List of 4
#>  $ platform : chr "ESPN"
#>  $ season   : chr "2020"
#>  $ league_id: chr "899513"
#>  $ cookies  : NULL
#>  - attr(*, "class")= chr "espn_conn"

I’ve done this with the espn_connect() function, although you can also do this from the ff_connect() call - they are equivalent. Most if not all of the remaining functions after this point are prefixed with “ff_”.

Cool! Let’s have a quick look at what this league is like.

sucioboys_summary <- ff_league(sucioboys)
#> Using request.R from "ffscrapr"

str(sucioboys_summary)
#> tibble [1 × 16] (S3: tbl_df/tbl/data.frame)
#>  $ league_id      : chr "899513"
#>  $ league_name    : chr "Sucio Boys"
#>  $ season         : int 2020
#>  $ league_type    : chr "keeper"
#>  $ franchise_count: int 10
#>  $ qb_type        : chr "2QB/SF"
#>  $ idp            : logi FALSE
#>  $ scoring_flags  : chr "0.5_ppr"
#>  $ best_ball      : logi FALSE
#>  $ salary_cap     : logi FALSE
#>  $ player_copies  : num 1
#>  $ years_active   : chr "2018-2020"
#>  $ qb_count       : chr "1-2"
#>  $ roster_size    : int 24
#>  $ league_depth   : num 240
#>  $ keeper_count   : int 22

Okay, so it’s the Sucio Boys league, it’s a 2QB league with 12 teams, half ppr scoring, and rosters about 240 players.

Let’s grab the rosters now.

sucioboys_rosters <- ff_rosters(sucioboys)

head(sucioboys_rosters) # quick snapshot of rosters
#> # A tibble: 6 × 10
#>   franchise_id franchise_name playe…¹ playe…² team  pos   eligi…³ status acqui…⁴
#>          <int> <chr>            <int> <chr>   <chr> <chr> <list>  <chr>  <chr>  
#> 1            1 The Early GGod 4036348 Michae… DAL   WR    <chr>   NORMAL DRAFT  
#> 2            1 The Early GGod 4036131 Noah F… DEN   TE    <chr>   NORMAL DRAFT  
#> 3            1 The Early GGod  -16003 Bears … CHI   DST   <chr>   NORMAL DRAFT  
#> 4            1 The Early GGod   15920 Latavi… NOS   RB    <chr>   NORMAL DRAFT  
#> 5            1 The Early GGod 3055899 Harris… KCC   K     <chr>   NORMAL DRAFT  
#> 6            1 The Early GGod 4241372 Marqui… BAL   WR    <chr>   NORMAL DRAFT  
#> # … with 1 more variable: acquisition_date <dttm>, and abbreviated variable
#> #   names ¹​player_id, ²​player_name, ³​eligible_pos, ⁴​acquisition_type
#> # ℹ Use `colnames()` to see all variable names

Values

Cool! Let’s pull in some additional context by adding DynastyProcess player values.

player_values <- dp_values("values-players.csv")

# The values are stored by fantasypros ID since that's where the data comes from. 
# To join it to our rosters, we'll need playerID mappings.

player_ids <- dp_playerids() %>% 
  select(espn_id,fantasypros_id) %>% 
  filter(!is.na(espn_id),!is.na(fantasypros_id))

# We'll be joining it onto rosters, so we can trim down the values dataframe
# to just IDs, age, and values

player_values <- player_values %>% 
  left_join(player_ids, by = c("fp_id" = "fantasypros_id")) %>% 
  select(espn_id,age,ecr_2qb,ecr_pos,value_2qb)

# we can join the roster's player_ids on the values' espn_id, with a bit of a type conversion first
sucioboys_values <- sucioboys_rosters %>% 
  mutate(player_id = as.character(player_id)) %>% 
  left_join(player_values, by = c("player_id"="espn_id")) %>% 
  arrange(franchise_id,desc(value_2qb))

head(sucioboys_values)
#> # A tibble: 6 × 14
#>   franchise_id franchise_name playe…¹ playe…² team  pos   eligi…³ status acqui…⁴
#>          <int> <chr>          <chr>   <chr>   <chr> <chr> <list>  <chr>  <chr>  
#> 1            1 The Early GGod 4242335 Jonath… IND   RB    <chr>   NORMAL DRAFT  
#> 2            1 The Early GGod 4241985 J.K. D… BAL   RB    <chr>   NORMAL DRAFT  
#> 3            1 The Early GGod 2976316 Michae… NOS   WR    <chr>   NORMAL DRAFT  
#> 4            1 The Early GGod 4040715 Jalen … PHI   QB    <chr>   NORMAL ADD    
#> 5            1 The Early GGod 4239993 Tee Hi… CIN   WR    <chr>   NORMAL ADD    
#> 6            1 The Early GGod 4241479 Tua Ta… MIA   QB    <chr>   NORMAL DRAFT  
#> # … with 5 more variables: acquisition_date <dttm>, age <dbl>, ecr_2qb <dbl>,
#> #   ecr_pos <dbl>, value_2qb <int>, and abbreviated variable names ¹​player_id,
#> #   ²​player_name, ³​eligible_pos, ⁴​acquisition_type
#> # ℹ Use `colnames()` to see all variable names

Let’s do some team summaries now!

value_summary <- sucioboys_values %>% 
  group_by(franchise_id,franchise_name,pos) %>% 
  summarise(total_value = sum(value_2qb,na.rm = TRUE)) %>%
  ungroup() %>% 
  group_by(franchise_id,franchise_name) %>% 
  mutate(team_value = sum(total_value)) %>% 
  ungroup() %>% 
  pivot_wider(names_from = pos, values_from = total_value) %>% 
  arrange(desc(team_value)) %>% 
  select(franchise_id,franchise_name,team_value,QB,RB,WR,TE)

value_summary
#> # A tibble: 10 × 7
#>    franchise_id franchise_name               team_value    QB    RB    WR    TE
#>           <int> <chr>                             <int> <int> <int> <int> <int>
#>  1            5 "The Juggernaut"                  49693  8628 18592 16747  5726
#>  2            6 "OBJ's Personal Porta Potty"      46447 20424 22738   997  2288
#>  3            7 "Tony El Tigre"                   44547 15508 17020  5921  6098
#>  4            2 "Coom  Dumpster"                  41599 13314  2329 24668  1288
#>  5            4 "I'm Also Sad "                   37216  1467 15597 16565  3587
#>  6            3 "PAKI STANS"                      32697  6048 11980 12829  1840
#>  7            1 "The Early GGod"                  32247  6642 13916  9406  2283
#>  8            9 "RAFI CUNADO"                     31958  6878 10726 13365   989
#>  9            8 "Big Coomers"                     21493  7416  1453 12408   216
#> 10           10 "Austin 🐐Drew Lock🐐"            20832  7153   276 13324    79

So with that, we’ve got a team summary of values! I like applying some context, so let’s turn these into percentages - this helps normalise it to your league environment.

value_summary_pct <- value_summary %>% 
  mutate_at(c("team_value","QB","RB","WR","TE"),~.x/sum(.x)) %>% 
  mutate_at(c("team_value","QB","RB","WR","TE"),round, 3)

value_summary_pct
#> # A tibble: 10 × 7
#>    franchise_id franchise_name               team_value    QB    RB    WR    TE
#>           <int> <chr>                             <dbl> <dbl> <dbl> <dbl> <dbl>
#>  1            5 "The Juggernaut"                  0.139 0.092 0.162 0.133 0.235
#>  2            6 "OBJ's Personal Porta Potty"      0.129 0.218 0.198 0.008 0.094
#>  3            7 "Tony El Tigre"                   0.124 0.166 0.148 0.047 0.25 
#>  4            2 "Coom  Dumpster"                  0.116 0.142 0.02  0.195 0.053
#>  5            4 "I'm Also Sad "                   0.104 0.016 0.136 0.131 0.147
#>  6            3 "PAKI STANS"                      0.091 0.065 0.105 0.102 0.075
#>  7            1 "The Early GGod"                  0.09  0.071 0.121 0.075 0.094
#>  8            9 "RAFI CUNADO"                     0.089 0.074 0.094 0.106 0.041
#>  9            8 "Big Coomers"                     0.06  0.079 0.013 0.098 0.009
#> 10           10 "Austin 🐐Drew Lock🐐"            0.058 0.077 0.002 0.106 0.003

Armed with a value summary like this, we can see team strengths and weaknesses pretty quickly, and figure out who might be interested in your positional surpluses and who might have a surplus at a position you want to look at.

Age

Another question you might ask: what is the average age of any given team?

I like looking at average age by position, but weighted by dynasty value. This helps give a better idea of age for each team - including who might be looking to offload an older veteran!

age_summary <- sucioboys_values %>% 
  filter(pos %in% c("QB","RB","WR","TE")) %>% 
  group_by(franchise_id,pos) %>% 
  mutate(position_value = sum(value_2qb,na.rm=TRUE)) %>% 
  ungroup() %>% 
  mutate(weighted_age = age*value_2qb/position_value,
         weighted_age = round(weighted_age, 1)) %>% 
  group_by(franchise_id,franchise_name,pos) %>% 
  summarise(count = n(),
            age = sum(weighted_age,na.rm = TRUE)) %>% 
  pivot_wider(names_from = pos,
              values_from = c(age,count))

age_summary
#> # A tibble: 10 × 10
#> # Groups:   franchise_id, franchise_name [10]
#>    franchi…¹ franc…² age_QB age_RB age_TE age_WR count…³ count…⁴ count…⁵ count…⁶
#>        <int> <chr>    <dbl>  <dbl>  <dbl>  <dbl>   <int>   <int>   <int>   <int>
#>  1         1 "The E…   23.5   22.4   24.7   26         4       6       3       7
#>  2         2 "Coom …   28.7   25.9   26.8   25.6       4       7       3       6
#>  3         3 "PAKI …   29.1   25.3   23.9   25.9       3       6       2       9
#>  4         4 "I'm A…   35.4   24.8   28.7   27.4       2       5       2       8
#>  5         5 "The J…   25     24.6   31.6   25.3       3       8       2       7
#>  6         6 "OBJ's…   24.8   24.7   25.3   23.4       3       6       2       7
#>  7         7 "Tony …   24.8   25.3   27.8   26.6       3       5       3       6
#>  8         8 "Big C…   23.5   26     27.2   27.1       3       7       2       6
#>  9         9 "RAFI …   35.1   25.9   26.4   24.4       3       5       3       8
#> 10        10 "Austi…   32.2   24.4   32.2   25.5       3       5       3       5
#> # … with abbreviated variable names ¹​franchise_id, ²​franchise_name, ³​count_QB,
#> #   ⁴​count_RB, ⁵​count_TE, ⁶​count_WR

Next steps

In this vignette, I’ve used only a few functions: ff_connect, ff_league, ff_rosters, and dp_values. Now that you’ve gotten this far, why not check out some of the other possibilities?

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.