梦幻足球的Python PuLP优化问题,如何添加某些条件约束?

问题描述

这是我第一次在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名球员中的其余球员。

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

解决方法

这是我的专长!通常,如果希望约束依赖于特定变量的选择(例如选择了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,则此等式无效,因为我们选择的非DST播放器不会超过8个。

  1. 将您的QB与对战球队中的至少一名球员堆叠在一起:这基本上与1.相同。但是我们选择n=1,然后用QB的对手而不是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'] == 'RB']) >= 2,prob += pulp.lpSum([player_vars[i] for i in player_ids if raw_data['Position'] == '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个玩家不同,该怎么办?您如何为玩家设置最小/最大曝光量?我希望这对您有所帮助,我知道这已经读了很长时间了。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...