James McCool
commited on
Commit
·
02bc151
1
Parent(s):
eb2b4ba
forced unique keys
Browse files- src/streamlit_app.py +75 -52
src/streamlit_app.py
CHANGED
|
@@ -305,9 +305,17 @@ def convert_hb_df(array, column_names):
|
|
| 305 |
array = pd.DataFrame(array, columns=column_names)
|
| 306 |
return array.to_csv().encode('utf-8')
|
| 307 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
col1, col2 = st.columns([1, 9])
|
| 309 |
with col1:
|
| 310 |
-
if st.button("Load/Reset Data", key='
|
| 311 |
st.cache_data.clear()
|
| 312 |
player_stats, dk_stacks_raw, fd_stacks_raw, dk_roo_raw, fd_roo_raw, dk_sd_roo_raw, fd_sd_roo_raw, dk_id_map, fd_id_map, dk_sd_id_map, fd_sd_id_map = init_baselines()
|
| 313 |
dk_lineups = init_DK_lineups('Regular', 'Main')
|
|
@@ -318,9 +326,9 @@ with col2:
|
|
| 318 |
with st.container():
|
| 319 |
col1, col2 = st.columns([3, 3])
|
| 320 |
with col1:
|
| 321 |
-
view_var = st.selectbox("Select view", ["Simple", "Advanced"], key='
|
| 322 |
with col2:
|
| 323 |
-
site_var = st.selectbox("What site do you want to view?", ('Draftkings', 'Fanduel'), key='
|
| 324 |
|
| 325 |
player_stats, dk_stacks_raw, fd_stacks_raw, dk_roo_raw, fd_roo_raw, dk_sd_roo_raw, fd_sd_roo_raw, dk_id_map, fd_id_map, dk_sd_id_map, fd_sd_id_map = init_baselines()
|
| 326 |
|
|
@@ -329,11 +337,11 @@ t_stamp = f"Last Update: " + str(dk_roo_raw['timestamp'][0]) + f" CST"
|
|
| 329 |
tab1, tab2, tab3, tab4 = st.tabs(["Stacks ROO", "Player ROO", "Optimals", "Handbuilder"])
|
| 330 |
|
| 331 |
with tab1:
|
| 332 |
-
with st.expander("Info and Filters"):
|
| 333 |
st.info(t_stamp)
|
| 334 |
with st.container():
|
| 335 |
-
slate_var1 = st.radio("Which data are you loading?", ('Main Slate', 'Secondary Slate', 'Late Slate', 'Thurs-Mon Slate'), key='
|
| 336 |
-
split_var1 = st.radio("Would you like to view the whole slate or just specific games?", ('Full Slate Run', 'Specific Games'), key='
|
| 337 |
if site_var == 'Draftkings':
|
| 338 |
raw_baselines = dk_stacks_raw[dk_stacks_raw['slate'] == str(slate_var1)]
|
| 339 |
raw_baselines = raw_baselines[raw_baselines['version'] == 'overall']
|
|
@@ -343,7 +351,7 @@ with tab1:
|
|
| 343 |
raw_baselines = raw_baselines[raw_baselines['version'] == 'overall']
|
| 344 |
raw_baselines = raw_baselines.iloc[:,:-2]
|
| 345 |
if split_var1 == 'Specific Games':
|
| 346 |
-
team_var1 = st.multiselect('Which teams would you like to include in the ROO?', options = raw_baselines['Team'].unique(), key='
|
| 347 |
elif split_var1 == 'Full Slate Run':
|
| 348 |
team_var1 = raw_baselines.Team.values.tolist()
|
| 349 |
|
|
@@ -353,18 +361,19 @@ with tab1:
|
|
| 353 |
elif view_var == 'Advanced':
|
| 354 |
final_stacks = final_stacks[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Total', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish',
|
| 355 |
'Top_10_finish', '60+%', '2x%', '3x%', '4x%', 'Own', 'LevX']]
|
| 356 |
-
st.dataframe(final_stacks.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(player_roo_format, precision=2), height=750, use_container_width = True)
|
| 357 |
st.download_button(
|
| 358 |
label="Export Tables",
|
| 359 |
data=convert_df_to_csv(final_stacks),
|
| 360 |
file_name='NFL_stacks_export.csv',
|
| 361 |
mime='text/csv',
|
|
|
|
| 362 |
)
|
| 363 |
|
| 364 |
with tab2:
|
| 365 |
-
with st.expander("Info and Filters"):
|
| 366 |
st.info(t_stamp)
|
| 367 |
-
slate_var2 = st.radio("Which data are you loading?", ('Main Slate', 'Secondary Slate', 'Late Slate', 'Thurs-Mon Slate'), key='
|
| 368 |
if site_var == 'Draftkings':
|
| 369 |
raw_baselines = dk_roo_raw[dk_roo_raw['slate'] == str(slate_var2)]
|
| 370 |
|
|
@@ -372,14 +381,14 @@ with tab2:
|
|
| 372 |
elif site_var == 'Fanduel':
|
| 373 |
raw_baselines = fd_roo_raw[fd_roo_raw['slate'] == str(slate_var2)]
|
| 374 |
raw_baselines = raw_baselines.iloc[:,:-2]
|
| 375 |
-
split_var2 = st.radio("Would you like to view the whole slate or just specific games?", ('Full Slate Run', 'Specific Games'), key='
|
| 376 |
if split_var2 == 'Specific Games':
|
| 377 |
-
team_var2 = st.multiselect('Which teams would you like to include in the ROO?', options = raw_baselines['Team'].unique(), key='
|
| 378 |
elif split_var2 == 'Full Slate Run':
|
| 379 |
team_var2 = raw_baselines.Team.values.tolist()
|
| 380 |
-
pos_split2 = st.selectbox('What Position table would you like to view?', options = ['Overall', 'QB', 'RB', 'WR', 'TE'], key='
|
| 381 |
-
pos_combos2 = st.multiselect('If Overall, specific positions?', options = ['QB', 'RB', 'WR', 'TE', 'DST'], default = ['QB', 'RB', 'WR', 'TE', 'DST'], key='
|
| 382 |
-
sal_var2 = st.slider("Is there a certain price range you want to view?", 2000, 15000, (2000, 15000), key='
|
| 383 |
|
| 384 |
if pos_split2 == 'Overall':
|
| 385 |
raw_baselines = raw_baselines[raw_baselines['version'] == 'overall']
|
|
@@ -414,21 +423,22 @@ with tab2:
|
|
| 414 |
elif view_var == 'Advanced':
|
| 415 |
final_Proj = final_Proj[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%', 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX']]
|
| 416 |
disp_proj = final_Proj.set_index('Player')
|
| 417 |
-
st.dataframe(disp_proj.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(player_roo_format, precision=2), height=750, use_container_width = True)
|
| 418 |
st.download_button(
|
| 419 |
label="Export Tables",
|
| 420 |
data=convert_df_to_csv(final_Proj),
|
| 421 |
file_name='NFL_ROO_export.csv',
|
| 422 |
mime='text/csv',
|
|
|
|
| 423 |
)
|
| 424 |
|
| 425 |
with tab3:
|
| 426 |
st.header("Optimals")
|
| 427 |
-
with st.expander("Info and Filters"):
|
| 428 |
st.info("These filters will display various optimals in the table below to pick from. If you want to export the entire set of 10,000 optimals, hit the 'Prepare full data export' button. If you would like to apply the filters here to the 10,000 optimals before you export, use the 'Prepare full data export (Filter)' button.")
|
| 429 |
col1, col2, col3, col4 = st.columns(4)
|
| 430 |
with col1:
|
| 431 |
-
slate_type_var3 = st.radio("Which slate type are you loading?", ('Regular', 'Showdown'), key='
|
| 432 |
if slate_type_var3 == 'Regular':
|
| 433 |
if site_var == 'Draftkings':
|
| 434 |
raw_baselines = dk_roo_raw
|
|
@@ -439,7 +449,7 @@ with tab3:
|
|
| 439 |
raw_baselines = dk_sd_roo_raw
|
| 440 |
elif site_var == 'Fanduel':
|
| 441 |
raw_baselines = fd_sd_roo_raw
|
| 442 |
-
slate_var3 = st.radio("Which slate data are you loading?", ('Main', 'Secondary', 'Auxiliary'), key='
|
| 443 |
if slate_type_var3 == 'Regular':
|
| 444 |
if site_var == 'Draftkings':
|
| 445 |
dk_lineups = init_DK_lineups(slate_type_var3, slate_var3)
|
|
@@ -451,26 +461,26 @@ with tab3:
|
|
| 451 |
elif site_var == 'Fanduel':
|
| 452 |
fd_lineups = init_FD_lineups(slate_type_var3, slate_var3)
|
| 453 |
with col2:
|
| 454 |
-
lineup_num_var = st.number_input("How many lineups do you want to display?", min_value=1, max_value=1000, value=150, step=1)
|
| 455 |
-
player_var1 = st.radio("Do you want a frame with specific Players?", ('Full Slate', 'Specific Players'), key='
|
| 456 |
if player_var1 == 'Specific Players':
|
| 457 |
-
player_var2 = st.multiselect('Which players do you want?', options = raw_baselines['Player'].unique())
|
| 458 |
elif player_var1 == 'Full Slate':
|
| 459 |
player_var2 = raw_baselines.Player.values.tolist()
|
| 460 |
with col3:
|
| 461 |
if site_var == 'Draftkings':
|
| 462 |
-
salary_min_var = st.number_input("Minimum salary used", min_value = 0, max_value = 50000, value = 49000, step = 100, key = '
|
| 463 |
-
salary_max_var = st.number_input("Maximum salary used", min_value = 0, max_value = 50000, value = 50000, step = 100, key = '
|
| 464 |
elif site_var == 'Fanduel':
|
| 465 |
-
salary_min_var = st.number_input("Minimum salary used", min_value = 0, max_value = 60000, value = 59000, step = 100, key = '
|
| 466 |
-
salary_max_var = st.number_input("Maximum salary used", min_value = 0, max_value = 60000, value = 60000, step = 100, key = '
|
| 467 |
with col4:
|
| 468 |
if site_var == 'Draftkings':
|
| 469 |
-
min_stacks_var = st.number_input("Minimum stacks used", min_value = 0, max_value = 5, value = 1, step = 1, key = '
|
| 470 |
-
max_stacks_var = st.number_input("Maximum stacks used", min_value = 0, max_value = 5, value = 5, step = 1, key = '
|
| 471 |
elif site_var == 'Fanduel':
|
| 472 |
-
min_stacks_var = st.number_input("Minimum stacks used", min_value = 0, max_value = 4, value = 1, step = 1, key = '
|
| 473 |
-
max_stacks_var = st.number_input("Maximum stacks used", min_value = 0, max_value = 4, value = 4, step = 1, key = '
|
| 474 |
|
| 475 |
|
| 476 |
if site_var == 'Draftkings':
|
|
@@ -496,7 +506,7 @@ with tab3:
|
|
| 496 |
|
| 497 |
reg_dl_col, filtered_dl_col, blank_dl_col = st.columns([2, 2, 6])
|
| 498 |
with reg_dl_col:
|
| 499 |
-
if st.button("Prepare full data export", key='
|
| 500 |
name_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
|
| 501 |
data_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
|
| 502 |
if site_var == 'Draftkings':
|
|
@@ -524,12 +534,14 @@ with tab3:
|
|
| 524 |
data=convert_df(data_export),
|
| 525 |
file_name='NFL_optimals_export.csv',
|
| 526 |
mime='text/csv',
|
|
|
|
| 527 |
)
|
| 528 |
st.download_button(
|
| 529 |
label="Export optimals set (Names)",
|
| 530 |
data=convert_df(name_export),
|
| 531 |
file_name='NFL_optimals_export.csv',
|
| 532 |
mime='text/csv',
|
|
|
|
| 533 |
)
|
| 534 |
with pm_opt_col:
|
| 535 |
if site_var == 'Draftkings':
|
|
@@ -547,6 +559,7 @@ with tab3:
|
|
| 547 |
data=convert_pm_df(data_export),
|
| 548 |
file_name='NFL_optimals_export.csv',
|
| 549 |
mime='text/csv',
|
|
|
|
| 550 |
)
|
| 551 |
|
| 552 |
if site_var == 'Draftkings':
|
|
@@ -564,9 +577,10 @@ with tab3:
|
|
| 564 |
data=convert_pm_df(name_export),
|
| 565 |
file_name='NFL_optimals_export.csv',
|
| 566 |
mime='text/csv',
|
|
|
|
| 567 |
)
|
| 568 |
with filtered_dl_col:
|
| 569 |
-
if st.button("Prepare full data export (Filtered)", key='
|
| 570 |
name_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
|
| 571 |
data_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
|
| 572 |
if site_var == 'Draftkings':
|
|
@@ -600,12 +614,14 @@ with tab3:
|
|
| 600 |
data=convert_df(data_export),
|
| 601 |
file_name='NFL_optimals_export.csv',
|
| 602 |
mime='text/csv',
|
|
|
|
| 603 |
)
|
| 604 |
st.download_button(
|
| 605 |
label="Export optimals set (Names)",
|
| 606 |
data=convert_df(name_export),
|
| 607 |
file_name='NFL_optimals_export.csv',
|
| 608 |
mime='text/csv',
|
|
|
|
| 609 |
)
|
| 610 |
with pm_opt_col:
|
| 611 |
if site_var == 'Draftkings':
|
|
@@ -623,6 +639,7 @@ with tab3:
|
|
| 623 |
data=convert_pm_df(data_export),
|
| 624 |
file_name='NFL_optimals_export.csv',
|
| 625 |
mime='text/csv',
|
|
|
|
| 626 |
)
|
| 627 |
|
| 628 |
if site_var == 'Draftkings':
|
|
@@ -640,6 +657,7 @@ with tab3:
|
|
| 640 |
data=convert_pm_df(name_export),
|
| 641 |
file_name='NFL_optimals_export.csv',
|
| 642 |
mime='text/csv',
|
|
|
|
| 643 |
)
|
| 644 |
|
| 645 |
if site_var == 'Draftkings':
|
|
@@ -697,7 +715,7 @@ with tab3:
|
|
| 697 |
export_file[col_idx] = export_file[col_idx].map(fd_id_map)
|
| 698 |
|
| 699 |
with st.container():
|
| 700 |
-
if st.button("Reset Optimals", key='
|
| 701 |
for key in st.session_state.keys():
|
| 702 |
del st.session_state[key]
|
| 703 |
if site_var == 'Draftkings':
|
|
@@ -705,18 +723,20 @@ with tab3:
|
|
| 705 |
elif site_var == 'Fanduel':
|
| 706 |
st.session_state.working_seed = fd_lineups.copy()
|
| 707 |
if 'data_export_display' in st.session_state:
|
| 708 |
-
st.dataframe(st.session_state.data_export_display.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), height=500, use_container_width = True)
|
| 709 |
st.download_button(
|
| 710 |
label="Export display optimals (IDs)",
|
| 711 |
data=convert_df(export_file),
|
| 712 |
file_name='NFL_display_optimals.csv',
|
| 713 |
mime='text/csv',
|
|
|
|
| 714 |
)
|
| 715 |
st.download_button(
|
| 716 |
label="Export display optimals (Names)",
|
| 717 |
data=convert_df(name_export),
|
| 718 |
file_name='NFL_display_optimals.csv',
|
| 719 |
mime='text/csv',
|
|
|
|
| 720 |
)
|
| 721 |
|
| 722 |
with st.container():
|
|
@@ -824,7 +844,7 @@ with tab3:
|
|
| 824 |
'Salary': '{:.2f}',
|
| 825 |
'Proj': '{:.2f}',
|
| 826 |
'Own': '{:.2f}'
|
| 827 |
-
}).background_gradient(cmap='RdYlGn', axis=0, subset=['Salary', 'Proj', 'Own']), use_container_width=True)
|
| 828 |
|
| 829 |
with st.container():
|
| 830 |
tab1, tab2 = st.tabs(["Display Frequency", "Seed Frame Frequency"])
|
|
@@ -862,13 +882,14 @@ with tab3:
|
|
| 862 |
|
| 863 |
# Display the table
|
| 864 |
st.write("Player Frequency Table:")
|
| 865 |
-
st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True)
|
| 866 |
|
| 867 |
st.download_button(
|
| 868 |
label="Export player frequency",
|
| 869 |
data=convert_df_to_csv(summary_df),
|
| 870 |
file_name='NFL_player_frequency.csv',
|
| 871 |
mime='text/csv',
|
|
|
|
| 872 |
)
|
| 873 |
with tab2:
|
| 874 |
if 'working_seed' in st.session_state:
|
|
@@ -903,13 +924,14 @@ with tab3:
|
|
| 903 |
|
| 904 |
# Display the table
|
| 905 |
st.write("Seed Frame Frequency Table:")
|
| 906 |
-
st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True)
|
| 907 |
|
| 908 |
st.download_button(
|
| 909 |
label="Export seed frame frequency",
|
| 910 |
data=convert_df_to_csv(summary_df),
|
| 911 |
file_name='NFL_seed_frame_frequency.csv',
|
| 912 |
mime='text/csv',
|
|
|
|
| 913 |
)
|
| 914 |
|
| 915 |
with tab4:
|
|
@@ -917,7 +939,7 @@ with tab4:
|
|
| 917 |
with col1:
|
| 918 |
st.header("Handbuilder")
|
| 919 |
with col2:
|
| 920 |
-
slate_var3 = st.selectbox("Slate Selection", options=['Main', 'Secondary', 'Auxiliary'])
|
| 921 |
if site_var == 'Draftkings':
|
| 922 |
if slate_var3 == 'Main':
|
| 923 |
handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Main Slate']
|
|
@@ -970,15 +992,15 @@ with tab4:
|
|
| 970 |
slot_counts = lineup['Slot'].value_counts() if not lineup.empty else {}
|
| 971 |
|
| 972 |
# --- PLAYER FILTERS ---
|
| 973 |
-
with st.expander("Player Filters"):
|
| 974 |
col1, col2 = st.columns(2)
|
| 975 |
with col1:
|
| 976 |
-
pos_select3 = st.multiselect("Select your position(s)", options=['QB', 'RB', 'WR', 'TE', 'UTIL', 'DST'], key='
|
| 977 |
with col2:
|
| 978 |
-
salary_var = st.number_input("Salary Max", min_value = 0, max_value = 20000, value = 20000, step = 100)
|
| 979 |
|
| 980 |
# --- TEAM FILTER UI ---
|
| 981 |
-
with st.expander("Team Filters"):
|
| 982 |
all_teams = sorted(handbuild_roo['Team'].unique())
|
| 983 |
st.markdown("**Toggle teams to include:**")
|
| 984 |
team_cols = st.columns(len(all_teams) // 2 + 1)
|
|
@@ -988,7 +1010,7 @@ with tab4:
|
|
| 988 |
col = team_cols[idx % len(team_cols)]
|
| 989 |
if f"handbuilder_team_{team}" not in st.session_state:
|
| 990 |
st.session_state[f"handbuilder_team_{team}"] = False
|
| 991 |
-
checked = col.toggle(team, value=st.session_state[f"handbuilder_team_{team}"], key=f"handbuilder_team_{team}")
|
| 992 |
if checked:
|
| 993 |
selected_teams.append(team)
|
| 994 |
|
|
@@ -1010,12 +1032,12 @@ with tab4:
|
|
| 1010 |
player_select_df = player_select_df[player_select_df['Salary'] <= salary_var]
|
| 1011 |
|
| 1012 |
|
| 1013 |
-
with st.expander("Quick Fill Options"):
|
| 1014 |
-
auto_team_var = st.selectbox("Auto Fill Team", options=all_teams)
|
| 1015 |
-
auto_size_var = st.selectbox("Auto Fill Size", options=[3, 4, 5])
|
| 1016 |
-
auto_range_var = st.selectbox("Auto Fill Options", options=['QB/WR', 'RB/WR/TE', 'QB/WR/TE/RB'])
|
| 1017 |
# --- QUICK FILL LOGIC ---
|
| 1018 |
-
if st.button("Quick Fill", key="
|
| 1019 |
# 1. Get all eligible players from the selected team, not already in the lineup
|
| 1020 |
current_players = set(st.session_state['handbuilder_lineup']['Player'])
|
| 1021 |
team_players = player_select_df[
|
|
@@ -1198,7 +1220,7 @@ with tab4:
|
|
| 1198 |
lineup_display_df.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn', subset=['Median']).background_gradient(cmap='RdYlGn_r', subset=['Salary', 'Own']).format(precision=2),
|
| 1199 |
on_select="rerun",
|
| 1200 |
selection_mode=["single-row"],
|
| 1201 |
-
key="
|
| 1202 |
height=445,
|
| 1203 |
hide_index=True
|
| 1204 |
)
|
|
@@ -1277,11 +1299,11 @@ with tab4:
|
|
| 1277 |
# Optionally, add a button to clear the lineup
|
| 1278 |
clear_col, save_col, export_col, clear_saved_col, blank_col = st.columns([2, 2, 2, 2, 12])
|
| 1279 |
with clear_col:
|
| 1280 |
-
if st.button("Clear Lineup", key='
|
| 1281 |
st.session_state['handbuilder_lineup'] = pd.DataFrame(columns=['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot'])
|
| 1282 |
st.rerun()
|
| 1283 |
with save_col:
|
| 1284 |
-
if st.button("Save Lineup", key='
|
| 1285 |
if 'saved_lineups' in st.session_state:
|
| 1286 |
st.session_state['saved_lineups'].append(lineup_display_df['Player'].tolist())
|
| 1287 |
print(st.session_state['saved_lineups'])
|
|
@@ -1298,6 +1320,7 @@ with tab4:
|
|
| 1298 |
data=convert_hb_df(saved_lineups_array, dk_hb_columns if site_var == 'Draftkings' else fd_hb_columns),
|
| 1299 |
file_name='handbuilds_export.csv',
|
| 1300 |
mime='text/csv',
|
|
|
|
| 1301 |
)
|
| 1302 |
else:
|
| 1303 |
st.write("No saved lineups to export")
|
|
@@ -1308,7 +1331,7 @@ with tab4:
|
|
| 1308 |
st.write("No saved lineups")
|
| 1309 |
|
| 1310 |
with clear_saved_col:
|
| 1311 |
-
if st.button("Clear Saved", key='
|
| 1312 |
if 'saved_lineups' in st.session_state:
|
| 1313 |
del st.session_state['saved_lineups']
|
| 1314 |
st.rerun()
|
|
|
|
| 305 |
array = pd.DataFrame(array, columns=column_names)
|
| 306 |
return array.to_csv().encode('utf-8')
|
| 307 |
|
| 308 |
+
# Add this function at the top after imports
|
| 309 |
+
def get_unique_key(prefix, base_key):
|
| 310 |
+
"""Generate a unique key for Streamlit elements"""
|
| 311 |
+
if f"{prefix}_{base_key}" not in st.session_state:
|
| 312 |
+
st.session_state[f"{prefix}_{base_key}"] = 0
|
| 313 |
+
st.session_state[f"{prefix}_{base_key}"] += 1
|
| 314 |
+
return f"{prefix}_{base_key}_{st.session_state[f'{prefix}_{base_key}']}"
|
| 315 |
+
|
| 316 |
col1, col2 = st.columns([1, 9])
|
| 317 |
with col1:
|
| 318 |
+
if st.button("Load/Reset Data", key='reset_data_button'):
|
| 319 |
st.cache_data.clear()
|
| 320 |
player_stats, dk_stacks_raw, fd_stacks_raw, dk_roo_raw, fd_roo_raw, dk_sd_roo_raw, fd_sd_roo_raw, dk_id_map, fd_id_map, dk_sd_id_map, fd_sd_id_map = init_baselines()
|
| 321 |
dk_lineups = init_DK_lineups('Regular', 'Main')
|
|
|
|
| 326 |
with st.container():
|
| 327 |
col1, col2 = st.columns([3, 3])
|
| 328 |
with col1:
|
| 329 |
+
view_var = st.selectbox("Select view", ["Simple", "Advanced"], key='view_selectbox')
|
| 330 |
with col2:
|
| 331 |
+
site_var = st.selectbox("What site do you want to view?", ('Draftkings', 'Fanduel'), key='site_selectbox')
|
| 332 |
|
| 333 |
player_stats, dk_stacks_raw, fd_stacks_raw, dk_roo_raw, fd_roo_raw, dk_sd_roo_raw, fd_sd_roo_raw, dk_id_map, fd_id_map, dk_sd_id_map, fd_sd_id_map = init_baselines()
|
| 334 |
|
|
|
|
| 337 |
tab1, tab2, tab3, tab4 = st.tabs(["Stacks ROO", "Player ROO", "Optimals", "Handbuilder"])
|
| 338 |
|
| 339 |
with tab1:
|
| 340 |
+
with st.expander("Info and Filters", key='stacks_info_expander'):
|
| 341 |
st.info(t_stamp)
|
| 342 |
with st.container():
|
| 343 |
+
slate_var1 = st.radio("Which data are you loading?", ('Main Slate', 'Secondary Slate', 'Late Slate', 'Thurs-Mon Slate'), key='slate_var1_radio')
|
| 344 |
+
split_var1 = st.radio("Would you like to view the whole slate or just specific games?", ('Full Slate Run', 'Specific Games'), key='split_var1_radio')
|
| 345 |
if site_var == 'Draftkings':
|
| 346 |
raw_baselines = dk_stacks_raw[dk_stacks_raw['slate'] == str(slate_var1)]
|
| 347 |
raw_baselines = raw_baselines[raw_baselines['version'] == 'overall']
|
|
|
|
| 351 |
raw_baselines = raw_baselines[raw_baselines['version'] == 'overall']
|
| 352 |
raw_baselines = raw_baselines.iloc[:,:-2]
|
| 353 |
if split_var1 == 'Specific Games':
|
| 354 |
+
team_var1 = st.multiselect('Which teams would you like to include in the ROO?', options = raw_baselines['Team'].unique(), key='team_var1_multiselect')
|
| 355 |
elif split_var1 == 'Full Slate Run':
|
| 356 |
team_var1 = raw_baselines.Team.values.tolist()
|
| 357 |
|
|
|
|
| 361 |
elif view_var == 'Advanced':
|
| 362 |
final_stacks = final_stacks[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Total', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish',
|
| 363 |
'Top_10_finish', '60+%', '2x%', '3x%', '4x%', 'Own', 'LevX']]
|
| 364 |
+
st.dataframe(final_stacks.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(player_roo_format, precision=2), height=750, use_container_width = True, key='stacks_dataframe')
|
| 365 |
st.download_button(
|
| 366 |
label="Export Tables",
|
| 367 |
data=convert_df_to_csv(final_stacks),
|
| 368 |
file_name='NFL_stacks_export.csv',
|
| 369 |
mime='text/csv',
|
| 370 |
+
key='stacks_export_button'
|
| 371 |
)
|
| 372 |
|
| 373 |
with tab2:
|
| 374 |
+
with st.expander("Info and Filters", key='player_info_expander'):
|
| 375 |
st.info(t_stamp)
|
| 376 |
+
slate_var2 = st.radio("Which data are you loading?", ('Main Slate', 'Secondary Slate', 'Late Slate', 'Thurs-Mon Slate'), key='slate_var2_radio')
|
| 377 |
if site_var == 'Draftkings':
|
| 378 |
raw_baselines = dk_roo_raw[dk_roo_raw['slate'] == str(slate_var2)]
|
| 379 |
|
|
|
|
| 381 |
elif site_var == 'Fanduel':
|
| 382 |
raw_baselines = fd_roo_raw[fd_roo_raw['slate'] == str(slate_var2)]
|
| 383 |
raw_baselines = raw_baselines.iloc[:,:-2]
|
| 384 |
+
split_var2 = st.radio("Would you like to view the whole slate or just specific games?", ('Full Slate Run', 'Specific Games'), key='split_var2_radio')
|
| 385 |
if split_var2 == 'Specific Games':
|
| 386 |
+
team_var2 = st.multiselect('Which teams would you like to include in the ROO?', options = raw_baselines['Team'].unique(), key='team_var2_multiselect')
|
| 387 |
elif split_var2 == 'Full Slate Run':
|
| 388 |
team_var2 = raw_baselines.Team.values.tolist()
|
| 389 |
+
pos_split2 = st.selectbox('What Position table would you like to view?', options = ['Overall', 'QB', 'RB', 'WR', 'TE'], key='pos_split2_selectbox')
|
| 390 |
+
pos_combos2 = st.multiselect('If Overall, specific positions?', options = ['QB', 'RB', 'WR', 'TE', 'DST'], default = ['QB', 'RB', 'WR', 'TE', 'DST'], key='pos_combos2_multiselect')
|
| 391 |
+
sal_var2 = st.slider("Is there a certain price range you want to view?", 2000, 15000, (2000, 15000), key='sal_var2_slider')
|
| 392 |
|
| 393 |
if pos_split2 == 'Overall':
|
| 394 |
raw_baselines = raw_baselines[raw_baselines['version'] == 'overall']
|
|
|
|
| 423 |
elif view_var == 'Advanced':
|
| 424 |
final_Proj = final_Proj[['Player', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%', 'Own', 'Small_Field_Own', 'Large_Field_Own', 'Cash_Field_Own', 'CPT_Own', 'LevX']]
|
| 425 |
disp_proj = final_Proj.set_index('Player')
|
| 426 |
+
st.dataframe(disp_proj.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(player_roo_format, precision=2), height=750, use_container_width = True, key='player_dataframe')
|
| 427 |
st.download_button(
|
| 428 |
label="Export Tables",
|
| 429 |
data=convert_df_to_csv(final_Proj),
|
| 430 |
file_name='NFL_ROO_export.csv',
|
| 431 |
mime='text/csv',
|
| 432 |
+
key='player_export_button'
|
| 433 |
)
|
| 434 |
|
| 435 |
with tab3:
|
| 436 |
st.header("Optimals")
|
| 437 |
+
with st.expander("Info and Filters", key='optimals_info_expander'):
|
| 438 |
st.info("These filters will display various optimals in the table below to pick from. If you want to export the entire set of 10,000 optimals, hit the 'Prepare full data export' button. If you would like to apply the filters here to the 10,000 optimals before you export, use the 'Prepare full data export (Filter)' button.")
|
| 439 |
col1, col2, col3, col4 = st.columns(4)
|
| 440 |
with col1:
|
| 441 |
+
slate_type_var3 = st.radio("Which slate type are you loading?", ('Regular', 'Showdown'), key='slate_type_var3_radio')
|
| 442 |
if slate_type_var3 == 'Regular':
|
| 443 |
if site_var == 'Draftkings':
|
| 444 |
raw_baselines = dk_roo_raw
|
|
|
|
| 449 |
raw_baselines = dk_sd_roo_raw
|
| 450 |
elif site_var == 'Fanduel':
|
| 451 |
raw_baselines = fd_sd_roo_raw
|
| 452 |
+
slate_var3 = st.radio("Which slate data are you loading?", ('Main', 'Secondary', 'Auxiliary'), key='slate_var3_radio')
|
| 453 |
if slate_type_var3 == 'Regular':
|
| 454 |
if site_var == 'Draftkings':
|
| 455 |
dk_lineups = init_DK_lineups(slate_type_var3, slate_var3)
|
|
|
|
| 461 |
elif site_var == 'Fanduel':
|
| 462 |
fd_lineups = init_FD_lineups(slate_type_var3, slate_var3)
|
| 463 |
with col2:
|
| 464 |
+
lineup_num_var = st.number_input("How many lineups do you want to display?", min_value=1, max_value=1000, value=150, step=1, key='lineup_num_var_input')
|
| 465 |
+
player_var1 = st.radio("Do you want a frame with specific Players?", ('Full Slate', 'Specific Players'), key='player_var1_radio')
|
| 466 |
if player_var1 == 'Specific Players':
|
| 467 |
+
player_var2 = st.multiselect('Which players do you want?', options = raw_baselines['Player'].unique(), key='player_var2_multiselect')
|
| 468 |
elif player_var1 == 'Full Slate':
|
| 469 |
player_var2 = raw_baselines.Player.values.tolist()
|
| 470 |
with col3:
|
| 471 |
if site_var == 'Draftkings':
|
| 472 |
+
salary_min_var = st.number_input("Minimum salary used", min_value = 0, max_value = 50000, value = 49000, step = 100, key = 'salary_min_var_dk')
|
| 473 |
+
salary_max_var = st.number_input("Maximum salary used", min_value = 0, max_value = 50000, value = 50000, step = 100, key = 'salary_max_var_dk')
|
| 474 |
elif site_var == 'Fanduel':
|
| 475 |
+
salary_min_var = st.number_input("Minimum salary used", min_value = 0, max_value = 60000, value = 59000, step = 100, key = 'salary_min_var_fd')
|
| 476 |
+
salary_max_var = st.number_input("Maximum salary used", min_value = 0, max_value = 60000, value = 60000, step = 100, key = 'salary_max_var_fd')
|
| 477 |
with col4:
|
| 478 |
if site_var == 'Draftkings':
|
| 479 |
+
min_stacks_var = st.number_input("Minimum stacks used", min_value = 0, max_value = 5, value = 1, step = 1, key = 'min_stacks_var_dk')
|
| 480 |
+
max_stacks_var = st.number_input("Maximum stacks used", min_value = 0, max_value = 5, value = 5, step = 1, key = 'max_stacks_var_dk')
|
| 481 |
elif site_var == 'Fanduel':
|
| 482 |
+
min_stacks_var = st.number_input("Minimum stacks used", min_value = 0, max_value = 4, value = 1, step = 1, key = 'min_stacks_var_fd')
|
| 483 |
+
max_stacks_var = st.number_input("Maximum stacks used", min_value = 0, max_value = 4, value = 4, step = 1, key = 'max_stacks_var_fd')
|
| 484 |
|
| 485 |
|
| 486 |
if site_var == 'Draftkings':
|
|
|
|
| 506 |
|
| 507 |
reg_dl_col, filtered_dl_col, blank_dl_col = st.columns([2, 2, 6])
|
| 508 |
with reg_dl_col:
|
| 509 |
+
if st.button("Prepare full data export", key='data_export_button'):
|
| 510 |
name_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
|
| 511 |
data_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
|
| 512 |
if site_var == 'Draftkings':
|
|
|
|
| 534 |
data=convert_df(data_export),
|
| 535 |
file_name='NFL_optimals_export.csv',
|
| 536 |
mime='text/csv',
|
| 537 |
+
key='export_optimals_ids_button'
|
| 538 |
)
|
| 539 |
st.download_button(
|
| 540 |
label="Export optimals set (Names)",
|
| 541 |
data=convert_df(name_export),
|
| 542 |
file_name='NFL_optimals_export.csv',
|
| 543 |
mime='text/csv',
|
| 544 |
+
key='export_optimals_names_button'
|
| 545 |
)
|
| 546 |
with pm_opt_col:
|
| 547 |
if site_var == 'Draftkings':
|
|
|
|
| 559 |
data=convert_pm_df(data_export),
|
| 560 |
file_name='NFL_optimals_export.csv',
|
| 561 |
mime='text/csv',
|
| 562 |
+
key='export_pm_ids_button'
|
| 563 |
)
|
| 564 |
|
| 565 |
if site_var == 'Draftkings':
|
|
|
|
| 577 |
data=convert_pm_df(name_export),
|
| 578 |
file_name='NFL_optimals_export.csv',
|
| 579 |
mime='text/csv',
|
| 580 |
+
key='export_pm_names_button'
|
| 581 |
)
|
| 582 |
with filtered_dl_col:
|
| 583 |
+
if st.button("Prepare full data export (Filtered)", key='data_export_filtered_button'):
|
| 584 |
name_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
|
| 585 |
data_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names)
|
| 586 |
if site_var == 'Draftkings':
|
|
|
|
| 614 |
data=convert_df(data_export),
|
| 615 |
file_name='NFL_optimals_export.csv',
|
| 616 |
mime='text/csv',
|
| 617 |
+
key='export_filtered_optimals_ids_button'
|
| 618 |
)
|
| 619 |
st.download_button(
|
| 620 |
label="Export optimals set (Names)",
|
| 621 |
data=convert_df(name_export),
|
| 622 |
file_name='NFL_optimals_export.csv',
|
| 623 |
mime='text/csv',
|
| 624 |
+
key='export_filtered_optimals_names_button'
|
| 625 |
)
|
| 626 |
with pm_opt_col:
|
| 627 |
if site_var == 'Draftkings':
|
|
|
|
| 639 |
data=convert_pm_df(data_export),
|
| 640 |
file_name='NFL_optimals_export.csv',
|
| 641 |
mime='text/csv',
|
| 642 |
+
key='export_filtered_pm_ids_button'
|
| 643 |
)
|
| 644 |
|
| 645 |
if site_var == 'Draftkings':
|
|
|
|
| 657 |
data=convert_pm_df(name_export),
|
| 658 |
file_name='NFL_optimals_export.csv',
|
| 659 |
mime='text/csv',
|
| 660 |
+
key='export_filtered_pm_names_button'
|
| 661 |
)
|
| 662 |
|
| 663 |
if site_var == 'Draftkings':
|
|
|
|
| 715 |
export_file[col_idx] = export_file[col_idx].map(fd_id_map)
|
| 716 |
|
| 717 |
with st.container():
|
| 718 |
+
if st.button("Reset Optimals", key='reset_optimals_button'):
|
| 719 |
for key in st.session_state.keys():
|
| 720 |
del st.session_state[key]
|
| 721 |
if site_var == 'Draftkings':
|
|
|
|
| 723 |
elif site_var == 'Fanduel':
|
| 724 |
st.session_state.working_seed = fd_lineups.copy()
|
| 725 |
if 'data_export_display' in st.session_state:
|
| 726 |
+
st.dataframe(st.session_state.data_export_display.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), height=500, use_container_width = True, key='optimals_dataframe')
|
| 727 |
st.download_button(
|
| 728 |
label="Export display optimals (IDs)",
|
| 729 |
data=convert_df(export_file),
|
| 730 |
file_name='NFL_display_optimals.csv',
|
| 731 |
mime='text/csv',
|
| 732 |
+
key='export_display_optimals_ids_button'
|
| 733 |
)
|
| 734 |
st.download_button(
|
| 735 |
label="Export display optimals (Names)",
|
| 736 |
data=convert_df(name_export),
|
| 737 |
file_name='NFL_display_optimals.csv',
|
| 738 |
mime='text/csv',
|
| 739 |
+
key='export_display_optimals_names_button'
|
| 740 |
)
|
| 741 |
|
| 742 |
with st.container():
|
|
|
|
| 844 |
'Salary': '{:.2f}',
|
| 845 |
'Proj': '{:.2f}',
|
| 846 |
'Own': '{:.2f}'
|
| 847 |
+
}).background_gradient(cmap='RdYlGn', axis=0, subset=['Salary', 'Proj', 'Own']), use_container_width=True, key='optimal_stats_dataframe')
|
| 848 |
|
| 849 |
with st.container():
|
| 850 |
tab1, tab2 = st.tabs(["Display Frequency", "Seed Frame Frequency"])
|
|
|
|
| 882 |
|
| 883 |
# Display the table
|
| 884 |
st.write("Player Frequency Table:")
|
| 885 |
+
st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True, key='player_frequency_dataframe')
|
| 886 |
|
| 887 |
st.download_button(
|
| 888 |
label="Export player frequency",
|
| 889 |
data=convert_df_to_csv(summary_df),
|
| 890 |
file_name='NFL_player_frequency.csv',
|
| 891 |
mime='text/csv',
|
| 892 |
+
key='export_player_frequency_button'
|
| 893 |
)
|
| 894 |
with tab2:
|
| 895 |
if 'working_seed' in st.session_state:
|
|
|
|
| 924 |
|
| 925 |
# Display the table
|
| 926 |
st.write("Seed Frame Frequency Table:")
|
| 927 |
+
st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True, key='seed_frame_frequency_dataframe')
|
| 928 |
|
| 929 |
st.download_button(
|
| 930 |
label="Export seed frame frequency",
|
| 931 |
data=convert_df_to_csv(summary_df),
|
| 932 |
file_name='NFL_seed_frame_frequency.csv',
|
| 933 |
mime='text/csv',
|
| 934 |
+
key='export_seed_frame_frequency_button'
|
| 935 |
)
|
| 936 |
|
| 937 |
with tab4:
|
|
|
|
| 939 |
with col1:
|
| 940 |
st.header("Handbuilder")
|
| 941 |
with col2:
|
| 942 |
+
slate_var3 = st.selectbox("Slate Selection", options=['Main', 'Secondary', 'Auxiliary'], key='handbuilder_slate_selectbox')
|
| 943 |
if site_var == 'Draftkings':
|
| 944 |
if slate_var3 == 'Main':
|
| 945 |
handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Main Slate']
|
|
|
|
| 992 |
slot_counts = lineup['Slot'].value_counts() if not lineup.empty else {}
|
| 993 |
|
| 994 |
# --- PLAYER FILTERS ---
|
| 995 |
+
with st.expander("Player Filters", key='handbuilder_player_filters_expander'):
|
| 996 |
col1, col2 = st.columns(2)
|
| 997 |
with col1:
|
| 998 |
+
pos_select3 = st.multiselect("Select your position(s)", options=['QB', 'RB', 'WR', 'TE', 'UTIL', 'DST'], key='pos_select3_multiselect')
|
| 999 |
with col2:
|
| 1000 |
+
salary_var = st.number_input("Salary Max", min_value = 0, max_value = 20000, value = 20000, step = 100, key='handbuilder_salary_max_input')
|
| 1001 |
|
| 1002 |
# --- TEAM FILTER UI ---
|
| 1003 |
+
with st.expander("Team Filters", key='handbuilder_team_filters_expander'):
|
| 1004 |
all_teams = sorted(handbuild_roo['Team'].unique())
|
| 1005 |
st.markdown("**Toggle teams to include:**")
|
| 1006 |
team_cols = st.columns(len(all_teams) // 2 + 1)
|
|
|
|
| 1010 |
col = team_cols[idx % len(team_cols)]
|
| 1011 |
if f"handbuilder_team_{team}" not in st.session_state:
|
| 1012 |
st.session_state[f"handbuilder_team_{team}"] = False
|
| 1013 |
+
checked = col.toggle(team, value=st.session_state[f"handbuilder_team_{team}"], key=f"handbuilder_team_{team}_toggle")
|
| 1014 |
if checked:
|
| 1015 |
selected_teams.append(team)
|
| 1016 |
|
|
|
|
| 1032 |
player_select_df = player_select_df[player_select_df['Salary'] <= salary_var]
|
| 1033 |
|
| 1034 |
|
| 1035 |
+
with st.expander("Quick Fill Options", key='handbuilder_quick_fill_expander'):
|
| 1036 |
+
auto_team_var = st.selectbox("Auto Fill Team", options=all_teams, key='auto_team_selectbox')
|
| 1037 |
+
auto_size_var = st.selectbox("Auto Fill Size", options=[3, 4, 5], key='auto_size_selectbox')
|
| 1038 |
+
auto_range_var = st.selectbox("Auto Fill Options", options=['QB/WR', 'RB/WR/TE', 'QB/WR/TE/RB'], key='auto_range_selectbox')
|
| 1039 |
# --- QUICK FILL LOGIC ---
|
| 1040 |
+
if st.button("Quick Fill", key="quick_fill_button"):
|
| 1041 |
# 1. Get all eligible players from the selected team, not already in the lineup
|
| 1042 |
current_players = set(st.session_state['handbuilder_lineup']['Player'])
|
| 1043 |
team_players = player_select_df[
|
|
|
|
| 1220 |
lineup_display_df.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn', subset=['Median']).background_gradient(cmap='RdYlGn_r', subset=['Salary', 'Own']).format(precision=2),
|
| 1221 |
on_select="rerun",
|
| 1222 |
selection_mode=["single-row"],
|
| 1223 |
+
key="lineup_remove_dataframe",
|
| 1224 |
height=445,
|
| 1225 |
hide_index=True
|
| 1226 |
)
|
|
|
|
| 1299 |
# Optionally, add a button to clear the lineup
|
| 1300 |
clear_col, save_col, export_col, clear_saved_col, blank_col = st.columns([2, 2, 2, 2, 12])
|
| 1301 |
with clear_col:
|
| 1302 |
+
if st.button("Clear Lineup", key='clear_lineup_button'):
|
| 1303 |
st.session_state['handbuilder_lineup'] = pd.DataFrame(columns=['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot'])
|
| 1304 |
st.rerun()
|
| 1305 |
with save_col:
|
| 1306 |
+
if st.button("Save Lineup", key='save_lineup_button'):
|
| 1307 |
if 'saved_lineups' in st.session_state:
|
| 1308 |
st.session_state['saved_lineups'].append(lineup_display_df['Player'].tolist())
|
| 1309 |
print(st.session_state['saved_lineups'])
|
|
|
|
| 1320 |
data=convert_hb_df(saved_lineups_array, dk_hb_columns if site_var == 'Draftkings' else fd_hb_columns),
|
| 1321 |
file_name='handbuilds_export.csv',
|
| 1322 |
mime='text/csv',
|
| 1323 |
+
key='export_handbuilds_button'
|
| 1324 |
)
|
| 1325 |
else:
|
| 1326 |
st.write("No saved lineups to export")
|
|
|
|
| 1331 |
st.write("No saved lineups")
|
| 1332 |
|
| 1333 |
with clear_saved_col:
|
| 1334 |
+
if st.button("Clear Saved", key='clear_saved_button'):
|
| 1335 |
if 'saved_lineups' in st.session_state:
|
| 1336 |
del st.session_state['saved_lineups']
|
| 1337 |
st.rerun()
|