basicusage.Rmd
Library the package and a solver (glpk)
library(dfsOptimizer)
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)
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 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 %
#>
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')
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)