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.

Sleeper: Basics

Tan Ho

2023-02-11

In this vignette, I’ll walk through how to get started with a basic dynasty value analysis on Sleeper.

We’ll start by loading the packages:

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

In Sleeper, unlike in other platforms, it’s very unlikely that you’ll remember the league ID - both because most people use the mobile app, and because it happens to be an 18 digit number! It’s a little more natural to start analyses from the username, so let’s start there!

solarpool_leagues <- sleeper_userleagues("solarpool",2020)
#> No encoding supplied: defaulting to UTF-8.
#> No encoding supplied: defaulting to UTF-8.
#> No encoding supplied: defaulting to UTF-8.
#> No encoding supplied: defaulting to UTF-8.
#> No encoding supplied: defaulting to UTF-8.

head(solarpool_leagues)
#> # A tibble: 3 × 4
#>   league_name                         league_id          franchise_name franch…¹
#>   <chr>                               <chr>              <chr>          <chr>   
#> 1 The JanMichaelLarkin Dynasty League 522458773317046272 solarpool      2028920…
#> 2 DLP Dynasty League                  521379020332068864 DLP::thoriyan  2028920…
#> 3 z_dynastyprocess-test               633501761776197632 solarpool      2028920…
#> # … with abbreviated variable name ¹​franchise_id

Let’s pull the JML league ID from here for analysis, and set up a Sleeper connection object.

jml_id <- solarpool_leagues %>% 
  filter(league_name == "The JanMichaelLarkin Dynasty League") %>% 
  pull(league_id)

jml_id # For quick analyses, I'm not above copy-pasting the league ID instead!
#> [1] "522458773317046272"

jml <- sleeper_connect(season = 2020, league_id = jml_id)

jml
#> <Sleeper connection 2020_522458773317046272>
#> List of 5
#>  $ platform : chr "Sleeper"
#>  $ season   : num 2020
#>  $ user_name: NULL
#>  $ league_id: chr "522458773317046272"
#>  $ user_id  : NULL
#>  - attr(*, "class")= chr "sleeper_conn"

I’ve done this with the sleeper_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.

jml_summary <- ff_league(jml)
#> No encoding supplied: defaulting to UTF-8.
#> No encoding supplied: defaulting to UTF-8.
#> No encoding supplied: defaulting to UTF-8.

str(jml_summary)
#> tibble [1 × 16] (S3: tbl_df/tbl/data.frame)
#>  $ league_id      : chr "522458773317046272"
#>  $ league_name    : chr "The JanMichaelLarkin Dynasty League"
#>  $ season         : int 2020
#>  $ league_type    : chr "dynasty"
#>  $ franchise_count: num 12
#>  $ qb_type        : chr "1QB"
#>  $ idp            : logi FALSE
#>  $ scoring_flags  : chr "0.5_ppr"
#>  $ best_ball      : logi FALSE
#>  $ salary_cap     : logi FALSE
#>  $ player_copies  : num 1
#>  $ years_active   : chr "2019-2020"
#>  $ qb_count       : chr "1"
#>  $ roster_size    : int 25
#>  $ league_depth   : num 300
#>  $ prev_league_ids: chr "386236959468675072"

Okay, so it’s the JanMichaelLarkin Dynasty League, it’s a 1QB league with 12 teams, half ppr scoring, and rosters about 300 players.

Let’s grab the rosters now.

jml_rosters <- ff_rosters(jml)
#> No encoding supplied: defaulting to UTF-8.
#> No encoding supplied: defaulting to UTF-8.
#> No encoding supplied: defaulting to UTF-8.
#> No encoding supplied: defaulting to UTF-8.

head(jml_rosters)
#> # A tibble: 6 × 7
#>   franchise_id franchise_name player_id player_name     pos   team    age
#>   <chr>        <chr>          <chr>     <chr>           <chr> <chr> <dbl>
#> 1 1            Fake News      1110      T.Y. Hilton     WR    IND    33.2
#> 2 1            Fake News      1339      Zach Ertz       TE    PHI    32.3
#> 3 1            Fake News      1426      DeAndre Hopkins WR    ARI    30.7
#> 4 1            Fake News      1825      Jarvis Landry   WR    CLE    30.2
#> 5 1            Fake News      2025      Albert Wilson   WR    MIA    30.6
#> 6 1            Fake News      2197      Brandin Cooks   WR    HOU    29.4

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(sleeper_id,fantasypros_id)

player_values <- player_values %>% 
  left_join(player_ids, by = c("fp_id" = "fantasypros_id")) %>% 
  select(sleeper_id,ecr_1qb,ecr_pos,value_1qb)

# Drilling down to just 1QB values and IDs, we'll be joining it onto rosters and don't need the extra stuff

jml_values <- jml_rosters %>% 
  left_join(player_values, by = c("player_id"="sleeper_id")) %>% 
  arrange(franchise_id,desc(value_1qb))

head(jml_values)
#> # A tibble: 6 × 10
#>   franchise_id franc…¹ playe…² playe…³ pos   team    age ecr_1qb ecr_pos value…⁴
#>   <chr>        <chr>   <chr>   <chr>   <chr> <chr> <dbl>   <dbl>   <dbl>   <int>
#> 1 1            Fake N… 4866    Saquon… RB    NYG    26       3.4     2.8    9694
#> 2 1            Fake N… 4199    Aaron … RB    GB     28.2    21      10.3    6410
#> 3 1            Fake N… 1426    DeAndr… WR    ARI    30.7    21.1     7.2    6395
#> 4 1            Fake N… 4037    Chris … WR    TB     27      33.7    13.8    4756
#> 5 1            Fake N… 4098    Kareem… RB    CLE    27.5    63.7    23.7    2350
#> 6 1            Fake N… 5022    Dallas… TE    PHI    27.6    83.2     7.8    1486
#> # … with abbreviated variable names ¹​franchise_name, ²​player_id, ³​player_name,
#> #   ⁴​value_1qb

Let’s do some team summaries now!

value_summary <- jml_values %>% 
  group_by(franchise_id,franchise_name,pos) %>% 
  summarise(total_value = sum(value_1qb,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))

value_summary
#> # A tibble: 12 × 8
#>    franchise_id franchise_name    team_value    QB    RB    TE    WR    FB
#>    <chr>        <chr>                  <int> <int> <int> <int> <int> <int>
#>  1 3            solarpool              45406  7664 23920   529 13293    NA
#>  2 4            The FANTom Menace      41754  3051  7594  1820 29289    NA
#>  3 11           Permian Panthers       40081  3889  9902  6997 19293    NA
#>  4 1            Fake News              37716  1505 19221  2730 14260    NA
#>  5 8            Hocka Flocka           37314  1234 20459  2511 13110    NA
#>  6 12           jaydk                  33981  1696 17692  2936 11657    NA
#>  7 5            Barbarians             32614  3770 19492  4629  4723    NA
#>  8 6            sox05syd               30780  4329  5614  8136 12701    NA
#>  9 9            ZPMiller97             24697  2941 12782   998  7976    NA
#> 10 2            KingGabe               19931    41  6327    15 13548    NA
#> 11 7            Flipadelphia05         18140  1951  4799   789 10601    NA
#> 12 10           JMLarkin               14197   336    67   884 12908     2

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: 12 × 8
#>    franchise_id franchise_name    team_value    QB    RB    TE    WR    FB
#>    <chr>        <chr>                  <dbl> <dbl> <dbl> <dbl> <dbl> <int>
#>  1 3            solarpool              0.121 0.236 0.162 0.016 0.081    NA
#>  2 4            The FANTom Menace      0.111 0.094 0.051 0.055 0.179    NA
#>  3 11           Permian Panthers       0.106 0.12  0.067 0.212 0.118    NA
#>  4 1            Fake News              0.1   0.046 0.13  0.083 0.087    NA
#>  5 8            Hocka Flocka           0.099 0.038 0.138 0.076 0.08     NA
#>  6 12           jaydk                  0.09  0.052 0.12  0.089 0.071    NA
#>  7 5            Barbarians             0.087 0.116 0.132 0.14  0.029    NA
#>  8 6            sox05syd               0.082 0.134 0.038 0.247 0.078    NA
#>  9 9            ZPMiller97             0.066 0.091 0.086 0.03  0.049    NA
#> 10 2            KingGabe               0.053 0.001 0.043 0     0.083    NA
#> 11 7            Flipadelphia05         0.048 0.06  0.032 0.024 0.065    NA
#> 12 10           JMLarkin               0.038 0.01  0     0.027 0.079     2

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 <- jml_values %>% 
  group_by(franchise_id,pos) %>% 
  mutate(position_value = sum(value_1qb,na.rm=TRUE)) %>% 
  ungroup() %>% 
  mutate(weighted_age = age*value_1qb/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: 12 × 12
#> # Groups:   franchise_id, franchise_name [12]
#>    franchis…¹ franc…² age_QB age_RB age_TE age_WR age_FB count…³ count…⁴ count…⁵
#>    <chr>      <chr>    <dbl>  <dbl>  <dbl>  <dbl>  <dbl>   <int>   <int>   <int>
#>  1 1          Fake N…   30.4   27.1   27.2   29.1   NA         3       8       7
#>  2 10         JMLark…   30.1   28.9   27.6   26.8   31.8       3      10       3
#>  3 11         Permia…   25.7   24.7   33.2   27.1   NA         4       9       7
#>  4 12         jaydk     33.8   27.3   27.6   29.5   NA         4       7       5
#>  5 2          KingGa…   30.5   24.1   28.8   23.9   NA         5       4       3
#>  6 3          solarp…   27.1   27.2   28.1   29.3   NA         5      10       4
#>  7 4          The FA…   30.4   25     25.4   28.5   NA         5      14       2
#>  8 5          Barbar…   26.7   26.1   30.4   27.9   NA         3      10       6
#>  9 6          sox05s…   25.5   25.7   28.8   26.9   NA         3       5       9
#> 10 7          Flipad…   34.6   27.3   28.9   28.4   NA         2      10       4
#> 11 8          Hocka …   33     25.8   25.5   25.1   NA         3       6       4
#> 12 9          ZPMill…   26     25.5   28.3   26.7   NA         3       9       4
#> # … with 2 more variables: count_WR <int>, count_FB <int>, and abbreviated
#> #   variable names ¹​franchise_id, ²​franchise_name, ³​count_QB, ⁴​count_RB,
#> #   ⁵​count_TE
#> # ℹ Use `colnames()` to see all variable names

Next steps

In this vignette, I’ve used ~three functions: ff_connect, ff_league, and ff_rosters. 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.