在梦幻足球阵容优化器中添加 Flex 位置

问题描述 投票:0回答:3

我已经编写了一些 R 代码来生成最佳梦幻足球阵容(最大化预计得分),该阵容受限于用户输入的花名册大小和基于名为“球员”的数据框的预算草案,该数据框由球员、位置、梦幻点数和草稿价值。

想法是在起草之前使用这个工具(心中有理想的阵容),然后在起草时实时更新它,因为最佳阵容很可能是一个流动的东西。

我对如何包括“灵活”位置感到困惑,这是一个可以是 RB、WR 或 TE 的花名册位置。我目前隐式地将它包含在 num_players 约束中——如果你的联赛需要 1 qb、2 rb、2 wr、1 te 和 1 flex 位置,你的 num_players 输入应该是 7。目前,此代码仅在 num_players = qb 时有效+rb+wr+te 没有弯曲(本例中为 6)。当我使 num_players 限制比这个总数多 1 以试图包含一个弹性位置时,它会中断。

我如何更新此代码以删除隐式 num_players 约束并包含 flexes 约束?

数据框是:

players <- structure(list(Player = c("Josh Allen", "Patrick Mahomes", "Justin Herbert", 
"Lamar Jackson", "Kyler Murray", "Jalen Hurts", "Tom Brady", 
"Dak Prescott", "Joe Burrow", "Russell Wilson", "Aaron Rodgers", 
"Trey Lance", "Matthew Stafford", "Kirk Cousins", "Derek Carr", 
"Tua Tagovailoa", "Justin Fields", "Trevor Lawrence", "Ryan Tannehill", 
"Daniel Jones", "Matt Ryan", "Jameis Winston", "Carson Wentz", 
"Mac Jones", "Jared Goff", "Zach Wilson", "Davis Mills", "Baker Mayfield", 
"Marcus Mariota", "Deshaun Watson", "Mitchell Trubisky", "Geno Smith", 
"Drew Lock", "Kenny Pickett", "Jacoby Brissett", "Desmond Ridder", 
"Travis Kelce", "Mark Andrews", "Kyle Pitts", "Darren Waller", 
"George Kittle", "Dalton Schultz", "T.J. Hockenson", "Dallas Goedert", 
"Zach Ertz", "Dawson Knox", "Hunter Henry", "Mike Gesicki", "Pat Freiermuth", 
"Cole Kmet", "Irv Smith Jr.", "Noah Fant", "Tyler Higbee", "David Njoku", 
"Albert Okwuegbunam", "Gerald Everett", "Robert Tonyan", "Jonathan Taylor", 
"Christian McCaffrey", "Derrick Henry", "Austin Ekeler", "Dalvin Cook", 
"Joe Mixon", "Najee Harris", "Alvin Kamara", "D'Andre Swift", 
"Leonard Fournette", "Saquon Barkley", "Aaron Jones", "Nick Chubb", 
"James Conner", "Javonte Williams", "Ezekiel Elliott", "David Montgomery", 
"Cam Akers", "Travis Etienne Jr.", "Breece Hall", "J.K. Dobbins", 
"Josh Jacobs", "Antonio Gibson", "Elijah Mitchell", "AJ Dillon", 
"Cordarrelle Patterson", "Damien Harris", "Miles Sanders", "Clyde Edwards-Helaire", 
"Tony Pollard", "Devin Singletary", "Kareem Hunt", "Chase Edmonds", 
"Rashaad Penny", "Rhamondre Stevenson", "Kenneth Walker III", 
"Melvin Gordon III", "Darrell Henderson Jr.", "James Robinson", 
"James Cook", "Dameon Pierce", "Michael Carter", "Jamaal Williams", 
"Nyheim Hines", "J.D. McKissic", "Kenneth Gainwell", "Alexander Mattison", 
"Isaiah Spiller", "Raheem Mostert", "Mark Ingram II", "Marlon Mack", 
"Brian Robinson", "Gus Edwards", "Rex Burkhead", "Rachaad White", 
"Khalil Herbert", "Damien Williams", "Tyler Allgeier", "D'Onta Foreman", 
"Jerick McKinnon", "Cooper Kupp", "Justin Jefferson", "Ja'Marr Chase", 
"Davante Adams", "Stefon Diggs", "Deebo Samuel", "CeeDee Lamb", 
"Mike Evans", "Tyreek Hill", "Tee Higgins", "Keenan Allen", "DJ Moore", 
"A.J. Brown", "Michael Pittman Jr.", "Mike Williams", "Brandin Cooks", 
"Jaylen Waddle", "Diontae Johnson", "Terry McLaurin", "DK Metcalf", 
"Courtland Sutton", "Amon-Ra St. Brown", "Darnell Mooney", "Allen Robinson II", 
"Marquise Brown", "Amari Cooper", "Gabriel Davis", "Chris Godwin", 
"Michael Thomas", "Jerry Jeudy", "Adam Thielen", "JuJu Smith-Schuster", 
"Hunter Renfrow", "Rashod Bateman", "Elijah Moore", "Tyler Lockett", 
"Christian Kirk", "Robert Woods", "DeVonta Smith", "Drake London", 
"Allen Lazard", "Brandon Aiyuk", "Chase Claypool", "Kadarius Toney", 
"Tyler Boyd", "Garrett Wilson", "DeVante Parker", "Chris Olave", 
"Kenny Golladay", "Jakobi Meyers", "Russell Gage", "Marquez Valdes-Scantling", 
"DeAndre Hopkins", "Marvin Jones Jr.", "Treylon Burks", "Michael Gallup", 
"Robbie Anderson", "DJ Chark", "Jahan Dotson", "Mecole Hardman"
), Position = c("QB", "QB", "QB", "QB", "QB", "QB", "QB", "QB", 
"QB", "QB", "QB", "QB", "QB", "QB", "QB", "QB", "QB", "QB", "QB", 
"QB", "QB", "QB", "QB", "QB", "QB", "QB", "QB", "QB", "QB", "QB", 
"QB", "QB", "QB", "QB", "QB", "QB", "TE", "TE", "TE", "TE", "TE", 
"TE", "TE", "TE", "TE", "TE", "TE", "TE", "TE", "TE", "TE", "TE", 
"TE", "TE", "TE", "TE", "TE", "RB", "RB", "RB", "RB", "RB", "RB", 
"RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", 
"RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", 
"RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", 
"RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", 
"RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "RB", "WR", 
"WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", 
"WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", 
"WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", 
"WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", 
"WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", "WR", 
"WR", "WR", "WR", "WR"), FantasyPoints = c(445, 410, 407, 348, 
351, 359, 354, 364, 402, 368, 353, 347, 349, 335, 366, 325, 297, 
313, 273, 283, 302, 284, 275, 296, 291, 0, 247, 286, 276, 0, 
0, 0, 0, 269, 0, 0, 252, 231, 206, 171, 185, 177, 174, 169, 169, 
171, 139, 131, 170, 170, 162, 129, 162, 119, 130, 126, 130, 340, 
285, 260, 278, 277, 271, 277, 247, 271, 225, 247, 249, 230, 196, 
268, 205, 199, 213, 231, 220, 177, 176, 159, 178, 185, 155, 181, 
157, 190, 177, 164, 156, 166, 169, 179, 158, 129, 147, 99, 158, 
176, 150, 100, 157, 128, 156, 124, 98, 95, 75, 90, 136, 80, 82, 
143, 128, 0, 147, 97, 63, 326, 337, 308, 299, 269, 267, 271, 
242, 243, 241, 239, 243, 242, 244, 209, 220, 233, 239, 221, 198, 
221, 209, 220, 209, 218, 178, 224, 183, 186, 203, 188, 164, 207, 
211, 202, 173, 188, 163, 199, 171, 181, 182, 140, 170, 175, 144, 
142, 164, 147, 131, 170, 160, 182, 136, 153, 157, 152, 148, 175, 
144), DraftValue = c(31, 23, 20, 15, 16, 14, 16, 11, 12, 10, 
10, 3, 7, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 37, 34, 22, 20, 17, 16, 12, 11, 9, 6, 4, 4, 
5, 5, 2, 2, 2, 1, 1, 1, 1, 56, 55, 44, 48, 38, 38, 40, 38, 36, 
34, 34, 33, 27, 30, 28, 27, 23, 21, 23, 21, 19, 18, 10, 15, 16, 
16, 12, 12, 14, 13, 10, 11, 12, 8, 9, 1, 6, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 56, 48, 41, 
40, 37, 31, 34, 29, 30, 28, 28, 26, 24, 26, 23, 23, 22, 21, 20, 
18, 19, 20, 17, 18, 17, 15, 15, 17, 17, 16, 16, 15, 15, 13, 12, 
12, 12, 11, 9, 9, 9, 7, 5, 6, 4, 2, 2, 2, 1, 3, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1)), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, 
-177L))

应用程序代码是:

library(shiny)
library(lpSolve)
library(purrr)
library(rsconnect)


# Define the UI for the app
ui <- fluidPage(
  titlePanel("Fantasy Football Lineup Optimizer"),

  sidebarLayout(
    sidebarPanel(
      numericInput("num_qb", "Enter the number of QBs:", 1, min = 1, max = 5),
      numericInput("num_rb", "Enter the number of RBs:", 3, min = 1, max = 5),
      numericInput("num_wr", "Enter the number of WRs:", 3, min = 1, max = 5),
      numericInput("num_te", "Enter the number of TEs:", 2, min = 1, max = 5),
      numericInput("num_value", "Enter your draft budget:", 200),
      numericInput("num_players", "Adding in your flex spots, enter the total number of starters:", 9, min = 1, max = 15),
      selectInput("remove", "Remove a player:", choices = c("",as.character(players$Player)), multiple = FALSE),
      selectInput("draft_player", "Draft Player", choices = c("",as.character(players$Player)), multiple = FALSE),
      actionButton("update", "Update Lineup")
    ),
    mainPanel(
      tableOutput("team")
    )
  )
)

# Define the server logic
server <- function(input, output, session) {
  players <- players

  # New col to indicate if a player has been drafted
  players$Drafted = "No"

  # Create a new column indicating the player's position
  players$QB <- ifelse(players$Position == "QB", 1, 0)
  players$RB <- ifelse(players$Position == "RB", 1, 0)
  players$WR <- ifelse(players$Position == "WR", 1, 0)
  players$TE <- ifelse(players$Position == "TE", 1, 0)
  players$Total <- 1
  rv <- reactiveValues(players=players)

  # Set up reactive table for lineup output
  updateLineup = reactiveVal(NULL)

  # Define the objective function (maximize fantasy points)
  obj <- players$FantasyPoints

  # Define the constraints (position limits and draft value limit)
  con <- reactive({
    matrix(c(
        # QB constraint
        rv$players$QB,
        # RB constraint
        rv$players$RB,
        # WR constraint
        rv$players$WR,
        # TE constraint
        rv$players$TE,
        # Draft value constraint
        rv$players$DraftValue,
        #Total players constraint
        rv$players$Total
    ), ncol = nrow(rv$players), byrow = TRUE)
  })

  # Define the variables for the lp
  dir <- c("<=", rep(">=",3),"<=","<=")

  # Define initial 'const.rhs'
  init_rhs <- reactive({
    list(
      QB = input$num_qb,
      RB = input$num_rb,
      WR = input$num_wr,
      TE = input$num_te,
      n_val = input$num_value,
      n_players = input$num_players
    )
  })

  # Define reactive 'const.rhs'
  rhs = reactiveValues(const = list())

  # Run once to get the initial values and set them to reactiveValues
  # so they can be changed later
  observeEvent(init_rhs(),{
    rhs$const = init_rhs()
  }, once = TRUE)

  # Define the initial optimal lineup
  initialLineup <- reactive({
    result <- lp("max", obj, con(), dir, init_rhs(), all.bin = TRUE)
    rv$players[result$solution == 1,]
  })

  # Define the function to run when the "update" button is pressed
  observeEvent(input$update, {
    # Remove player here
    if(input$remove != "") {
      removedPlayer <- input$remove
      rv$players <- rv$players[rv$players$Player != removedPlayer,]
      obj <- rv$players$FantasyPoints
    }

    # Draft player
    if(input$draft_player != "") {
      draftedPlayer <- input$draft_player
      draftedPlayer_details <- rv$players[rv$players$Player == draftedPlayer,]
      draftedPlayer_details$Drafted = "Yes"
      rv$players <- rv$players[rv$players$Player != draftedPlayer,]
      rv$draftedPlayers <- rbind(rv$draftedPlayers, draftedPlayer_details)
      obj <- rv$players$FantasyPoints # missing object

      # Subtract constraints: position and n_players by 1 and draft budget by the players 'DraftValue'
      # Necessary so "result" outputs a table with the remaining positions left
      # otherwise it will return an entirely new lineup
      rhs$const = purrr::imap(rhs$const, function(cs, nm) {
        if(nm == draftedPlayer_details$Position) {cs = cs - 1}
        if(nm == "n_players") {cs = cs - 1}
        if(nm == "n_val") {cs = cs - draftedPlayer_details$DraftValue}
        return(cs)
      })
    }

    # Update select inputs to remove players after "Update Lineup" is clicked
    if(input$remove != "" || input$draft_player != "") {
      updateSelectInput(session, inputId = "remove", choices = c("",rv$players), selected = "")
      updateSelectInput(session, inputId = "draft_player", choices = c("",rv$players), selected = "")
    }

    # Define result with updated arguments
    result <- lp("max", obj, con(), dir, rhs$const, all.bin = TRUE)
    # Assign new table to the reactiveVal 'updateLineup'
    updateLineup(rbind(rv$draftedPlayers, rv$players[result$solution == 1,]))
  })

  output$team <- renderTable({
    if (input$update == 0) {
      initialLineup()[, c("Player", "Position", "FantasyPoints", "DraftValue", "Drafted")]
    } else {
      updateLineup()[, c("Player", "Position", "FantasyPoints", "DraftValue", "Drafted")]
    }
  })
}

# Run the app
shinyApp(ui, server)
r optimization shiny linear-programming
3个回答
0
投票

您可能需要稍微修改一下代码。我在这里回答了一个非常相似的问题(你的竞争对手之一?:)):

Python Pulp - 没有重复的名字

我认为,关键是你将从同一个名字池中抽签来填补其他人的“FLEX”位置,因此你需要一些方法来指示每个玩家被分配到哪个位置,以防止重复将 1 名球员分配到“核心”位置(RB、WR 或 TE)和“弹性”位置。您将需要一个约束来对每个玩家求和,并确保所有位置的总分配<= 1. I did that in the other construct via a double-indexed decision variable.

我不擅长

r
和线性规划,但你似乎走在正确的轨道上。也许你可以巧妙地增加你的矩阵并包括那个总和。

编辑:

刚刚回想起我回答的另一个问题,它说明了另一种方法。这里的基本代码(我尽量少修改)非常草率,但我想你可以看出这个想法。这种方法不使用如上所述的双索引,而是对 FLEX 播放器可以覆盖的位置创建更灵活的约束,使用单索引变量(按播放器)。您应该能够从代码中推断出约束结构。你可能需要粗短地用铅笔来计算数学以确保数学是你想要的......我没有太多 QA 它。

Kernel 在 Jupyter notebook 中不断消亡


0
投票

您可以通过满足以下等式来包括弹性位置:

POS <= pos <= POS + FLEX

其中

POS
是给定位置允许的玩家数量,
FLEX
是灵活位置的数量。很容易更新我的答案R优化以找到最佳梦幻足球阵容

player_types <- c(QB = 1, RB = 2, WR = 2, TE = 1)
n_flex <- 1
max_cost <- 200

type_matrix <- t(model.matrix(~Position - 1, players))[paste0("Position", names(player_types)),]

res <- lp(
  direction = "max",
  objective.in = players[["FantasyPoints"]],
  const.mat = rbind(type_matrix, type_matrix[-1,], 1, players[["DraftValue"]]),
  const.dir = c(rep(">=", length(player_types)), rep("<=", length(player_types) - 1), "==", "<="),
  const.rhs = c(player_types, player_types[-1] + n_flex, sum(player_types) + n_flex, max_cost),
  all.bin = TRUE
)

我限制了你需要有弹性播放器,你可以通过在

<=
中放置
==
而不是
const.dir
来改变它。


0
投票

这里有一个包含弹性位置的选项。我依靠 det 的响应来更新应用程序上下文中的约束。

如 det 所述,您需要使用您的 flex 特定位置更新约束矩阵。在这种情况下是 RB、WR 和 TE。然后定义新约束的方向。这里的关键是使用“<=" for your flexes. That represents the max number of draft picks for a particular position given your total number of starters.

之后,您可以设置约束的右值。您需要添加 flex 相关组件。我通过从启动器总数中减去位置输入的总和来定义弹性点的数量。

最后,为了让它按预期工作,我添加了一个新按钮“Set Draft Constraints”。单击后,这将永久设置草稿的约束条件。如果您需要进行更改,则需要重新加载应用程序。 在开始草稿过程之前必须先单击它。

应用代码

library(shiny)
library(lpSolve)
library(purrr)
library(shinyjs)

# Define the UI for the app
ui <- fluidPage(
  useShinyjs(),
  titlePanel("Fantasy Football Lineup Optimizer"),

  sidebarLayout(
    sidebarPanel(
      numericInput("num_qb", "Enter the number of QBs:", 1, min = 1, max = 5),
      numericInput("num_rb", "Enter the number of RBs:", 2, min = 1, max = 5),
      numericInput("num_wr", "Enter the number of WRs:", 2, min = 1, max = 5),
      numericInput("num_te", "Enter the number of TEs:", 1, min = 1, max = 5),
      numericInput("num_value", "Enter your draft budget:", 200),
      numericInput("num_players", "Adding in your flex spots, enter the total number of starters:", 9, min = 1, max = 15),
      actionButton("set_const", "Set Draft Constraints"),
      selectInput("remove", "Remove a player:", choices = c("",as.character(players$Player)), multiple = FALSE),
      selectInput("draft_player", "Draft Player", choices = c("",as.character(players$Player)), multiple = FALSE),
      actionButton("update", "Update Lineup")
    ),
    mainPanel(
      tableOutput("team")
    )
  )
)

# Define the server logic
server <- function(input, output, session) {
  players <- players

  # New col to indicate if a player has been drafted
  players$Drafted = "No"

  # Create a new column indicating the player's position
  players$QB <- ifelse(players$Position == "QB", 1, 0)
  players$RB <- ifelse(players$Position == "RB", 1, 0)
  players$WR <- ifelse(players$Position == "WR", 1, 0)
  players$TE <- ifelse(players$Position == "TE", 1, 0)
  players$Total <- 1
  rv <- reactiveValues(players=players)

  # Set up reactive table for lineup output
  updateLineup = reactiveVal(NULL)

  # Define the objective function (maximize fantasy points)
  obj <- players$FantasyPoints

  # Define the constraints (position limits and draft value limit)
  con <- reactive({
    matrix(c(
        # Position constraint
        rv$players$QB,
        rv$players$RB,
        rv$players$WR,
        rv$players$TE,
        # Flex constraints
        rv$players$RB,
        rv$players$WR,
        rv$players$TE,
        #Total players constraint
        rv$players$Total,
        # Draft value constraint
        rv$players$DraftValue
    ), ncol = nrow(rv$players), byrow = TRUE)
  })

  # Define the variables for the lp
  dir <- c("<=",rep(">=",3),rep("<=",3),"==","<=")

  # Define num of flex spots
  flex_spots = reactiveVal(input$num_players - sum(input$num_qb,input$num_rb,input$num_wr,input$num_te))

  # Define initial 'const.rhs'
  init_rhs <- reactive({
    list(
      QB = input$num_qb,
      RB = input$num_rb,
      WR = input$num_wr,
      TE = input$num_te,
      RB_flex = input$num_rb + flex_spots(),
      WR_flex = input$num_wr + flex_spots(),
      TE_flex = input$num_te + flex_spots(),
      n_players = input$num_players,
      n_val = input$num_value
    )
  })

  # Define reactive 'const.rhs'
  rhs = reactiveValues(const = list())

  # Define the initial optimal lineup
  initialLineup <- reactive({
    result <- lp("max", obj, con(), dir, init_rhs(), all.bin = TRUE)
    rv$players[result$solution == 1,]
  })
  
  # Set constraints and disable draft inputs
  observeEvent(input$set_const, {
    disable(selector = "input[type = 'number']")
    disable(id = "set_const")
    rhs$const = init_rhs()
  })

  # Define the function to run when the "update" button is pressed
  observeEvent(input$update, {
    if(input$set_const == 0) { stop("Please set draft constraints first") }
    
    # Remove player here
    if(input$remove != "") {
      removedPlayer <- input$remove
      rv$players <- rv$players[rv$players$Player != removedPlayer,]
      obj <- rv$players$FantasyPoints
    }

    # Draft player
    if(input$draft_player != "") {
      draftedPlayer <- input$draft_player
      draftedPlayer_details <- rv$players[rv$players$Player == draftedPlayer,]
      draftedPlayer_details$Drafted = "Yes"
      rv$players <- rv$players[rv$players$Player != draftedPlayer,]
      rv$draftedPlayers <- rbind(rv$draftedPlayers, draftedPlayer_details)
      obj <- rv$players$FantasyPoints # missing object

      # Subtract constraints: position and n_players by 1 and draft budget by the players 'DraftValue'
      # Necessary so "result" outputs a table with the remaining positions left
      # otherwise it will return an entirely new lineup
      rhs$const = purrr::imap(rhs$const, function(cs, nm) {
        if(nm == draftedPlayer_details$Position) {cs = cs - 1}
        if(nm == "n_players") {cs = cs - 1}
        if(nm == "n_val") {cs = cs - draftedPlayer_details$DraftValue}
        return(cs)
      })
    }

    # Update select inputs to remove players after "Update Lineup" is clicked
    if(input$remove != "" || input$draft_player != "") {
      updateSelectInput(session, inputId = "remove", choices = c("",rv$players), selected = "")
      updateSelectInput(session, inputId = "draft_player", choices = c("",rv$players), selected = "")
    }

    # Define result with updated arguments
    result <- lp("max", obj, con(), dir, rhs$const, all.bin = TRUE)
    # Assign new table to the reactiveVal 'updateLineup'
    updateLineup(rbind(rv$draftedPlayers, rv$players[result$solution == 1,]))
  })

  output$team <- renderTable({
    if (input$update == 0) {
      initialLineup()[, c("Player", "Position", "FantasyPoints", "DraftValue", "Drafted")]
    } else {
      updateLineup()[, c("Player", "Position", "FantasyPoints", "DraftValue", "Drafted")]
    }
  })
}
© www.soinside.com 2019 - 2024. All rights reserved.