Fantasy Football的Python PuLP优化问题,如何添加某些条件约束?

gas*_*lad 0 python optimization linear-programming pandas pulp

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

我有一个 400 名球员的 Excel 表格以及我如何预测他们的比赛方式,并且我想在给定特定限制的情况下找到 9 名球员的最佳组合。Excel 表包含球员姓名、球员投射、球队球员在场、对手球员面对以及位置。下面是 panda 数据框的头部的样子。

              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
Run Code Online (Sandbox Code Playgroud)

我成功完成的约束条件是:限制最多选择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)
Run Code Online (Sandbox Code Playgroud)

我试图添加但无法弄清楚如何添加的限制:让 9 名球员中的 2 名球员与 QB 属于同一支球队,DST 的对手不能是 9 队的任何人,让 1 名球员的对手是 QB 的球队。知道我该怎么做吗?这些数据位于我的 Excel 文件中,但我不确定如何将这些约束添加到模型中?

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

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

小智 5

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

  1. 至少将n球员与您的四分卫堆叠起来:您的球员池中的每个四分卫都将受到新的限制。约束将如下所示:
[sum of other players on the same team as QB] + -n*[QB] >= 0
Run Code Online (Sandbox Code Playgroud)

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

  1. 不要根据 DST 堆叠任何玩家:这与上面的类似,我们为每个团队都有一个方程式,但是说“此玩家列表中没有一个”而不是“这些玩家中至少有 n 个”会改变数学小的。
[sum of players facing DST] + 8*[DST] <= 8
Run Code Online (Sandbox Code Playgroud)

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

  1. 将您的 QB 与对方球队中的至少一名球员堆叠在一起:这本质上与 1. 相同,但我们选择n=1,并用 QB 的对手而不是队友填充方程的其余部分:
[sum of players on the team facing QB] + -1*[QB] >= 0
Run Code Online (Sandbox Code Playgroud)

同样,如果选择了四分卫,我们还必须选择该等式中的其他球员之一来平衡它并保持总和为非负数。如果没有选择四分卫,这个方程就没有任何作用,因为所有其他涉及的球员都有正系数。

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

player_ids = raw_data.index
player_vars = pulp.LpVariable.dicts('player', player_ids, cat = 'Binary')
Run Code Online (Sandbox Code Playgroud)

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

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,
Run Code Online (Sandbox Code Playgroud)

您可能可以从那里推断出如何做您已经以这种风格完成的所有事情。现在进行 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,
Run Code Online (Sandbox Code Playgroud)

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