Setup

Library the package and a solver (glpk)

library(dfsOptimizer)

Building Optimizer and getting lineups

Example of instantiating an Optimizer object, adding players from an internal dataset (nhl_players), generating 5 lineups.

# Instantiate
mod <- create_optimizer(site = 'DRAFTKINGS', sport = 'HOCKEY', contest_type = 'CLASSIC')
# Add players
mod <- add_players_from_df(mod, nhl_players)
# Generate lineups
lineups <- build_lineups(mod, num_lineups = 5, verbose = FALSE)

View the lineups

You can view the lineups simply by calling the object saved by build_lineups.

lineups
#> $lineup_1
#>           id          fullname team position salary   fpts roster_position
#>  1: 14086443  Nathan MacKinnon  COL        C   8300  19.00               C
#>  2: 14086445   Auston Matthews  TOR        C   7900  15.85               C
#>  3: 14086573 Brendan Gallagher  MON        W   6000  13.57               W
#>  4: 14086657   Kailer Yamamoto  EDM        W   2900   7.93               W
#>  5: 14086659        Martin Frk   LA        W   2900   9.32               W
#>  6: 14086813     Oscar Klefbom  EDM        D   6200  13.37               D
#>  7: 14086917         Sami Niku  WPG        D   2600   8.40               D
#>  8: 14087024    Cayden Primeau  MON        G   6500  19.20               G
#>  9: 14086453    Leon Draisaitl  EDM        C   6600  15.20            UTIL
#> 10:                                    TOTAL  49900 121.84                
#> 
#> $lineup_2
#>           id          fullname team position salary   fpts roster_position
#>  1: 14086443  Nathan MacKinnon  COL        C   8300  19.00               C
#>  2: 14086447    Connor McDavid  EDM        C   7500  15.55               C
#>  3: 14086573 Brendan Gallagher  MON        W   6000  13.57               W
#>  4: 14086641      Matt Calvert  COL        W   3300   8.01               W
#>  5: 14086659        Martin Frk   LA        W   2900   9.32               W
#>  6: 14086813     Oscar Klefbom  EDM        D   6200  13.37               D
#>  7: 14086917         Sami Niku  WPG        D   2600   8.40               D
#>  8: 14087024    Cayden Primeau  MON        G   6500  19.20               G
#>  9: 14086453    Leon Draisaitl  EDM        C   6600  15.20            UTIL
#> 10:                                    TOTAL  49900 121.62                
#> 
#> $lineup_3
#>           id          fullname team position salary   fpts roster_position
#>  1: 14086443  Nathan MacKinnon  COL        C   8300  19.00               C
#>  2: 14086447    Connor McDavid  EDM        C   7500  15.55               C
#>  3: 14086569    Mikko Rantanen  COL        W   6400  13.25               W
#>  4: 14086573 Brendan Gallagher  MON        W   6000  13.57               W
#>  5: 14086659        Martin Frk   LA        W   2900   9.32               W
#>  6: 14086867       Jake Muzzin  TOR        D   3200   8.07               D
#>  7: 14086917         Sami Niku  WPG        D   2600   8.40               D
#>  8: 14087024    Cayden Primeau  MON        G   6500  19.20               G
#>  9: 14086453    Leon Draisaitl  EDM        C   6600  15.20            UTIL
#> 10:                                    TOTAL  50000 121.56                
#> 
#> $lineup_4
#>           id          fullname team position salary   fpts roster_position
#>  1: 14086443  Nathan MacKinnon  COL        C   8300  19.00               C
#>  2: 14086447    Connor McDavid  EDM        C   7500  15.55               C
#>  3: 14086573 Brendan Gallagher  MON        W   6000  13.57               W
#>  4: 14086657   Kailer Yamamoto  EDM        W   2900   7.93               W
#>  5: 14086659        Martin Frk   LA        W   2900   9.32               W
#>  6: 14086813     Oscar Klefbom  EDM        D   6200  13.37               D
#>  7: 14086917         Sami Niku  WPG        D   2600   8.40               D
#>  8: 14087024    Cayden Primeau  MON        G   6500  19.20               G
#>  9: 14086453    Leon Draisaitl  EDM        C   6600  15.20            UTIL
#> 10:                                    TOTAL  49500 121.54                
#> 
#> $lineup_5
#>           id          fullname team position salary   fpts roster_position
#>  1: 14086443  Nathan MacKinnon  COL        C   8300  19.00               C
#>  2: 14086445   Auston Matthews  TOR        C   7900  15.85               C
#>  3: 14086573 Brendan Gallagher  MON        W   6000  13.57               W
#>  4: 14086653    Ilya Kovalchuk  MON        W   3000   7.39               W
#>  5: 14086659        Martin Frk   LA        W   2900   9.32               W
#>  6: 14086813     Oscar Klefbom  EDM        D   6200  13.37               D
#>  7: 14086917         Sami Niku  WPG        D   2600   8.40               D
#>  8: 14087024    Cayden Primeau  MON        G   6500  19.20               G
#>  9: 14086453    Leon Draisaitl  EDM        C   6600  15.20            UTIL
#> 10:                                    TOTAL  50000 121.30

You can access the individual lineups with standard ‘[’ indexing

lineups[1]
#> $lineup_1
#>          id          fullname team position salary  fpts roster_position
#> 1: 14086443  Nathan MacKinnon  COL        C   8300 19.00               C
#> 2: 14086445   Auston Matthews  TOR        C   7900 15.85               C
#> 3: 14086573 Brendan Gallagher  MON        W   6000 13.57               W
#> 4: 14086657   Kailer Yamamoto  EDM        W   2900  7.93               W
#> 5: 14086659        Martin Frk   LA        W   2900  9.32               W
#> 6: 14086813     Oscar Klefbom  EDM        D   6200 13.37               D
#> 7: 14086917         Sami Niku  WPG        D   2600  8.40               D
#> 8: 14087024    Cayden Primeau  MON        G   6500 19.20               G
#> 9: 14086453    Leon Draisaitl  EDM        C   6600 15.20            UTIL
lineups[[2]]
#>          id          fullname team position salary  fpts roster_position
#> 1: 14086443  Nathan MacKinnon  COL        C   8300 19.00               C
#> 2: 14086447    Connor McDavid  EDM        C   7500 15.55               C
#> 3: 14086573 Brendan Gallagher  MON        W   6000 13.57               W
#> 4: 14086641      Matt Calvert  COL        W   3300  8.01               W
#> 5: 14086659        Martin Frk   LA        W   2900  9.32               W
#> 6: 14086813     Oscar Klefbom  EDM        D   6200 13.37               D
#> 7: 14086917         Sami Niku  WPG        D   2600  8.40               D
#> 8: 14087024    Cayden Primeau  MON        G   6500 19.20               G
#> 9: 14086453    Leon Draisaitl  EDM        C   6600 15.20            UTIL

Finally, you may to extract the lineups to a list, To do so, you can simply run as.list(lineups)

# Here we're getting the total fpts from every lineup using sapply
colSums(sapply(as.list(lineups), '[[', 'fpts'))
#> lineup_1 lineup_2 lineup_3 lineup_4 lineup_5 
#>   121.84   121.62   121.56   121.54   121.30

Lineup Summary

Lineup summary provides information about how often individual players were selected for lineups (count and percentage), which teams were selected most often, and a measure of lineup variance (higher Jaccard Distance means more variance across lineups)

# Summary stats
summary(lineups)
#> Number of Expected Lineups: 5
#> Number of Lineups Found: 5
#> 
#> Player Exposures:
#>                Player Lineups Percent
#>  1:  Nathan MacKinnon       5     100
#>  2: Brendan Gallagher       5     100
#>  3:        Martin Frk       5     100
#>  4:         Sami Niku       5     100
#>  5:    Cayden Primeau       5     100
#>  6:    Leon Draisaitl       5     100
#>  7:     Oscar Klefbom       4      80
#>  8:    Connor McDavid       3      60
#>  9:   Auston Matthews       2      40
#> 10:   Kailer Yamamoto       2      40
#> 11:      Matt Calvert       1      20
#> 12:    Mikko Rantanen       1      20
#> 13:       Jake Muzzin       1      20
#> 14:    Ilya Kovalchuk       1      20
#> 
#> Players Per Team:
#> COL EDM  LA MON TOR WPG 
#>   7  14   5  11   3   5 
#> 
#> Lineup Variance (mean Jaccard Distance): 34.18 %
#> 

Export the lineups

The format of the file output by export_lineups is determined by the Site, Sport, and Contest Type.

export_lineups(lineups, file = 'lineup_export.csv')

Including Previously Generated Lineups

There may be cases where you want to include previously generated lineups. For example, let’s say you want certain players’ variance to be dependent – like if a hockey center’s projection decreases randomly, his wingers’ projections should decrease a similar amount. By including previously generated lineups, you can alter the projections before each iteration of the model, but still be sure you’re not generating the same lineup twice.

# Make an empty object called Lineups
lineups <- list()

# Pull the initial player data, to build off of
player_data <- get_player_data(mod)

# Iterate for as many lineups as you'd like, let's say 20
for (nl in 1:20) {
  # Apply your formula for dependent-randomness
  # Get a single percentage adjustment (say, 10%)
  random_adjustment <- runif(1, -.1, .1)
  
  # We'll apply it to COL Cs and Ws from our nhl_players set, as an illustration
  # Note the data.table notation here -- we're saying for ALL Cs and Ws on Colorado,
  # Let's apply the _same_ random adjustment.  You could easily loop this across teams,
  # and lines if you had line-information
  player_data[team == 'COL' && position %in% c('C','W'), 
              fpts := fpts * (1 + random_adjustment)]
  
  # Now add that updated data back into the optimizer
  mod <- add_players_from_df(mod, player_data)

  # And Build the next lineup.Note the inclusion of the `existing_lineups` parameter
  lineups <- build_lineups(mod, num_lineups = 1, existing_lineups = lineups, verbose = FALSE)
}

Or, as another example, let’s say you need 100 lineups - after the first 50, you notice the model is overwhelmingly selecting forwards from two teams, COL and EDM. You want to block those forwards in the next 50 lineups, but allow for any number of D or G from those teams.

# Build 50 lineups
lineups <- build_lineups(mod, num_lineups = 50, verbose = FALSE) 

# Block any COL/EDM forwards
player_data <- get_player_data(mod)
ids_to_block <- player_data[team %in% c('COL','EDM') & position %in% c('C','W'), ]$id
mod <- block_players_by_id(mod, ids_to_block)

# Build 50 more lineups, which do not match any of the previous
lineups_new <- build_lineups(mod, num_lineups = 50, existing_lineups = lineups, verbose = FALSE)