Spaces:
Sleeping
Sleeping
James McCool
commited on
Commit
·
42541ad
1
Parent(s):
a15c0a1
Add lineup optimization feature in app.py: Implemented a function to generate optimal NFL lineups based on median projections, incorporating position and salary constraints. Added UI button to trigger lineup generation and display results, enhancing user experience for DraftKings and FanDuel users.
Browse files
app.py
CHANGED
|
@@ -121,6 +121,73 @@ t_stamp = f"Last Update: " + str(dk_roo_raw['timestamp'][0]) + f" CST"
|
|
| 121 |
|
| 122 |
tab1, tab2, tab3, tab4, tab5, tab6, tab7 = st.tabs(["Team Stacks Range of Outcomes", "Overall Range of Outcomes", "QB Range of Outcomes", "RB Range of Outcomes", "WR Range of Outcomes", "TE Range of Outcomes", "Optimals"])
|
| 123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
with st.sidebar:
|
| 125 |
st.header("Quick Builder")
|
| 126 |
|
|
@@ -136,6 +203,23 @@ with st.sidebar:
|
|
| 136 |
roo_sample = roo_sample[roo_sample['version'] == 'overall']
|
| 137 |
#roo_sample = roo_sample.sort_values(by='Own', ascending=False)
|
| 138 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
# Create empty lists to store selected players
|
| 140 |
selected_qbs = []
|
| 141 |
selected_rb1 = []
|
|
|
|
| 121 |
|
| 122 |
tab1, tab2, tab3, tab4, tab5, tab6, tab7 = st.tabs(["Team Stacks Range of Outcomes", "Overall Range of Outcomes", "QB Range of Outcomes", "RB Range of Outcomes", "WR Range of Outcomes", "TE Range of Outcomes", "Optimals"])
|
| 123 |
|
| 124 |
+
# ... existing code ...
|
| 125 |
+
|
| 126 |
+
@st.cache_data
|
| 127 |
+
def optimize_lineup(player_data):
|
| 128 |
+
"""
|
| 129 |
+
Creates optimal lineup based on median projections while respecting position and salary constraints
|
| 130 |
+
"""
|
| 131 |
+
# Create optimization problem
|
| 132 |
+
prob = pulp.LpProblem("NFL_Lineup_Optimization", pulp.LpMaximize)
|
| 133 |
+
|
| 134 |
+
# Create player dictionary with binary variables
|
| 135 |
+
players = {}
|
| 136 |
+
for idx, row in player_data.iterrows():
|
| 137 |
+
players[row['Player']] = pulp.LpVariable(f"player_{idx}", 0, 1, pulp.LpBinary)
|
| 138 |
+
|
| 139 |
+
# Objective: Maximize total median points
|
| 140 |
+
prob += pulp.lpSum([players[row['Player']] * row['Median'] for idx, row in player_data.iterrows()])
|
| 141 |
+
|
| 142 |
+
# Constraint: Salary cap
|
| 143 |
+
prob += pulp.lpSum([players[row['Player']] * row['Salary'] for idx, row in player_data.iterrows()]) <= 50000
|
| 144 |
+
|
| 145 |
+
# Position constraints
|
| 146 |
+
qbs = player_data[player_data['Position'] == 'QB']['Player'].tolist()
|
| 147 |
+
rbs = player_data[player_data['Position'] == 'RB']['Player'].tolist()
|
| 148 |
+
wrs = player_data[player_data['Position'] == 'WR']['Player'].tolist()
|
| 149 |
+
tes = player_data[player_data['Position'] == 'TE']['Player'].tolist()
|
| 150 |
+
dsts = player_data[player_data['Position'] == 'DST']['Player'].tolist()
|
| 151 |
+
|
| 152 |
+
# QB: exactly 1
|
| 153 |
+
prob += pulp.lpSum([players[p] for p in qbs]) == 1
|
| 154 |
+
|
| 155 |
+
# RB: 2-3
|
| 156 |
+
prob += pulp.lpSum([players[p] for p in rbs]) >= 2
|
| 157 |
+
prob += pulp.lpSum([players[p] for p in rbs]) <= 3
|
| 158 |
+
|
| 159 |
+
# WR: 3-4
|
| 160 |
+
prob += pulp.lpSum([players[p] for p in wrs]) >= 3
|
| 161 |
+
prob += pulp.lpSum([players[p] for p in wrs]) <= 4
|
| 162 |
+
|
| 163 |
+
# TE: 1-2
|
| 164 |
+
prob += pulp.lpSum([players[p] for p in tes]) >= 1
|
| 165 |
+
prob += pulp.lpSum([players[p] for p in tes]) <= 2
|
| 166 |
+
|
| 167 |
+
# DST: exactly 1
|
| 168 |
+
prob += pulp.lpSum([players[p] for p in dsts]) == 1
|
| 169 |
+
|
| 170 |
+
# Solve the problem
|
| 171 |
+
prob.solve()
|
| 172 |
+
|
| 173 |
+
# Get selected players
|
| 174 |
+
selected_players = []
|
| 175 |
+
total_salary = 0
|
| 176 |
+
total_median = 0
|
| 177 |
+
|
| 178 |
+
for idx, row in player_data.iterrows():
|
| 179 |
+
if players[row['Player']].value() == 1:
|
| 180 |
+
selected_players.append({
|
| 181 |
+
'Player': row['Player'],
|
| 182 |
+
'Position': row['Position'],
|
| 183 |
+
'Salary': row['Salary'],
|
| 184 |
+
'Median': row['Median']
|
| 185 |
+
})
|
| 186 |
+
total_salary += row['Salary']
|
| 187 |
+
total_median += row['Median']
|
| 188 |
+
|
| 189 |
+
return selected_players, total_salary, total_median
|
| 190 |
+
|
| 191 |
with st.sidebar:
|
| 192 |
st.header("Quick Builder")
|
| 193 |
|
|
|
|
| 203 |
roo_sample = roo_sample[roo_sample['version'] == 'overall']
|
| 204 |
#roo_sample = roo_sample.sort_values(by='Own', ascending=False)
|
| 205 |
|
| 206 |
+
st.write("---")
|
| 207 |
+
if st.button("Generate Optimal Lineup"):
|
| 208 |
+
if sidebar_site == 'Draftkings':
|
| 209 |
+
roo_data = dk_roo_raw[dk_roo_raw['slate'] == str(sidebar_slate)]
|
| 210 |
+
roo_data = roo_data[roo_data['version'] == 'overall']
|
| 211 |
+
else:
|
| 212 |
+
roo_data = fd_roo_raw[fd_roo_raw['slate'] == str(sidebar_slate)]
|
| 213 |
+
roo_data = roo_data[roo_data['version'] == 'overall']
|
| 214 |
+
|
| 215 |
+
optimal_players, total_salary, total_median = optimize_lineup(roo_data)
|
| 216 |
+
|
| 217 |
+
st.write("Optimal Lineup:")
|
| 218 |
+
for player in optimal_players:
|
| 219 |
+
st.write(f"{player['Position']}: {player['Player']} (${player['Salary']:,})")
|
| 220 |
+
st.write(f"Total Salary: ${total_salary:,}")
|
| 221 |
+
st.write(f"Projected Points: {total_median:.2f}")
|
| 222 |
+
|
| 223 |
# Create empty lists to store selected players
|
| 224 |
selected_qbs = []
|
| 225 |
selected_rb1 = []
|