James McCool commited on
Commit
36430a1
·
1 Parent(s): 9a0fceb

Update 'Stack Finder' tab to 'Info and Filters', add 'Run Stack Analysis' button, and streamline combination generation logic for player selection in Streamlit app

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +221 -220
src/streamlit_app.py CHANGED
@@ -145,7 +145,7 @@ selected_tab = st.segmented_control(
145
  )
146
 
147
  if selected_tab == 'Stack Finder':
148
- with st.expander("Stack Finder"):
149
  app_info_column, slate_choice_column, filtering_column, stack_info_column = st.columns(4)
150
  with app_info_column:
151
  if st.button("Load/Reset Data", key='reset1'):
@@ -208,232 +208,233 @@ if selected_tab == 'Stack Finder':
208
  own_dict = dict(zip(raw_baselines.Player, raw_baselines.Own))
209
  cost_dict = dict(zip(raw_baselines.Player, raw_baselines.Salary))
210
  qb_dict = dict(zip(qb_lookup.Team, qb_lookup.Player))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
- if site_var == 'Draftkings':
213
- position_limits = {
214
- 'QB': 1,
215
- 'RB': 2,
216
- 'WR': 3,
217
- 'TE': 1,
218
- 'UTIL': 1,
219
- 'DST': 1,
220
- # Add more as needed
221
- }
222
- max_salary = max_sal2
223
- max_players = 9
224
- else:
225
- position_limits = {
226
- 'QB': 1,
227
- 'RB': 2,
228
- 'WR': 3,
229
- 'TE': 1,
230
- 'UTIL': 1,
231
- 'DST': 1,
232
- # Add more as needed
233
- }
234
- max_salary = max_sal2
235
- max_players = 9
236
-
237
- stack_hold_container = st.empty()
238
- comb_list = []
239
- raw_baselines = raw_baselines[raw_baselines['Position'].str.contains('|'.join(pos_var2 + ['QB']))]
240
-
241
- # Create a position dictionary mapping players to their eligible positions
242
- pos_dict = dict(zip(raw_baselines.Player, raw_baselines.Position))
243
-
244
- def is_valid_combination(combo):
245
- # Count positions in this combination
246
- position_counts = {pos: 0 for pos in position_limits.keys()}
247
-
248
- # For each player in the combination
249
- for player in combo:
250
- # Get their eligible positions
251
- player_positions = pos_dict[player].split('/')
252
 
253
- # For each position they can play
254
- for pos in player_positions:
255
- if pos == 'UTIL':
256
- # UTIL can be filled by any position
257
- for p in position_counts:
258
- position_counts[p] += 1
259
-
260
- # Check if any position exceeds its limit
261
- for pos, limit in position_limits.items():
262
- if position_counts[pos] > limit:
263
- return False
264
-
265
- return True
266
-
267
- # Modify the combination generation code
268
- comb_list = []
269
- for cur_team in team_var2:
270
- working_baselines = raw_baselines
271
- working_baselines = working_baselines[working_baselines['Team'] == cur_team]
272
- working_baselines = working_baselines[working_baselines['Position'] != 'DST']
273
- working_baselines = working_baselines[working_baselines['Position'] != 'K']
274
- qb_var = qb_dict[cur_team]
275
- order_list = working_baselines['Player']
276
-
277
- comb = combinations(order_list, stack_size)
278
-
279
- for i in list(comb):
280
- if qb_var in i:
281
- comb_list.append(i)
282
-
283
- # Only add combinations that satisfy position limits
284
- for i in list(comb):
285
- if is_valid_combination(i):
286
- comb_list.append(i)
287
-
288
- comb_DF = pd.DataFrame(comb_list)
289
-
290
- if stack_size == 3:
291
- comb_DF['Team'] = comb_DF[0].map(team_dict)
292
-
293
- comb_DF['Proj'] = sum([comb_DF[0].map(proj_dict),
294
- comb_DF[1].map(proj_dict),
295
- comb_DF[2].map(proj_dict)])
296
-
297
- comb_DF['Salary'] = sum([comb_DF[0].map(cost_dict),
298
- comb_DF[1].map(cost_dict),
299
- comb_DF[2].map(cost_dict)])
300
-
301
- comb_DF['Own%'] = sum([comb_DF[0].map(own_dict),
302
- comb_DF[1].map(own_dict),
303
- comb_DF[2].map(own_dict)])
304
- elif stack_size == 4:
305
- comb_DF['Team'] = comb_DF[0].map(team_dict)
306
-
307
- comb_DF['Proj'] = sum([comb_DF[0].map(proj_dict),
308
- comb_DF[1].map(proj_dict),
309
- comb_DF[2].map(proj_dict),
310
- comb_DF[3].map(proj_dict)])
311
-
312
- comb_DF['Salary'] = sum([comb_DF[0].map(cost_dict),
313
- comb_DF[1].map(cost_dict),
314
- comb_DF[2].map(cost_dict),
315
- comb_DF[3].map(cost_dict)])
316
-
317
- comb_DF['Own%'] = sum([comb_DF[0].map(own_dict),
318
- comb_DF[1].map(own_dict),
319
- comb_DF[2].map(own_dict),
320
- comb_DF[3].map(own_dict)])
321
- elif stack_size == 5:
322
- comb_DF['Team'] = comb_DF[0].map(team_dict)
323
-
324
- comb_DF['Proj'] = sum([comb_DF[0].map(proj_dict),
325
- comb_DF[1].map(proj_dict),
326
- comb_DF[2].map(proj_dict),
327
- comb_DF[3].map(proj_dict),
328
- comb_DF[4].map(proj_dict)])
329
-
330
- comb_DF['Salary'] = sum([comb_DF[0].map(cost_dict),
331
- comb_DF[1].map(cost_dict),
332
- comb_DF[2].map(cost_dict),
333
- comb_DF[3].map(cost_dict),
334
- comb_DF[4].map(cost_dict)])
335
-
336
- comb_DF['Own%'] = sum([comb_DF[0].map(own_dict),
337
- comb_DF[1].map(own_dict),
338
- comb_DF[2].map(own_dict),
339
- comb_DF[3].map(own_dict),
340
- comb_DF[4].map(own_dict)])
341
-
342
- comb_DF = comb_DF.sort_values(by='Proj', ascending=False)
343
- comb_DF = comb_DF.loc[comb_DF['Salary'] <= max_sal2]
344
-
345
- cut_var = 0
346
-
347
- if stack_size == 2:
348
- while cut_var <= int(len(comb_DF)):
349
- try:
350
- if int(cut_var) == 0:
351
- cur_proj = float(comb_DF.iat[cut_var, 3])
352
- cur_own = float(comb_DF.iat[cut_var, 5])
353
- elif int(cut_var) >= 1:
354
- check_own = float(comb_DF.iat[cut_var, 5])
355
- if check_own > cur_own:
356
- comb_DF = comb_DF.drop([cut_var])
357
- cur_own = cur_own
358
- cut_var = cut_var - 1
359
- comb_DF = comb_DF.reset_index()
360
- comb_DF = comb_DF.drop(['index'], axis=1)
361
- elif check_own <= cur_own:
362
  cur_own = float(comb_DF.iat[cut_var, 5])
363
- cut_var = cut_var
364
- cut_var += 1
365
- except:
366
- cut_var += 1
367
- elif stack_size == 3:
368
- while cut_var <= int(len(comb_DF)):
369
- try:
370
- if int(cut_var) == 0:
371
- cur_proj = float(comb_DF.iat[cut_var,4])
372
- cur_own = float(comb_DF.iat[cut_var,6])
373
- elif int(cut_var) >= 1:
374
- check_own = float(comb_DF.iat[cut_var,6])
375
- if check_own > cur_own:
376
- comb_DF = comb_DF.drop([cut_var])
377
- cur_own = cur_own
378
- cut_var = cut_var - 1
379
- comb_DF = comb_DF.reset_index()
380
- comb_DF = comb_DF.drop(['index'], axis=1)
381
- elif check_own <= cur_own:
382
  cur_own = float(comb_DF.iat[cut_var,6])
383
- cut_var = cut_var
384
- cut_var += 1
385
- except:
386
- cut_var += 1
387
- elif stack_size == 4:
388
- while cut_var <= int(len(comb_DF)):
389
- try:
390
- if int(cut_var) == 0:
391
- cur_proj = float(comb_DF.iat[cut_var,5])
392
- cur_own = float(comb_DF.iat[cut_var,7])
393
- elif int(cut_var) >= 1:
394
- check_own = float(comb_DF.iat[cut_var,7])
395
- if check_own > cur_own:
396
- comb_DF = comb_DF.drop([cut_var])
397
- cur_own = cur_own
398
- cut_var = cut_var - 1
399
- comb_DF = comb_DF.reset_index()
400
- comb_DF = comb_DF.drop(['index'], axis=1)
401
- elif check_own <= cur_own:
402
  cur_own = float(comb_DF.iat[cut_var,7])
403
- cut_var = cut_var
404
- cut_var += 1
405
- except:
406
- cut_var += 1
407
- elif stack_size == 5:
408
- while cut_var <= int(len(comb_DF)):
409
- try:
410
- if int(cut_var) == 0:
411
- cur_proj = float(comb_DF.iat[cut_var,6])
412
- cur_own = float(comb_DF.iat[cut_var,8])
413
- elif int(cut_var) >= 1:
414
- check_own = float(comb_DF.iat[cut_var,8])
415
- if check_own > cur_own:
416
- comb_DF = comb_DF.drop([cut_var])
417
- cur_own = cur_own
418
- cut_var = cut_var - 1
419
- comb_DF = comb_DF.reset_index()
420
- comb_DF = comb_DF.drop(['index'], axis=1)
421
- elif check_own <= cur_own:
422
  cur_own = float(comb_DF.iat[cut_var,8])
423
- cut_var = cut_var
424
- cut_var += 1
425
- except:
426
- cut_var += 1
427
- st.session_state['display_frame'] = comb_DF
428
- with stack_hold_container:
429
- stack_hold_container = st.empty()
 
 
 
 
 
 
 
 
 
430
  st.dataframe(st.session_state['display_frame'].style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), hide_index=True, use_container_width = True)
431
- st.download_button(
432
- label="Export Tables",
433
- data=convert_df_to_csv(st.session_state['display_frame']),
434
- file_name='NFL_Stack_Options_export.csv',
435
- mime='text/csv',
436
- )
 
 
437
 
438
  if selected_tab == 'User Upload':
439
  st.info("The Projections file can have any columns in any order, but must contain columns explicitly named: 'Player', 'Salary', 'Position', 'Team', 'Opp', 'Median', and 'Own'.")
 
145
  )
146
 
147
  if selected_tab == 'Stack Finder':
148
+ with st.expander("Info and Filters"):
149
  app_info_column, slate_choice_column, filtering_column, stack_info_column = st.columns(4)
150
  with app_info_column:
151
  if st.button("Load/Reset Data", key='reset1'):
 
208
  own_dict = dict(zip(raw_baselines.Player, raw_baselines.Own))
209
  cost_dict = dict(zip(raw_baselines.Player, raw_baselines.Salary))
210
  qb_dict = dict(zip(qb_lookup.Team, qb_lookup.Player))
211
+ if st.button("Run Stack Analysis", key='run_stack_analysis'):
212
+ if site_var == 'Draftkings':
213
+ position_limits = {
214
+ 'QB': 1,
215
+ 'RB': 2,
216
+ 'WR': 3,
217
+ 'TE': 1,
218
+ 'UTIL': 1,
219
+ 'DST': 1,
220
+ # Add more as needed
221
+ }
222
+ max_salary = max_sal2
223
+ max_players = 9
224
+ else:
225
+ position_limits = {
226
+ 'QB': 1,
227
+ 'RB': 2,
228
+ 'WR': 3,
229
+ 'TE': 1,
230
+ 'UTIL': 1,
231
+ 'DST': 1,
232
+ # Add more as needed
233
+ }
234
+ max_salary = max_sal2
235
+ max_players = 9
236
 
237
+ stack_hold_container = st.empty()
238
+ comb_list = []
239
+ raw_baselines = raw_baselines[raw_baselines['Position'].str.contains('|'.join(pos_var2 + ['QB']))]
240
+
241
+ # Create a position dictionary mapping players to their eligible positions
242
+ pos_dict = dict(zip(raw_baselines.Player, raw_baselines.Position))
243
+
244
+ def is_valid_combination(combo):
245
+ # Count positions in this combination
246
+ position_counts = {pos: 0 for pos in position_limits.keys()}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
 
248
+ # For each player in the combination
249
+ for player in combo:
250
+ # Get their eligible positions
251
+ player_positions = pos_dict[player].split('/')
252
+
253
+ # For each position they can play
254
+ for pos in player_positions:
255
+ if pos == 'UTIL':
256
+ # UTIL can be filled by any position
257
+ for p in position_counts:
258
+ position_counts[p] += 1
259
+
260
+ # Check if any position exceeds its limit
261
+ for pos, limit in position_limits.items():
262
+ if position_counts[pos] > limit:
263
+ return False
264
+
265
+ return True
266
+
267
+ # Modify the combination generation code
268
+ comb_list = []
269
+ for cur_team in team_var2:
270
+ working_baselines = raw_baselines
271
+ working_baselines = working_baselines[working_baselines['Team'] == cur_team]
272
+ working_baselines = working_baselines[working_baselines['Position'] != 'DST']
273
+ working_baselines = working_baselines[working_baselines['Position'] != 'K']
274
+ qb_var = qb_dict[cur_team]
275
+ order_list = working_baselines['Player']
276
+
277
+ comb = combinations(order_list, stack_size)
278
+
279
+ for i in list(comb):
280
+ if qb_var in i:
281
+ comb_list.append(i)
282
+
283
+ # Only add combinations that satisfy position limits
284
+ for i in list(comb):
285
+ if is_valid_combination(i):
286
+ comb_list.append(i)
287
+
288
+ comb_DF = pd.DataFrame(comb_list)
289
+
290
+ if stack_size == 3:
291
+ comb_DF['Team'] = comb_DF[0].map(team_dict)
292
+
293
+ comb_DF['Proj'] = sum([comb_DF[0].map(proj_dict),
294
+ comb_DF[1].map(proj_dict),
295
+ comb_DF[2].map(proj_dict)])
296
+
297
+ comb_DF['Salary'] = sum([comb_DF[0].map(cost_dict),
298
+ comb_DF[1].map(cost_dict),
299
+ comb_DF[2].map(cost_dict)])
300
+
301
+ comb_DF['Own%'] = sum([comb_DF[0].map(own_dict),
302
+ comb_DF[1].map(own_dict),
303
+ comb_DF[2].map(own_dict)])
304
+ elif stack_size == 4:
305
+ comb_DF['Team'] = comb_DF[0].map(team_dict)
306
+
307
+ comb_DF['Proj'] = sum([comb_DF[0].map(proj_dict),
308
+ comb_DF[1].map(proj_dict),
309
+ comb_DF[2].map(proj_dict),
310
+ comb_DF[3].map(proj_dict)])
311
+
312
+ comb_DF['Salary'] = sum([comb_DF[0].map(cost_dict),
313
+ comb_DF[1].map(cost_dict),
314
+ comb_DF[2].map(cost_dict),
315
+ comb_DF[3].map(cost_dict)])
316
+
317
+ comb_DF['Own%'] = sum([comb_DF[0].map(own_dict),
318
+ comb_DF[1].map(own_dict),
319
+ comb_DF[2].map(own_dict),
320
+ comb_DF[3].map(own_dict)])
321
+ elif stack_size == 5:
322
+ comb_DF['Team'] = comb_DF[0].map(team_dict)
323
+
324
+ comb_DF['Proj'] = sum([comb_DF[0].map(proj_dict),
325
+ comb_DF[1].map(proj_dict),
326
+ comb_DF[2].map(proj_dict),
327
+ comb_DF[3].map(proj_dict),
328
+ comb_DF[4].map(proj_dict)])
329
+
330
+ comb_DF['Salary'] = sum([comb_DF[0].map(cost_dict),
331
+ comb_DF[1].map(cost_dict),
332
+ comb_DF[2].map(cost_dict),
333
+ comb_DF[3].map(cost_dict),
334
+ comb_DF[4].map(cost_dict)])
335
+
336
+ comb_DF['Own%'] = sum([comb_DF[0].map(own_dict),
337
+ comb_DF[1].map(own_dict),
338
+ comb_DF[2].map(own_dict),
339
+ comb_DF[3].map(own_dict),
340
+ comb_DF[4].map(own_dict)])
341
+
342
+ comb_DF = comb_DF.sort_values(by='Proj', ascending=False)
343
+ comb_DF = comb_DF.loc[comb_DF['Salary'] <= max_sal2]
344
+
345
+ cut_var = 0
346
+
347
+ if stack_size == 2:
348
+ while cut_var <= int(len(comb_DF)):
349
+ try:
350
+ if int(cut_var) == 0:
351
+ cur_proj = float(comb_DF.iat[cut_var, 3])
 
 
 
 
 
352
  cur_own = float(comb_DF.iat[cut_var, 5])
353
+ elif int(cut_var) >= 1:
354
+ check_own = float(comb_DF.iat[cut_var, 5])
355
+ if check_own > cur_own:
356
+ comb_DF = comb_DF.drop([cut_var])
357
+ cur_own = cur_own
358
+ cut_var = cut_var - 1
359
+ comb_DF = comb_DF.reset_index()
360
+ comb_DF = comb_DF.drop(['index'], axis=1)
361
+ elif check_own <= cur_own:
362
+ cur_own = float(comb_DF.iat[cut_var, 5])
363
+ cut_var = cut_var
364
+ cut_var += 1
365
+ except:
366
+ cut_var += 1
367
+ elif stack_size == 3:
368
+ while cut_var <= int(len(comb_DF)):
369
+ try:
370
+ if int(cut_var) == 0:
371
+ cur_proj = float(comb_DF.iat[cut_var,4])
372
  cur_own = float(comb_DF.iat[cut_var,6])
373
+ elif int(cut_var) >= 1:
374
+ check_own = float(comb_DF.iat[cut_var,6])
375
+ if check_own > cur_own:
376
+ comb_DF = comb_DF.drop([cut_var])
377
+ cur_own = cur_own
378
+ cut_var = cut_var - 1
379
+ comb_DF = comb_DF.reset_index()
380
+ comb_DF = comb_DF.drop(['index'], axis=1)
381
+ elif check_own <= cur_own:
382
+ cur_own = float(comb_DF.iat[cut_var,6])
383
+ cut_var = cut_var
384
+ cut_var += 1
385
+ except:
386
+ cut_var += 1
387
+ elif stack_size == 4:
388
+ while cut_var <= int(len(comb_DF)):
389
+ try:
390
+ if int(cut_var) == 0:
391
+ cur_proj = float(comb_DF.iat[cut_var,5])
392
  cur_own = float(comb_DF.iat[cut_var,7])
393
+ elif int(cut_var) >= 1:
394
+ check_own = float(comb_DF.iat[cut_var,7])
395
+ if check_own > cur_own:
396
+ comb_DF = comb_DF.drop([cut_var])
397
+ cur_own = cur_own
398
+ cut_var = cut_var - 1
399
+ comb_DF = comb_DF.reset_index()
400
+ comb_DF = comb_DF.drop(['index'], axis=1)
401
+ elif check_own <= cur_own:
402
+ cur_own = float(comb_DF.iat[cut_var,7])
403
+ cut_var = cut_var
404
+ cut_var += 1
405
+ except:
406
+ cut_var += 1
407
+ elif stack_size == 5:
408
+ while cut_var <= int(len(comb_DF)):
409
+ try:
410
+ if int(cut_var) == 0:
411
+ cur_proj = float(comb_DF.iat[cut_var,6])
412
  cur_own = float(comb_DF.iat[cut_var,8])
413
+ elif int(cut_var) >= 1:
414
+ check_own = float(comb_DF.iat[cut_var,8])
415
+ if check_own > cur_own:
416
+ comb_DF = comb_DF.drop([cut_var])
417
+ cur_own = cur_own
418
+ cut_var = cut_var - 1
419
+ comb_DF = comb_DF.reset_index()
420
+ comb_DF = comb_DF.drop(['index'], axis=1)
421
+ elif check_own <= cur_own:
422
+ cur_own = float(comb_DF.iat[cut_var,8])
423
+ cut_var = cut_var
424
+ cut_var += 1
425
+ except:
426
+ cut_var += 1
427
+ st.session_state['display_frame'] = comb_DF
428
+ if 'display_frame' in st.session_state:
429
  st.dataframe(st.session_state['display_frame'].style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), hide_index=True, use_container_width = True)
430
+ st.download_button(
431
+ label="Export Tables",
432
+ data=convert_df_to_csv(st.session_state['display_frame']),
433
+ file_name='NFL_Stack_Options_export.csv',
434
+ mime='text/csv',
435
+ )
436
+ else:
437
+ st.info("When you run the stack analysis, the results will be displayed here. Open up the 'Info and Filters' tab to check the settings.")
438
 
439
  if selected_tab == 'User Upload':
440
  st.info("The Projections file can have any columns in any order, but must contain columns explicitly named: 'Player', 'Salary', 'Position', 'Team', 'Opp', 'Median', and 'Own'.")