1
# Setup -------------------------------------------------------------------
# --- Load Packages
library(conflicted)
library(lpSolve)
library(tidyverse)
conflicts_prefer(dplyr::filter, dplyr::lag, dplyr::collapse, .quiet = TRUE)
# --- Assert Conflicts
if (some(conflict_scout(), \(x) length(x) > 1)) {
print(conflict_scout())
stop("Fix conflicts")
}
data = tribble(
~id, ~name, ~pts, ~team, ~opp, ~position, ~salary,
"117232-135260", "Logan O'Hoppe" , 10.3372093, "LAA", "MIA", "C1B" , 5500 ,
"117232-135260", "Logan O'Hoppe" , 10.3372093, "LAA", "MIA", "UTIL" , 5500 ,
"117232-206482", "Jacob Wilson" , 10.5083321, "ATH", "PHI", "SS" , 5500 ,
"117232-206482", "Jacob Wilson" , 10.5083321, "ATH", "PHI", "UTIL" , 5500 ,
"117232-2644" , "Jesus Sanchez" , 8.4600014 , "MIA", "LAA", "UTIL" , 3200 ,
"117232-82644" , "Jesus Sanchez" , 8.4600014 , "MIA", "LAA", "UTIL" , 3200 ,
"117232-119306", "Shea Langeliers" , 9.9044474 , "ATH", "PHI", "C1B" , 5500 ,
"117232-119306", "Shea Langeliers" , 9.9044474 , "ATH", "PHI", "UTIL" , 5500 ,
"117232-13342" , "Nick Castellanos", 8.9459998 , "PHI", "ATH", "OF" , 5100 ,
"117232-13342" , "Nick Castellanos", 8.9459998 , "PHI", "ATH", "P" , 5100 ,
"117232-61553" , "Yoan Moncada" , 10.8590963, "LAA", "MIA", "3B" , 3800 ,
"117232-61553" , "Yoan Moncada" , 10.8590963, "LAA", "MIA", "UTIL" , 3800 ,
"117232-189389", "Dane Myers" , 9.1172412 , "MIA", "LAA", "OF" , 3500 ,
"117232-189389", "Dane Myers" , 9.1172412 , "MIA", "LAA", "UTIL" , 3500 ,
"117232-23107" , "Jorge Soler" , 8.0347824 , "MIA", "LAA", "OF" , 3500 ,
"117232-23107" , "Jorge Soler" , 8.0347824 , "MIA", "LAA", "P" , 3500 ,
"117232-164506", "Connor Norby" , 8.6551724 , "MIA", "LAA", "2B" , 3400 ,
"117232-164506", "Connor Norby" , 8.6551724 , "MIA", "LAA", "UTIL" , 3400 ,
"117232-164506", "Connor Norby" , 8.6551724 , "MIA", "LAA", "SS" , 3400 ,
"117232-102393", "Xavier Edwards" , 8.8595239 , "MIA", "LAA", "UTIL" , 3400 ,
"117232-102393", "Xavier Edwards" , 8.8595239 , "MIA", "LAA", "SS" , 3400 ,
"117232-19358" , "Nolan Schanuel" , 8.6851066 , "LAA", "MIA", "C1B" , 3300 ,
"117232-19358" , "Nolan Schanuel" , 8.6851066 , "LAA", "MIA", "UTIL" , 3300 ,
"117232-119363", "JJ Bleday" , 8.4723407 , "PHI", "ATH", "OF" , 3300 ,
"117232-119363", "JJ Bleday" , 8.4723407 , "PHI", "ATH", "UTIL" , 3300 ,
"117232-222794", "Nick Kurtz" , 7.6653847 , "ATH", "PHI", "C1B" , 3200 ,
"117232-222794", "Nick Kurtz" , 7.6653847 , "ATH", "PHI", "UTIL" , 3200 ,
"117232-220835", "Liam Hicks" , 8.8103448 , "MIA", "LAA", "P" , 3200 ,
"117232-220835", "Liam Hicks" , 8.8103448 , "MIA", "LAA", "C1B" , 3200 ,
"117232-60636" , "Derek Hill" , 8.3349992 , "MIA", "LAA", "OF" , 3200 ,
"117232-60636" , "Derek Hill" , 8.3349992 , "MIA", "LAA", "UTIL" , 3200 ,
"117232-79195" , "J.T. Realmuto" , 9.3146937 , "PHI", "ATH", "C1B" , 3200 ,
"117232-79195" , "J.T. Realmuto" , 9.3146937 , "PHI", "ATH", "UTIL" , 3200 ,
"117232-79195" , "Miguel Andujar" , 8.6891892 , "ATH", "PHI", "C1B" , 3200 ,
"117232-79195" , "Miguel Andujar" , 8.6891892 , "ATH", "PHI", "3B" , 3200 ,
"117232-79195" , "Miguel Andujar" , 8.6891892 , "ATH", "PHI", "UTIL" , 3200 ,
)
# Functions ---------------------------------------------------------------
prepare_players_df = function(data) {
# Add binary columns of position
players =
data |>
relocate(pts, salary, .after = position) |>
arrange(position) |>
mutate(
position_name = paste0("pos_", position),
position_value = 1L
) |>
pivot_wider(names_from = position_name, values_from = position_value)
# Add binary columns for each team
players =
players |>
arrange(team, id) |>
mutate(team_name = paste0("team_", team)) |>
mutate(team_value = cur_group_id(), .by = team) |>
pivot_wider(names_from = team_name, values_from = team_value)
# add binary columns for each player
players =
players |>
arrange(id, position) |>
mutate(player_name = paste0("player_", str_replace(id, "-", "_"))) |>
mutate(player_value = cur_group_id(), .by = team) |>
pivot_wider(names_from = player_name, values_from = player_value) |>
mutate(across(where(is.integer), \(x) replace_na(x, 0L))) |>
rowwise() |>
mutate(across(where(is.integer), \(x) min(x, 1L))) |>
ungroup()
return(players)
}
prepare_constraints_df = function(players, max_total_salary = 6e4L) {
constraints_base = tribble(
~name , ~direction, ~rhs ,
"max_total_pts" , "<=" , 99999L , #init value
"max_total_salary" , "<=" , max_total_salary,
"max_pos_2B" , "=" , 1L ,
"max_pos_3B" , "=" , 1L ,
"max_pos_C1B" , "=" , 1L ,
"max_pos_OF" , "=" , 3L ,
"max_pos_P" , "=" , 1L ,
"max_pos_SS" , "=" , 1L ,
"max_pos_UTIL" , "=" , 1L ,
)
constraints_teams = tibble(
name = paste0("max_", colnames(players)[str_detect(colnames(players), "^team_")]),
direction = "<=",
rhs = 4L
)
constraints_players = tibble(
name = paste0("max_", colnames(players)[str_detect(colnames(players), "^player_")]),
direction = "<=",
rhs = 1L
)
bind_rows(constraints_base, constraints_teams, constraints_players)
}
prepare_lp_args = function(players, constraints) {
f.con = players |> select(pts:last_col()) |> as.matrix() |> t()
colnames(f.con) = players$name
list(
f.con = f.con,
f.dir = structure(constraints$direction, names = constraints$name),
f.rhs = structure(constraints$rhs, names = constraints$name)
)
}
solve_n_times = function(players, lp_args, n = 10, decrease_max_pts_amount = 1e-4) {
result = vector("list", n)
for (i in 1:n) {
result[[i]] = lpSolve::lp(
direction = "max",
objective.in = players$pts,
const.mat = lp_args$f.con,
const.dir = lp_args$f.dir,
const.rhs = lp_args$f.rhs,
all.bin = TRUE
)
lp_args$f.rhs[[1]] = sum(players[as.logical(result[[i]]$solution), ]$pts) - decrease_max_pts_amount
}
return(result)
}
# Solve -------------------------------------------------------------------
players = prepare_players_df(data)
glimpse(players)
constraints = prepare_constraints_df(players)
print(constraints, n = Inf)
lp_args = prepare_lp_args(players, constraints)
lp_args
results = solve_n_times(players, lp_args)
solutions = map(results, \(result) players[as.logical(result$solution), ])
# --- Results
# Verifies that no solution has less than 9 distinct id
map_int(solutions, \(solution) n_distinct(solution$id)) # Good !
# --- Details
solution_df =
solutions |>
imap(\(x, i) mutate(x, solution_i = i, .before = 1)) |>
bind_rows() |>
select(solution_i:salary) |>
arrange(solution_i, position)
print(solution_df, n = 25)
summarise(
solution_df,
total_pts = sum(pts),
total_salary = sum(salary),
.by = solution_i
)
lineupsMatrix = matrix(
c(seq_along(results), solution_df$id),
nrow = max(solution_df$solution_i)
)
lineupsMatrix
For immediate assistance, please email our customer support: [email protected]