【问题标题】:Python PuLP Optimization problem for Fantasy Football, how to add Certain Conditional Constraints?Fantasy Football的Python PuLP优化问题,如何添加某些条件约束?
【发布时间】:2021-09-15 21:19:22
【问题描述】:

这是我目前第一次在 python 中使用 PuLP 库。潜入这个库的目的是用python制作一个梦幻足球解算器。我已经成功制作了求解器,但不知道如何添加一些我需要的约束。

我有一个包含 400 名球员的 Excel 表格以及我如何让他们参与比赛,我想在特定限制条件下找到 9 名球员的最佳组合。 excel表格包含球员姓名、球员投影、球队球员在场、对手球员面对和位置。下面是熊猫数据框头部的样子。

              Name  Projection Position Team  Salary Opponent
0             Jets    3.528576      DST  NYJ    2000      IND
1           Texans    7.936528      DST  HOU    2100      PIT
2         Panthers    4.219883      DST  CAR    2200      LAC
3          Raiders    0.904948      DST  LVR    2300       NE

我已经成功完成的限制:限制最多选择9名球员,QB位置只有1人,WR位置3-4人,TE位置1-2人,DST位置1人,RB位置2-3人.

raw_data = pd.read_csv(file_name,engine="python",index_col=False, header=0, delimiter=",", quoting = 3)

#create new columns that has binary numbers if player == a specific position
raw_data["RB"] = (raw_data["Position"] == 'RB').astype(float)
raw_data["WR"] = (raw_data["Position"] == 'WR').astype(float)
raw_data["QB"] = (raw_data["Position"] == 'QB').astype(float)
raw_data["TE"] = (raw_data["Position"] == 'TE').astype(float)
raw_data["DST"] = (raw_data["Position"] == 'DST').astype(float)
raw_data["Salary"] = raw_data["Salary"].astype(float)

total_points = {}
cost = {}
QBs = {}
RBs = {}
WRs = {}
TEs = {}
DST = {}
number_of_players = {}

# i = row index, player = player attributes
for i, player in raw_data.iterrows():
    var_name = 'x' + str(i) # Create variable name
    decision_var = pulp.LpVariable(var_name, cat='Binary') # Initialize Variables

    total_points[decision_var] = player["Projection"] # Create Projection Dictionary
    cost[decision_var] = player["Salary"] # Create Cost Dictionary
    
    # Create Dictionary for Player Types
    QBs[decision_var] = player["QB"]
    RBs[decision_var] = player["RB"]
    WRs[decision_var] = player["WR"]
    TEs[decision_var] = player["TE"]
    DST[decision_var] = player["DST"]
    number_of_players[decision_var] = 1.0

QB_constraint = pulp.LpAffineExpression(QBs)
RB_constraint = pulp.LpAffineExpression(RBs)
WR_constraint = pulp.LpAffineExpression(WRs)
TE_constraint = pulp.LpAffineExpression(TEs)
DST_constraint = pulp.LpAffineExpression(DST)
total_players = pulp.LpAffineExpression(number_of_players)

model += (QB_constraint == 1)
model += (RB_constraint <= 3)
model += (RB_constraint >= 2)
model += (WR_constraint <= 4)
model += (WR_constraint >= 3)
model += (TE_constraint <= 2)
model += (TE_constraint >= 1)
model += (DST_constraint == 1)
model += (total_players == 9)

我试图添加的限制条件并且无法弄清楚如何:选择 9 名球员中的 2 名球员与 QB 在同一支球队中,DST 的对手不能是 9 队中的任何人,有 1 名球员的对手是QB的团队。知道我会怎么做吗?此数据在我的 excel 文件中,但我不确定如何将这些约束添加到模型中?

我一直在查看文档中的案例,但找不到任何根据模型选择来更改最佳输出的示例。示例:如果选择四分卫,则会影响其余 8 名被选中的球员。

感谢任何人可以为我提供的任何帮助

【问题讨论】:

    标签: python pandas optimization linear-programming pulp


    【解决方案1】:

    这正是我的专长!通常,如果您希望约束依赖于特定变量的选择(例如选择哪个 QB 变量),您需要为每个可能的选择设置一个新的约束,以一种有点聪明的方式来确保该约束仅在选择该变量时才执行任何操作。

    1. 至少将n 玩家与您的 QB 堆叠在一起:您将为您的玩家池中的每个 QB 设置一个新的约束。约束将如下所示:
    [sum of other players on the same team as QB] + -n*[QB] >= 0
    

    这样,如果优化器选择 QB,它还必须选择该 QB 团队中的其他玩家 n,以满足当您从该团队的其他玩家数量中减去 n 时的要求,结果是非负的。当没有选择 QB 时,这个方程什么也不做,因为 QB 变量有唯一的负系数。请注意,此方法还允许您通过操纵出现在该等式左侧的玩家来堆叠特定位置(例如 QB-WR 堆叠)。您还可以对其进行调整以强制使用 DST-RB 堆栈。

    1. 不要让任何球员对抗你的 DST:这与上面的类似,因为我们为每支球队都有一个等式,但说“没有这个球员名单”而不是“至少有 n 个球员”会发生变化有点数学。
    [sum of players facing DST] + 8*[DST] <= 8
    

    在此等式中,如果优化器选择 DST,则左侧已经是 8,因此添加对方球队中的任何球员都会使等式超出限制。如果未选择 DST,则此等式无效,因为无论如何我们不会选择超过 8 个非 DST 玩家。

    1. 将您的 QB 与对方球队中的至少一名球员堆叠:这与 1. 基本相同,但我们选择 n=1,并用 QB 的对手而不是队友来填充等式的其余部分:
    [sum of players on the team facing QB] + -1*[QB] >= 0
    

    同样,如果选择了 QB,我们还必须在此等式中选择其他玩家之一来平衡它并保持总数为非负数。如果没有选择 QB,这个方程什么也不做,因为所有其他参与的玩家都有正系数。

    在使用纸浆实现这些方面,我发现使用 LpVariables.dicts 创建变量非常有帮助,这样您就可以多次遍历您的玩家列表,并且每次都访问相同的变量:

    player_ids = raw_data.index
    player_vars = pulp.LpVariable.dicts('player', player_ids, cat = 'Binary')
    

    然后使用列表理解创建您的花名册约束和目标真的很容易,例如:

    prob = pulp.LpProblem("DFS Optimizer", pulp.LpMaximize)
    #Objective
    prob += pulp.lpSum([raw_data['Projection'][i]*player_vars[i] for i in player_ids]),
    ##Total Salary:
    prib += pulp.lpSum([raw_data['Salary'][i]*player_vars[i] for i in player_ids]) <= 50000,
    ##Exactly 9 players:
    prob += pulp.lpSum([player_vars[i] for i in player_ids]) == 9,
    ##2-3 RBs:
    prob += pulp.lpSum([player_vars[i] for i in player_ids if raw_data['Position'][i] == 'RB']) >= 2,
    prob += pulp.lpSum([player_vars[i] for i in player_ids if raw_data['Position'][i] == 'RB']) <= 3,
    

    您可能可以从那里推断出如何以该风格完成您已经完成的所有工作。现在是 QB 堆叠:

    ###Stack QB with 2 teammates
    for qbid in player_ids:
        if raw_data['Position'][qbid] == 'QB':
            prob += pulp.lpSum([player_vars[i] for i in player_ids if 
                               (raw_data['Team'][i] == raw_data['Team'][qbid] and 
                                raw_data['Position'][i] in ('WR', 'RB', 'TE'))] + 
                               [-2*player_vars[qbid]]) >= 0,
    ###Don't stack with opposing DST:
    for dstid in player_ids:
        if raw_data['Position'][dstid] == 'DST':
            prob += pulp.lpSum([player_vars[i] for i in player_ids if
                                raw_data['Team'][i] == raw_data['Opponent'][dstid]] +
                               [8*player_vars[dstid]]) <= 8,
    ###Stack QB with 1 opposing player:
    for qbid in player_ids:
        if raw_data['Position'][qbid] == 'QB':
            prob += pulp.lpSum([player_vars[i] for i in player_ids if
                                raw_data['Team'][i] == raw_data['Opponent'][qbid]] +
                               [-1*player_vars[qbid]]) >= 0,
    

    一旦您搞定并能够使用任何您想要的堆叠规则生成单个阵容,当您开始尝试生成多个阵容以进入 GPP 时,它会变得非常有趣。你如何确保它们都是不同的?如果你想让你的阵容中的任何 2 名至少有 3 名不同的球员怎么办?你如何为你的玩家设置最小/最大曝光?我希望这对您有所帮助,我知道这有点长。

    【讨论】:

    • 感谢您的帮助,非常感谢您的帮助!我使用了这段代码,但有问题。首先,prob += pulp.lpSum([player_vars[i] for i in player_ids if raw_data['Position'] == 'RB']) &gt;= 2, 这个代码在我的代码中不起作用,因为我得到了一个 ValueError 并且需要在 ['Position'] 之后添加any()all() 第二当我求解模型时,虽然我输入了约束,但没有一个他们实际上在工作。它不会准确选择 9 名玩家,不会堆叠,也不会使用位置限制。我的完整代码在这个 pastebin 中:pastebin.com/fSEVL0Vs 我很感激任何建议!谢谢
    • 那一行是我的错误,应该是prob += pulp.lpSum([player_vars[i] for i in player_ids if raw_data['Position'][i] == 'RB']) &gt;= 2,。 (我忘记了raw_data['Position']之后的[i]
    • 查看你的代码,它认为问题在于你有from pulp import *,在这种情况下你想使用它的函数就像lpSum而不是pulp.lpSum。如果您不想更改所有其余代码,只需将该导入语句更改为 import pulp 即可。
    • 太棒了!非常感谢它现在有效。还有一个问题,是否可以。如果我想添加一个约束条件,说 9 名球员中必须有 7 支不同的球队,我会怎么做?
    • 非常感谢您的帮助,我一直在为此苦苦挣扎,非常感谢!
    猜你喜欢
    • 2021-02-27
    • 2021-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-24
    • 1970-01-01
    • 2021-08-18
    • 2016-01-26
    相关资源
    最近更新 更多