【问题标题】:Adding a Flex position in a fantasy football lineup optimizer在梦幻足球阵容优化器中添加 Flex 位置
【发布时间】:2023-02-22 05:26:56
【问题描述】:

我编写了一些 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


    【解决方案1】:

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

    Python Pulp - No duplicate Names

    我认为,关键是你将从同一个名字池中抽签来填补其他人的“FLEX”位置,因此你需要一些方法来指示每个玩家被分配到哪个位置,以防止重复分配1 名球员到“核心”位置(RB、WR 或 TE)和“弹性”位置。您将需要一个约束来对每个玩家求和,并确保所有位置的总分配 <= 1。我在另一个构造中通过双索引决策变量做到了这一点。

    我不会熟练使用 r 和线性编程,但您似乎走在正确的轨道上。也许您可以巧妙地扩充矩阵并包括该总和。

    【讨论】:

      猜你喜欢
      • 2011-11-27
      • 1970-01-01
      • 2017-08-16
      • 1970-01-01
      • 1970-01-01
      • 2020-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多