diff --git "a/src/streamlit_app.py" "b/src/streamlit_app.py" --- "a/src/streamlit_app.py" +++ "b/src/streamlit_app.py" @@ -70,6 +70,28 @@ st.markdown(""" """, unsafe_allow_html=True) +@st.cache_resource(ttl=60) +def init_handbuilder_data(site_var): + """Load only the data needed for handbuilder""" + if site_var == 'Draftkings': + collection = db["DK_NFL_ROO"] + cursor = collection.find() + raw_display = pd.DataFrame(list(cursor)) + raw_display = raw_display.rename(columns={'player_ID': 'player_id'}) + raw_display = raw_display[['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', 'version', 'slate', 'timestamp', 'player_id', 'site']] + load_display = raw_display[raw_display['Position'] != 'K'] + return load_display.dropna(subset=['Median']) + else: + collection = db["FD_NFL_ROO"] + cursor = collection.find() + raw_display = pd.DataFrame(list(cursor)) + raw_display = raw_display.rename(columns={'player_ID': 'player_id'}) + raw_display = raw_display[['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', 'version', 'slate', 'timestamp', 'player_id', 'site']] + load_display = raw_display[raw_display['Position'] != 'K'] + return load_display.dropna(subset=['Median']) + @st.cache_resource(ttl=60) def init_baselines(): @@ -305,14 +327,6 @@ def convert_hb_df(array, column_names): array = pd.DataFrame(array, columns=column_names) return array.to_csv().encode('utf-8') -# Add this function at the top after imports -def get_unique_key(prefix, base_key): - """Generate a unique key for Streamlit elements""" - if f"{prefix}_{base_key}" not in st.session_state: - st.session_state[f"{prefix}_{base_key}"] = 0 - st.session_state[f"{prefix}_{base_key}"] += 1 - return f"{prefix}_{base_key}_{st.session_state[f'{prefix}_{base_key}']}" - app_load_reset_column, app_view_site_column = st.columns([1, 9]) with app_load_reset_column: if st.button("Load/Reset Data", key='reset_data_button'): @@ -334,660 +348,77 @@ player_stats, dk_stacks_raw, fd_stacks_raw, dk_roo_raw, fd_roo_raw, dk_sd_roo_ra t_stamp = f"Last Update: " + str(dk_roo_raw['timestamp'][0]) + f" CST" -stacks_tab, player_tab, optimals_tab, handbuilder_tab = st.tabs(["Stacks ROO", "Player ROO", "Optimals", "Handbuilder"]) +handbuilder_tab, stacks_tab, player_tab, optimals_tab = st.tabs(["Handbuilder", "Stacks ROO", "Player ROO", "Optimals"]) -with stacks_tab: - with st.expander("Info and Filters"): - st.info(t_stamp) - with st.container(): - slate_var1 = st.radio("Which data are you loading?", ('Main Slate', 'Secondary Slate', 'Late Slate', 'Thurs-Mon Slate'), key='slate_var1_radio') - 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') - if site_var == 'Draftkings': - raw_baselines = dk_stacks_raw[dk_stacks_raw['slate'] == str(slate_var1)] - raw_baselines = raw_baselines[raw_baselines['version'] == 'overall'] - raw_baselines = raw_baselines.iloc[:,:-2] - elif site_var == 'Fanduel': - raw_baselines = fd_stacks_raw[fd_stacks_raw['slate'] == str(slate_var1)] - raw_baselines = raw_baselines[raw_baselines['version'] == 'overall'] - raw_baselines = raw_baselines.iloc[:,:-2] - if split_var1 == 'Specific Games': - team_var1 = st.multiselect('Which teams would you like to include in the ROO?', options = raw_baselines['Team'].unique(), key='team_var1_multiselect') - elif split_var1 == 'Full Slate Run': - team_var1 = raw_baselines.Team.values.tolist() - - final_stacks = raw_baselines[raw_baselines['Team'].isin(team_var1)] - if view_var == 'Simple': - final_stacks = final_stacks[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Salary', 'Median', '60+%', '4x%']] - elif view_var == 'Advanced': - final_stacks = final_stacks[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Total', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', - 'Top_10_finish', '60+%', '2x%', '3x%', '4x%', 'Own', 'LevX']] - with st.container(): - 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') - st.download_button( - label="Export Tables", - data=convert_df_to_csv(final_stacks), - file_name='NFL_stacks_export.csv', - mime='text/csv', - key='stacks_export_button' - ) +# Only load heavy data if not on handbuilder tab +if st.session_state['current_tab'] != 'Handbuilder': + 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() + t_stamp = f"Last Update: " + str(dk_roo_raw['timestamp'][0]) + f" CST" +else: + # Load only handbuilder data + handbuilder_data = init_handbuilder_data(site_var) + t_stamp = f"Last Update: " + str(handbuilder_data['timestamp'][0]) + f" CST" -with player_tab: - with st.expander("Info and Filters"): - st.info(t_stamp) - slate_var2 = st.radio("Which data are you loading?", ('Main Slate', 'Secondary Slate', 'Late Slate', 'Thurs-Mon Slate'), key='slate_var2_radio') +with handbuilder_tab: + st.session_state['current_tab'] = 'Handbuilder' + + # Use the lightweight handbuilder data + if site_var == 'Draftkings': + handbuild_roo = handbuilder_data[handbuilder_data['site'] == 'dk'] + else: + handbuild_roo = handbuilder_data[handbuilder_data['site'] == 'fd'] + + handbuilder_header_column, handbuilder_slate_column = st.columns(2) + with handbuilder_header_column: + st.header("Handbuilder") + with handbuilder_slate_column: + slate_var3 = st.selectbox("Slate Selection", options=['Main', 'Secondary', 'Auxiliary'], key='handbuilder_slate_selectbox') if site_var == 'Draftkings': - raw_baselines = dk_roo_raw[dk_roo_raw['slate'] == str(slate_var2)] - - raw_baselines = raw_baselines.iloc[:,:-2] + if slate_var3 == 'Main': + handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Main Slate'] + elif slate_var3 == 'Secondary': + handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Secondary Slate'] + elif slate_var3 == 'Auxiliary': + handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Late Slate'] elif site_var == 'Fanduel': - raw_baselines = fd_roo_raw[fd_roo_raw['slate'] == str(slate_var2)] - raw_baselines = raw_baselines.iloc[:,:-2] - 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') - if split_var2 == 'Specific Games': - team_var2 = st.multiselect('Which teams would you like to include in the ROO?', options = raw_baselines['Team'].unique(), key='team_var2_multiselect') - elif split_var2 == 'Full Slate Run': - team_var2 = raw_baselines.Team.values.tolist() - pos_split2 = st.selectbox('What Position table would you like to view?', options = ['Overall', 'QB', 'RB', 'WR', 'TE'], key='pos_split2_selectbox') - pos_combos2 = st.multiselect('If Overall, specific positions?', options = ['QB', 'RB', 'WR', 'TE', 'DST'], default = ['QB', 'RB', 'WR', 'TE', 'DST'], key='pos_combos2_multiselect') - sal_var2 = st.slider("Is there a certain price range you want to view?", 2000, 15000, (2000, 15000), key='sal_var2_slider') + if slate_var3 == 'Main': + handbuild_roo = fd_roo_raw[fd_roo_raw['slate'] == 'Main Slate'] + elif slate_var3 == 'Secondary': + handbuild_roo = fd_roo_raw[fd_roo_raw['slate'] == 'Secondary Slate'] + elif slate_var3 == 'Auxiliary': + handbuild_roo = fd_roo_raw[fd_roo_raw['slate'] == 'Late Slate'] - if pos_split2 == 'Overall': - raw_baselines = raw_baselines[raw_baselines['version'] == 'overall'] - elif pos_split2 == 'QB': - if site_var == 'Draftkings': - raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_qbs'] - elif site_var == 'Fanduel': - raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_qbs'] - elif pos_split2 == 'RB': - if site_var == 'Draftkings': - raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_rbs'] - elif site_var == 'Fanduel': - raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_rbs'] - elif pos_split2 == 'WR': - if site_var == 'Draftkings': - raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_wrs'] - elif site_var == 'Fanduel': - raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_wrs'] - elif pos_split2 == 'TE': - if site_var == 'Draftkings': - raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_tes'] - elif site_var == 'Fanduel': - raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_tes'] - raw_baselines = raw_baselines[raw_baselines['Position'].str.contains('|'.join(pos_combos2))] - final_Proj = raw_baselines[raw_baselines['Team'].isin(team_var2)] - final_Proj = final_Proj[final_Proj['Salary'] >= sal_var2[0]] - final_Proj = final_Proj[final_Proj['Salary'] <= sal_var2[1]] - - if view_var == 'Simple': - final_Proj = final_Proj[['Player', 'Position', 'Team', 'Salary', 'Median', 'Top_5_finish', '4x%']] - disp_proj = final_Proj.set_index('Player') - elif view_var == 'Advanced': - 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']] - disp_proj = final_Proj.set_index('Player') - with st.container(): - 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') - st.download_button( - label="Export Tables", - data=convert_df_to_csv(final_Proj), - file_name='NFL_ROO_export.csv', - mime='text/csv', - key='player_export_button' - ) + # --- POSITION LIMITS --- + if site_var == 'Draftkings': + position_limits = { + 'QB': 1, + 'RB': 2, + 'WR': 3, + 'TE': 1, + 'UTIL': 1, + 'DST': 1, + # Add more as needed + } + max_salary = 50000 + max_players = 9 + else: + position_limits = { + 'QB': 1, + 'RB': 2, + 'WR': 3, + 'TE': 1, + 'UTIL': 1, + 'DST': 1, + # Add more as needed + } + max_salary = 60000 + max_players = 9 -with optimals_tab: - st.header("Optimals") - with st.expander("Info and Filters"): - 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.") - optimals_site_col, optimals_macro_col, optimals_salary_col, optimals_stacks_col = st.columns(4) - with optimals_site_col: - slate_type_var3 = st.radio("Which slate type are you loading?", ('Regular', 'Showdown'), key='slate_type_var3_radio') - if slate_type_var3 == 'Regular': - if site_var == 'Draftkings': - raw_baselines = dk_roo_raw - elif site_var == 'Fanduel': - raw_baselines = fd_roo_raw - elif slate_type_var3 == 'Showdown': - if site_var == 'Draftkings': - raw_baselines = dk_sd_roo_raw - elif site_var == 'Fanduel': - raw_baselines = fd_sd_roo_raw - slate_var3 = st.radio("Which slate data are you loading?", ('Main', 'Secondary', 'Auxiliary'), key='slate_var3_radio') - if slate_type_var3 == 'Regular': - if site_var == 'Draftkings': - dk_lineups = init_DK_lineups(slate_type_var3, slate_var3) - elif site_var == 'Fanduel': - fd_lineups = init_FD_lineups(slate_type_var3, slate_var3) - elif slate_type_var3 == 'Showdown': - if site_var == 'Draftkings': - dk_lineups = init_DK_lineups(slate_type_var3, slate_var3) - elif site_var == 'Fanduel': - fd_lineups = init_FD_lineups(slate_type_var3, slate_var3) - with optimals_macro_col: - 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') - player_var1 = st.radio("Do you want a frame with specific Players?", ('Full Slate', 'Specific Players'), key='player_var1_radio') - if player_var1 == 'Specific Players': - player_var2 = st.multiselect('Which players do you want?', options = raw_baselines['Player'].unique(), key='player_var2_multiselect') - elif player_var1 == 'Full Slate': - player_var2 = raw_baselines.Player.values.tolist() - with optimals_salary_col: - if site_var == 'Draftkings': - salary_min_var = st.number_input("Minimum salary used", min_value = 0, max_value = 50000, value = 49000, step = 100, key = 'salary_min_var_dk') - salary_max_var = st.number_input("Maximum salary used", min_value = 0, max_value = 50000, value = 50000, step = 100, key = 'salary_max_var_dk') - elif site_var == 'Fanduel': - salary_min_var = st.number_input("Minimum salary used", min_value = 0, max_value = 60000, value = 59000, step = 100, key = 'salary_min_var_fd') - salary_max_var = st.number_input("Maximum salary used", min_value = 0, max_value = 60000, value = 60000, step = 100, key = 'salary_max_var_fd') - with optimals_stacks_col: - if site_var == 'Draftkings': - min_stacks_var = st.number_input("Minimum stacks used", min_value = 0, max_value = 5, value = 1, step = 1, key = 'min_stacks_var_dk') - max_stacks_var = st.number_input("Maximum stacks used", min_value = 0, max_value = 5, value = 5, step = 1, key = 'max_stacks_var_dk') - elif site_var == 'Fanduel': - min_stacks_var = st.number_input("Minimum stacks used", min_value = 0, max_value = 4, value = 1, step = 1, key = 'min_stacks_var_fd') - max_stacks_var = st.number_input("Maximum stacks used", min_value = 0, max_value = 4, value = 4, step = 1, key = 'max_stacks_var_fd') - - - if site_var == 'Draftkings': - raw_baselines = dk_roo_raw - if slate_type_var3 == 'Regular': - ROO_slice = raw_baselines - player_salaries = dict(zip(ROO_slice['Player'], ROO_slice['Salary'])) - column_names = dk_columns - elif slate_type_var3 == 'Showdown': - player_salaries = dict(zip(raw_baselines['Player'], raw_baselines['Salary'])) - column_names = dk_sd_columns - - - elif site_var == 'Fanduel': - raw_baselines = fd_roo_raw - if slate_type_var3 == 'Regular': - ROO_slice = raw_baselines - player_salaries = dict(zip(ROO_slice['Player'], ROO_slice['Salary'])) - column_names = fd_columns - elif slate_type_var3 == 'Showdown': - player_salaries = dict(zip(raw_baselines['Player'], raw_baselines['Salary'])) - column_names = fd_sd_columns - - reg_dl_col, filtered_dl_col, blank_dl_col = st.columns([2, 2, 6]) - with reg_dl_col: - if st.button("Prepare full data export", key='data_export_button'): - name_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names) - data_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names) - if site_var == 'Draftkings': - if slate_type_var3 == 'Regular': - map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] - for col_idx in map_columns: - data_export[col_idx] = data_export[col_idx].map(dk_id_map) - elif slate_type_var3 == 'Showdown': - map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] - for col_idx in map_columns: - data_export[col_idx] = data_export[col_idx].map(dk_sd_id_map) - elif site_var == 'Fanduel': - if slate_type_var3 == 'Regular': - map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] - for col_idx in map_columns: - data_export[col_idx] = data_export[col_idx].map(fd_id_map) - elif slate_type_var3 == 'Showdown': - map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] - for col_idx in map_columns: - data_export[col_idx] = data_export[col_idx].map(fd_sd_id_map) - reg_opt_col, pm_opt_col = st.columns(2) - with reg_opt_col: - st.download_button( - label="Export optimals set (IDs)", - data=convert_df(data_export), - file_name='NFL_optimals_export.csv', - mime='text/csv', - key='export_optimals_ids_button' - ) - st.download_button( - label="Export optimals set (Names)", - data=convert_df(name_export), - file_name='NFL_optimals_export.csv', - mime='text/csv', - key='export_optimals_names_button' - ) - with pm_opt_col: - if site_var == 'Draftkings': - if slate_type_var3 == 'Regular': - data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif slate_type_var3 == 'Showdown': - data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif site_var == 'Fanduel': - if slate_type_var3 == 'Regular': - data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif slate_type_var3 == 'Showdown': - data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - st.download_button( - label="Portfolio Manager Export (IDs)", - data=convert_pm_df(data_export), - file_name='NFL_optimals_export.csv', - mime='text/csv', - key='export_pm_ids_button' - ) - - if site_var == 'Draftkings': - if slate_type_var3 == 'Regular': - name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif slate_type_var3 == 'Showdown': - name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif site_var == 'Fanduel': - if slate_type_var3 == 'Regular': - name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif slate_type_var3 == 'Showdown': - name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - st.download_button( - label="Portfolio Manager Export (Names)", - data=convert_pm_df(name_export), - file_name='NFL_optimals_export.csv', - mime='text/csv', - key='export_pm_names_button' - ) - with filtered_dl_col: - if st.button("Prepare full data export (Filtered)", key='data_export_filtered_button'): - name_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names) - data_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names) - if site_var == 'Draftkings': - if slate_type_var3 == 'Regular': - map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] - elif slate_type_var3 == 'Showdown': - map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] - for col_idx in map_columns: - data_export[col_idx] = data_export[col_idx].map(dk_id_map) - elif site_var == 'Fanduel': - if slate_type_var3 == 'Regular': - map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] - elif slate_type_var3 == 'Showdown': - map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] - for col_idx in map_columns: - data_export[col_idx] = data_export[col_idx].map(fd_id_map) - data_export = data_export[data_export['salary'] >= salary_min_var] - data_export = data_export[data_export['salary'] <= salary_max_var] - data_export = data_export[data_export['Team_count'] >= min_stacks_var] - data_export = data_export[data_export['Team_count'] <= max_stacks_var] - - name_export = name_export[name_export['salary'] >= salary_min_var] - name_export = name_export[name_export['salary'] <= salary_max_var] - name_export = name_export[name_export['Team_count'] >= min_stacks_var] - name_export = name_export[name_export['Team_count'] <= max_stacks_var] - - reg_opt_col, pm_opt_col = st.columns(2) - with reg_opt_col: - st.download_button( - label="Export optimals set (IDs)", - data=convert_df(data_export), - file_name='NFL_optimals_export.csv', - mime='text/csv', - key='export_filtered_optimals_ids_button' - ) - st.download_button( - label="Export optimals set (Names)", - data=convert_df(name_export), - file_name='NFL_optimals_export.csv', - mime='text/csv', - key='export_filtered_optimals_names_button' - ) - with pm_opt_col: - if site_var == 'Draftkings': - if slate_type_var3 == 'Regular': - data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif slate_type_var3 == 'Showdown': - data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif site_var == 'Fanduel': - if slate_type_var3 == 'Regular': - data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif slate_type_var3 == 'Showdown': - data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - st.download_button( - label="Portfolio Manager Export (IDs)", - data=convert_pm_df(data_export), - file_name='NFL_optimals_export.csv', - mime='text/csv', - key='export_filtered_pm_ids_button' - ) - - if site_var == 'Draftkings': - if slate_type_var3 == 'Regular': - name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif slate_type_var3 == 'Showdown': - name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif site_var == 'Fanduel': - if slate_type_var3 == 'Regular': - name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - elif slate_type_var3 == 'Showdown': - name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) - st.download_button( - label="Portfolio Manager Export (Names)", - data=convert_pm_df(name_export), - file_name='NFL_optimals_export.csv', - mime='text/csv', - key='export_filtered_pm_names_button' - ) - - if site_var == 'Draftkings': - if 'working_seed' in st.session_state: - st.session_state.working_seed = st.session_state.working_seed - if player_var1 == 'Specific Players': - st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)] - elif player_var1 == 'Full Slate': - st.session_state.working_seed = dk_lineups.copy() - st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names) - elif 'working_seed' not in st.session_state: - st.session_state.working_seed = dk_lineups.copy() - st.session_state.working_seed = st.session_state.working_seed - if player_var1 == 'Specific Players': - st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)] - elif player_var1 == 'Full Slate': - st.session_state.working_seed = dk_lineups.copy() - st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names) - - elif site_var == 'Fanduel': - if 'working_seed' in st.session_state: - st.session_state.working_seed = st.session_state.working_seed - if player_var1 == 'Specific Players': - st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)] - elif player_var1 == 'Full Slate': - st.session_state.working_seed = fd_lineups.copy() - st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names) - elif 'working_seed' not in st.session_state: - st.session_state.working_seed = fd_lineups.copy() - st.session_state.working_seed = st.session_state.working_seed - if player_var1 == 'Specific Players': - st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)] - elif player_var1 == 'Full Slate': - st.session_state.working_seed = fd_lineups.copy() - st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names) - st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['salary'] >= salary_min_var] - st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['salary'] <= salary_max_var] - st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['Team_count'] >= min_stacks_var] - st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['Team_count'] <= max_stacks_var] - export_file = st.session_state.data_export_display.copy() - name_export = st.session_state.data_export_display.copy() - if site_var == 'Draftkings': - if slate_type_var3 == 'Regular': - map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] - elif slate_type_var3 == 'Showdown': - map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] - for col_idx in map_columns: - export_file[col_idx] = export_file[col_idx].map(dk_id_map) - elif site_var == 'Fanduel': - if slate_type_var3 == 'Regular': - map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] - elif slate_type_var3 == 'Showdown': - map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] - for col_idx in map_columns: - export_file[col_idx] = export_file[col_idx].map(fd_id_map) - - with st.container(): - if st.button("Reset Optimals", key='reset_optimals_button'): - for key in st.session_state.keys(): - del st.session_state[key] - if site_var == 'Draftkings': - st.session_state.working_seed = dk_lineups.copy() - elif site_var == 'Fanduel': - st.session_state.working_seed = fd_lineups.copy() - if 'data_export_display' in st.session_state: - 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') - st.download_button( - label="Export display optimals (IDs)", - data=convert_df(export_file), - file_name='NFL_display_optimals.csv', - mime='text/csv', - key='export_display_optimals_ids_button' - ) - st.download_button( - label="Export display optimals (Names)", - data=convert_df(name_export), - file_name='NFL_display_optimals.csv', - mime='text/csv', - key='export_display_optimals_names_button' - ) - - with st.container(): - if slate_type_var3 == 'Regular': - if 'working_seed' in st.session_state: - # Create a new dataframe with summary statistics - if site_var == 'Draftkings': - summary_df = pd.DataFrame({ - 'Metric': ['Min', 'Average', 'Max', 'STDdev'], - 'Salary': [ - np.min(st.session_state.working_seed[:,9]), - np.mean(st.session_state.working_seed[:,9]), - np.max(st.session_state.working_seed[:,9]), - np.std(st.session_state.working_seed[:,9]) - ], - 'Proj': [ - np.min(st.session_state.working_seed[:,10]), - np.mean(st.session_state.working_seed[:,10]), - np.max(st.session_state.working_seed[:,10]), - np.std(st.session_state.working_seed[:,10]) - ], - 'Own': [ - np.min(st.session_state.working_seed[:,15]), - np.mean(st.session_state.working_seed[:,15]), - np.max(st.session_state.working_seed[:,15]), - np.std(st.session_state.working_seed[:,15]) - ] - }) - elif site_var == 'Fanduel': - summary_df = pd.DataFrame({ - 'Metric': ['Min', 'Average', 'Max', 'STDdev'], - 'Salary': [ - np.min(st.session_state.working_seed[:,9]), - np.mean(st.session_state.working_seed[:,9]), - np.max(st.session_state.working_seed[:,9]), - np.std(st.session_state.working_seed[:,9]) - ], - 'Proj': [ - np.min(st.session_state.working_seed[:,10]), - np.mean(st.session_state.working_seed[:,10]), - np.max(st.session_state.working_seed[:,10]), - np.std(st.session_state.working_seed[:,10]) - ], - 'Own': [ - np.min(st.session_state.working_seed[:,15]), - np.mean(st.session_state.working_seed[:,15]), - np.max(st.session_state.working_seed[:,15]), - np.std(st.session_state.working_seed[:,15]) - ] - }) - elif slate_type_var3 == 'Showdown': - if 'working_seed' in st.session_state: - # Create a new dataframe with summary statistics - if site_var == 'Draftkings': - summary_df = pd.DataFrame({ - 'Metric': ['Min', 'Average', 'Max', 'STDdev'], - 'Salary': [ - np.min(st.session_state.working_seed[:,6]), - np.mean(st.session_state.working_seed[:,6]), - np.max(st.session_state.working_seed[:,6]), - np.std(st.session_state.working_seed[:,6]) - ], - 'Proj': [ - np.min(st.session_state.working_seed[:,7]), - np.mean(st.session_state.working_seed[:,7]), - np.max(st.session_state.working_seed[:,7]), - np.std(st.session_state.working_seed[:,7]) - ], - 'Own': [ - np.min(st.session_state.working_seed[:,12]), - np.mean(st.session_state.working_seed[:,12]), - np.max(st.session_state.working_seed[:,12]), - np.std(st.session_state.working_seed[:,12]) - ] - }) - elif site_var == 'Fanduel': - summary_df = pd.DataFrame({ - 'Metric': ['Min', 'Average', 'Max', 'STDdev'], - 'Salary': [ - np.min(st.session_state.working_seed[:,6]), - np.mean(st.session_state.working_seed[:,6]), - np.max(st.session_state.working_seed[:,6]), - np.std(st.session_state.working_seed[:,6]) - ], - 'Proj': [ - np.min(st.session_state.working_seed[:,7]), - np.mean(st.session_state.working_seed[:,7]), - np.max(st.session_state.working_seed[:,7]), - np.std(st.session_state.working_seed[:,7]) - ], - 'Own': [ - np.min(st.session_state.working_seed[:,12]), - np.mean(st.session_state.working_seed[:,12]), - np.max(st.session_state.working_seed[:,12]), - np.std(st.session_state.working_seed[:,12]) - ] - }) - - # Set the index of the summary dataframe as the "Metric" column - summary_df = summary_df.set_index('Metric') - - # Display the summary dataframe - st.subheader("Optimal Statistics") - st.dataframe(summary_df.style.format({ - 'Salary': '{:.2f}', - 'Proj': '{:.2f}', - 'Own': '{:.2f}' - }).background_gradient(cmap='RdYlGn', axis=0, subset=['Salary', 'Proj', 'Own']), use_container_width=True, key='optimal_stats_dataframe') - - with st.container(): - display_freq_tab, seed_frame_freq_tab = st.tabs(["Display Frequency", "Seed Frame Frequency"]) - with display_freq_tab: - if 'data_export_display' in st.session_state: - if site_var == 'Draftkings': - if slate_type_var3 == 'Regular': - player_columns = st.session_state.data_export_display.iloc[:, :9] - elif slate_type_var3 == 'Showdown': - player_columns = st.session_state.data_export_display.iloc[:, :6] - elif site_var == 'Fanduel': - if slate_type_var3 == 'Regular': - player_columns = st.session_state.data_export_display.iloc[:, :9] - elif slate_type_var3 == 'Showdown': - player_columns = st.session_state.data_export_display.iloc[:, :6] - - # Flatten the DataFrame and count unique values - value_counts = player_columns.values.flatten().tolist() - value_counts = pd.Series(value_counts).value_counts() - - percentages = (value_counts / lineup_num_var * 100).round(2) - - # Create a DataFrame with the results - summary_df = pd.DataFrame({ - 'Player': value_counts.index, - 'Frequency': value_counts.values, - 'Percentage': percentages.values - }) - - # Sort by frequency in descending order - summary_df['Salary'] = summary_df['Player'].map(player_salaries) - summary_df = summary_df[['Player', 'Salary', 'Frequency', 'Percentage']] - summary_df = summary_df.sort_values('Frequency', ascending=False) - summary_df = summary_df.set_index('Player') - - # Display the table - st.write("Player Frequency Table:") - st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True, key='player_frequency_dataframe') - - st.download_button( - label="Export player frequency", - data=convert_df_to_csv(summary_df), - file_name='NFL_player_frequency.csv', - mime='text/csv', - key='export_player_frequency_button' - ) - with seed_frame_freq_tab: - if 'working_seed' in st.session_state: - if site_var == 'Draftkings': - if slate_type_var3 == 'Regular': - player_columns = st.session_state.working_seed[:, :9] - elif slate_type_var3 == 'Showdown': - player_columns = st.session_state.working_seed[:, :6] - elif site_var == 'Fanduel': - if slate_type_var3 == 'Regular': - player_columns = st.session_state.working_seed[:, :9] - elif slate_type_var3 == 'Showdown': - player_columns = st.session_state.working_seed[:, :6] - - # Flatten the DataFrame and count unique values - value_counts = player_columns.flatten().tolist() - value_counts = pd.Series(value_counts).value_counts() - - percentages = (value_counts / len(st.session_state.working_seed) * 100).round(2) - # Create a DataFrame with the results - summary_df = pd.DataFrame({ - 'Player': value_counts.index, - 'Frequency': value_counts.values, - 'Percentage': percentages.values - }) - - # Sort by frequency in descending order - summary_df['Salary'] = summary_df['Player'].map(player_salaries) - summary_df = summary_df[['Player', 'Salary', 'Frequency', 'Percentage']] - summary_df = summary_df.sort_values('Frequency', ascending=False) - summary_df = summary_df.set_index('Player') - - # Display the table - st.write("Seed Frame Frequency Table:") - st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True, key='seed_frame_frequency_dataframe') - - st.download_button( - label="Export seed frame frequency", - data=convert_df_to_csv(summary_df), - file_name='NFL_seed_frame_frequency.csv', - mime='text/csv', - key='export_seed_frame_frequency_button' - ) - -with handbuilder_tab: - handbuilder_header_column, handbuilder_slate_column = st.columns(2) - with handbuilder_header_column: - st.header("Handbuilder") - with handbuilder_slate_column: - slate_var3 = st.selectbox("Slate Selection", options=['Main', 'Secondary', 'Auxiliary'], key='handbuilder_slate_selectbox') - if site_var == 'Draftkings': - if slate_var3 == 'Main': - handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Main Slate'] - elif slate_var3 == 'Secondary': - handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Secondary Slate'] - elif slate_var3 == 'Auxiliary': - handbuild_roo = dk_roo_raw[dk_roo_raw['slate'] == 'Late Slate'] - elif site_var == 'Fanduel': - if slate_var3 == 'Main': - handbuild_roo = fd_roo_raw[fd_roo_raw['slate'] == 'Main Slate'] - elif slate_var3 == 'Secondary': - handbuild_roo = fd_roo_raw[fd_roo_raw['slate'] == 'Secondary Slate'] - elif slate_var3 == 'Auxiliary': - handbuild_roo = fd_roo_raw[fd_roo_raw['slate'] == 'Late Slate'] - - # --- POSITION LIMITS --- - if site_var == 'Draftkings': - position_limits = { - 'QB': 1, - 'RB': 2, - 'WR': 3, - 'TE': 1, - 'UTIL': 1, - 'DST': 1, - # Add more as needed - } - max_salary = 50000 - max_players = 9 - else: - position_limits = { - 'QB': 1, - 'RB': 2, - 'WR': 3, - 'TE': 1, - 'UTIL': 1, - 'DST': 1, - # Add more as needed - } - max_salary = 60000 - max_players = 9 - - # --- LINEUP STATE --- - if 'handbuilder_lineup' not in st.session_state: - st.session_state['handbuilder_lineup'] = pd.DataFrame(columns=['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']) - if 'handbuilder_select_key' not in st.session_state: - st.session_state['handbuilder_select_key'] = 0 + # --- LINEUP STATE --- + if 'handbuilder_lineup' not in st.session_state: + st.session_state['handbuilder_lineup'] = pd.DataFrame(columns=['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own']) + if 'handbuilder_select_key' not in st.session_state: + st.session_state['handbuilder_select_key'] = 0 # Count positions in the current lineup lineup = st.session_state['handbuilder_lineup'] @@ -1129,211 +560,811 @@ with handbuilder_tab: return True return False - # player_select_df = player_select_df[player_select_df.apply(is_player_eligible, axis=1)] - print(player_select_df.head(10)) + # player_select_df = player_select_df[player_select_df.apply(is_player_eligible, axis=1)] + print(player_select_df.head(10)) + + handbuilder_lineup_build_column, handbuilder_player_select_column = st.columns([1, 2]) + with handbuilder_player_select_column: + st.subheader("Player Select") + event = st.dataframe( + player_select_df.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').background_gradient(cmap='RdYlGn_r', subset=['Salary', 'Own']).format(precision=2), + on_select="rerun", + selection_mode=["single-row"], + key=f"handbuilder_select_{st.session_state['handbuilder_select_key']}", + height=500, + hide_index=True + ) + # If a row is selected, add that player to the lineup and reset selection + if event and "rows" in event.selection and len(event.selection["rows"]) > 0: + idx = event.selection["rows"][0] + player_row = player_select_df.iloc[[idx]] + eligible_positions = re.split(r'[/, ]+', player_row['Position'].iloc[0]) + # Find the first eligible slot that is not full + slot_to_fill = None + + for slot in ['QB', 'RB', 'WR', 'TE', 'UTIL', 'DST']: + if slot_counts.get(slot, 0) < position_limits.get(slot, 0): + if slot == 'UTIL': + if 'DST' not in eligible_positions and 'QB' not in eligible_positions: + slot_to_fill = slot + break + elif slot in eligible_positions: + slot_to_fill = slot + break + + if slot_to_fill is not None: + # Avoid duplicates + if not player_row['Player'].iloc[0] in st.session_state['handbuilder_lineup']['Player'].values: + # Add the slot info + player_row = player_row.assign(Slot=slot_to_fill) + st.session_state['handbuilder_lineup'] = pd.concat( + [st.session_state['handbuilder_lineup'], player_row[['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot']]], + ignore_index=True + ) + st.session_state['handbuilder_select_key'] += 1 + st.rerun() + + + with handbuilder_lineup_build_column: + st.subheader("Lineup Build") + + # --- EXPLICIT LINEUP ORDER --- + if site_var == 'Draftkings': + lineup_slots = ['QB', 'RB', 'RB', 'WR', 'WR', 'WR', 'TE', 'UTIL', 'DST'] + else: + lineup_slots = ['QB', 'RB', 'RB', 'WR', 'WR', 'WR', 'TE', 'UTIL', 'DST'] + display_columns = ['Slot', 'Player', 'Position', 'Team', 'Salary', 'Median', 'Own'] + + filled_lineup = st.session_state['handbuilder_lineup'] + display_rows = [] + used_indices = set() + if not filled_lineup.empty: + for slot in lineup_slots: + match = filled_lineup[(filled_lineup['Slot'] == slot) & (~filled_lineup.index.isin(used_indices))] + if not match.empty: + row = match.iloc[0] + used_indices.add(match.index[0]) + display_rows.append({ + 'Slot': slot, + 'Player': row['Player'], + 'Position': row['Position'], + 'Team': row['Team'], + 'Salary': row['Salary'], + 'Median': row['Median'], + '2x%': row['2x%'], + 'Own': row['Own'] + }) + else: + display_rows.append({ + 'Slot': slot, + 'Player': '', + 'Position': '', + 'Team': '', + 'Salary': np.nan, + 'Median': np.nan, + '2x%': np.nan, + 'Own': np.nan + }) + + lineup_display_df = pd.DataFrame(display_rows, columns=display_columns) + + # Show the lineup table with single-row selection for removal + event_remove = st.dataframe( + 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), + on_select="rerun", + selection_mode=["single-row"], + key="lineup_remove_dataframe", + height=445, + hide_index=True + ) + + # If a row is selected and not blank, remove that player from the lineup + if event_remove and "rows" in event_remove.selection and len(event_remove.selection["rows"]) > 0: + idx = event_remove.selection["rows"][0] + player_to_remove = lineup_display_df.iloc[idx]['Player'] + slot_to_remove = lineup_display_df.iloc[idx]['Slot'] + if player_to_remove: # Only remove if not blank + st.session_state['handbuilder_lineup'] = filled_lineup[ + ~((filled_lineup['Player'] == player_to_remove) & (filled_lineup['Slot'] == slot_to_remove)) + ] + st.rerun() + + # --- SUMMARY ROW --- + if not filled_lineup.empty: + total_salary = filled_lineup['Salary'].sum() + total_median = filled_lineup['Median'].sum() + avg_2x = filled_lineup['2x%'].mean() + total_own = filled_lineup['Own'].sum() + most_common_team = filled_lineup['Team'].mode()[0] if not filled_lineup['Team'].mode().empty else "" + + summary_row = pd.DataFrame({ + 'Slot': [''], + 'Player': ['TOTAL'], + 'Position': [''], + 'Team': [most_common_team], + 'Salary': [total_salary], + 'Median': [total_median], + '2x%': [avg_2x], + 'Own': [total_own] + }) + summary_row = summary_row[['Salary', 'Median', 'Own']].head(max_players) + + handbuilder_lineup_build_salary_column, handbuilder_lineup_build_median_column = st.columns([2, 3]) + + with handbuilder_lineup_build_salary_column: + if (max_players - len(filled_lineup)) > 0: + st.markdown(f""" +
+ 💰 Per Player: ${round((max_salary - total_salary) / (max_players - len(filled_lineup)), 0)}   +
+ """, + unsafe_allow_html=True) + else: + st.markdown(f""" +
+ 💰 Leftover: ${round(max_salary - total_salary, 0)}   +
+ """, + unsafe_allow_html=True) + + with handbuilder_lineup_build_median_column: + if total_salary <= max_salary: + st.markdown( + f""" +
+ 💰 Salary: ${round(total_salary, 0)}   + 🔥 Median: {round(total_median, 2)}   +
+ """, + unsafe_allow_html=True + ) + else: + st.markdown( + f""" +
+ ❌ Salary: ${round(total_salary, 0)}   + 🔥 Median: {round(total_median, 2)}   +
+ """, + unsafe_allow_html=True + ) + + # Optionally, add a button to clear the lineup + clear_col, save_col, export_col, clear_saved_col, blank_col = st.columns([2, 2, 2, 2, 12]) + with clear_col: + if st.button("Clear Lineup", key='clear_lineup_button'): + st.session_state['handbuilder_lineup'] = pd.DataFrame(columns=['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot']) + st.rerun() + with save_col: + if st.button("Save Lineup", key='save_lineup_button'): + if 'saved_lineups' in st.session_state: + st.session_state['saved_lineups'].append(lineup_display_df['Player'].tolist()) + print(st.session_state['saved_lineups']) + else: + st.session_state['saved_lineups'] = [lineup_display_df['Player'].tolist()] + print(st.session_state['saved_lineups']) + st.rerun() + with export_col: + if 'saved_lineups' in st.session_state and st.session_state['saved_lineups']: + # Convert list of lists to numpy array + saved_lineups_array = np.array(st.session_state['saved_lineups']) + st.download_button( + label="Export Handbuilds", + data=convert_hb_df(saved_lineups_array, dk_hb_columns if site_var == 'Draftkings' else fd_hb_columns), + file_name='handbuilds_export.csv', + mime='text/csv', + key='export_handbuilds_button' + ) + else: + st.write("No saved lineups to export") + + if 'saved_lineups' in st.session_state: + st.table(pd.DataFrame(st.session_state['saved_lineups'], columns=dk_hb_columns if site_var == 'Draftkings' else fd_hb_columns)) + else: + st.write("No saved lineups") + + with clear_saved_col: + if st.button("Clear Saved", key='clear_saved_button'): + if 'saved_lineups' in st.session_state: + del st.session_state['saved_lineups'] + st.rerun() + +with stacks_tab: + with st.expander("Info and Filters"): + st.info(t_stamp) + with st.container(): + slate_var1 = st.radio("Which data are you loading?", ('Main Slate', 'Secondary Slate', 'Late Slate', 'Thurs-Mon Slate'), key='slate_var1_radio') + 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') + if site_var == 'Draftkings': + raw_baselines = dk_stacks_raw[dk_stacks_raw['slate'] == str(slate_var1)] + raw_baselines = raw_baselines[raw_baselines['version'] == 'overall'] + raw_baselines = raw_baselines.iloc[:,:-2] + elif site_var == 'Fanduel': + raw_baselines = fd_stacks_raw[fd_stacks_raw['slate'] == str(slate_var1)] + raw_baselines = raw_baselines[raw_baselines['version'] == 'overall'] + raw_baselines = raw_baselines.iloc[:,:-2] + if split_var1 == 'Specific Games': + team_var1 = st.multiselect('Which teams would you like to include in the ROO?', options = raw_baselines['Team'].unique(), key='team_var1_multiselect') + elif split_var1 == 'Full Slate Run': + team_var1 = raw_baselines.Team.values.tolist() + + final_stacks = raw_baselines[raw_baselines['Team'].isin(team_var1)] + if view_var == 'Simple': + final_stacks = final_stacks[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Salary', 'Median', '60+%', '4x%']] + elif view_var == 'Advanced': + final_stacks = final_stacks[['Team', 'QB', 'WR1_TE', 'WR2_TE', 'Total', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', + 'Top_10_finish', '60+%', '2x%', '3x%', '4x%', 'Own', 'LevX']] + with st.container(): + 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') + st.download_button( + label="Export Tables", + data=convert_df_to_csv(final_stacks), + file_name='NFL_stacks_export.csv', + mime='text/csv', + key='stacks_export_button' + ) + +with player_tab: + with st.expander("Info and Filters"): + st.info(t_stamp) + slate_var2 = st.radio("Which data are you loading?", ('Main Slate', 'Secondary Slate', 'Late Slate', 'Thurs-Mon Slate'), key='slate_var2_radio') + if site_var == 'Draftkings': + raw_baselines = dk_roo_raw[dk_roo_raw['slate'] == str(slate_var2)] + + raw_baselines = raw_baselines.iloc[:,:-2] + elif site_var == 'Fanduel': + raw_baselines = fd_roo_raw[fd_roo_raw['slate'] == str(slate_var2)] + raw_baselines = raw_baselines.iloc[:,:-2] + 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') + if split_var2 == 'Specific Games': + team_var2 = st.multiselect('Which teams would you like to include in the ROO?', options = raw_baselines['Team'].unique(), key='team_var2_multiselect') + elif split_var2 == 'Full Slate Run': + team_var2 = raw_baselines.Team.values.tolist() + pos_split2 = st.selectbox('What Position table would you like to view?', options = ['Overall', 'QB', 'RB', 'WR', 'TE'], key='pos_split2_selectbox') + pos_combos2 = st.multiselect('If Overall, specific positions?', options = ['QB', 'RB', 'WR', 'TE', 'DST'], default = ['QB', 'RB', 'WR', 'TE', 'DST'], key='pos_combos2_multiselect') + sal_var2 = st.slider("Is there a certain price range you want to view?", 2000, 15000, (2000, 15000), key='sal_var2_slider') - handbuilder_lineup_build_column, handbuilder_player_select_column = st.columns([1, 2]) - with handbuilder_player_select_column: - st.subheader("Player Select") - event = st.dataframe( - player_select_df.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').background_gradient(cmap='RdYlGn_r', subset=['Salary', 'Own']).format(precision=2), - on_select="rerun", - selection_mode=["single-row"], - key=f"handbuilder_select_{st.session_state['handbuilder_select_key']}", - height=500, - hide_index=True + if pos_split2 == 'Overall': + raw_baselines = raw_baselines[raw_baselines['version'] == 'overall'] + elif pos_split2 == 'QB': + if site_var == 'Draftkings': + raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_qbs'] + elif site_var == 'Fanduel': + raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_qbs'] + elif pos_split2 == 'RB': + if site_var == 'Draftkings': + raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_rbs'] + elif site_var == 'Fanduel': + raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_rbs'] + elif pos_split2 == 'WR': + if site_var == 'Draftkings': + raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_wrs'] + elif site_var == 'Fanduel': + raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_wrs'] + elif pos_split2 == 'TE': + if site_var == 'Draftkings': + raw_baselines = raw_baselines[raw_baselines['version'] == 'dk_tes'] + elif site_var == 'Fanduel': + raw_baselines = raw_baselines[raw_baselines['version'] == 'fd_tes'] + raw_baselines = raw_baselines[raw_baselines['Position'].str.contains('|'.join(pos_combos2))] + final_Proj = raw_baselines[raw_baselines['Team'].isin(team_var2)] + final_Proj = final_Proj[final_Proj['Salary'] >= sal_var2[0]] + final_Proj = final_Proj[final_Proj['Salary'] <= sal_var2[1]] + + if view_var == 'Simple': + final_Proj = final_Proj[['Player', 'Position', 'Team', 'Salary', 'Median', 'Top_5_finish', '4x%']] + disp_proj = final_Proj.set_index('Player') + elif view_var == 'Advanced': + 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']] + disp_proj = final_Proj.set_index('Player') + with st.container(): + 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') + st.download_button( + label="Export Tables", + data=convert_df_to_csv(final_Proj), + file_name='NFL_ROO_export.csv', + mime='text/csv', + key='player_export_button' ) - # If a row is selected, add that player to the lineup and reset selection - if event and "rows" in event.selection and len(event.selection["rows"]) > 0: - idx = event.selection["rows"][0] - player_row = player_select_df.iloc[[idx]] - eligible_positions = re.split(r'[/, ]+', player_row['Position'].iloc[0]) - # Find the first eligible slot that is not full - slot_to_fill = None - for slot in ['QB', 'RB', 'WR', 'TE', 'UTIL', 'DST']: - if slot_counts.get(slot, 0) < position_limits.get(slot, 0): - if slot == 'UTIL': - if 'DST' not in eligible_positions and 'QB' not in eligible_positions: - slot_to_fill = slot - break - elif slot in eligible_positions: - slot_to_fill = slot - break +with optimals_tab: + st.header("Optimals") + with st.expander("Info and Filters"): + 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.") + optimals_site_col, optimals_macro_col, optimals_salary_col, optimals_stacks_col = st.columns(4) + with optimals_site_col: + slate_type_var3 = st.radio("Which slate type are you loading?", ('Regular', 'Showdown'), key='slate_type_var3_radio') + if slate_type_var3 == 'Regular': + if site_var == 'Draftkings': + raw_baselines = dk_roo_raw + elif site_var == 'Fanduel': + raw_baselines = fd_roo_raw + elif slate_type_var3 == 'Showdown': + if site_var == 'Draftkings': + raw_baselines = dk_sd_roo_raw + elif site_var == 'Fanduel': + raw_baselines = fd_sd_roo_raw + slate_var3 = st.radio("Which slate data are you loading?", ('Main', 'Secondary', 'Auxiliary'), key='slate_var3_radio') + if slate_type_var3 == 'Regular': + if site_var == 'Draftkings': + dk_lineups = init_DK_lineups(slate_type_var3, slate_var3) + elif site_var == 'Fanduel': + fd_lineups = init_FD_lineups(slate_type_var3, slate_var3) + elif slate_type_var3 == 'Showdown': + if site_var == 'Draftkings': + dk_lineups = init_DK_lineups(slate_type_var3, slate_var3) + elif site_var == 'Fanduel': + fd_lineups = init_FD_lineups(slate_type_var3, slate_var3) + with optimals_macro_col: + 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') + player_var1 = st.radio("Do you want a frame with specific Players?", ('Full Slate', 'Specific Players'), key='player_var1_radio') + if player_var1 == 'Specific Players': + player_var2 = st.multiselect('Which players do you want?', options = raw_baselines['Player'].unique(), key='player_var2_multiselect') + elif player_var1 == 'Full Slate': + player_var2 = raw_baselines.Player.values.tolist() + with optimals_salary_col: + if site_var == 'Draftkings': + salary_min_var = st.number_input("Minimum salary used", min_value = 0, max_value = 50000, value = 49000, step = 100, key = 'salary_min_var_dk') + salary_max_var = st.number_input("Maximum salary used", min_value = 0, max_value = 50000, value = 50000, step = 100, key = 'salary_max_var_dk') + elif site_var == 'Fanduel': + salary_min_var = st.number_input("Minimum salary used", min_value = 0, max_value = 60000, value = 59000, step = 100, key = 'salary_min_var_fd') + salary_max_var = st.number_input("Maximum salary used", min_value = 0, max_value = 60000, value = 60000, step = 100, key = 'salary_max_var_fd') + with optimals_stacks_col: + if site_var == 'Draftkings': + min_stacks_var = st.number_input("Minimum stacks used", min_value = 0, max_value = 5, value = 1, step = 1, key = 'min_stacks_var_dk') + max_stacks_var = st.number_input("Maximum stacks used", min_value = 0, max_value = 5, value = 5, step = 1, key = 'max_stacks_var_dk') + elif site_var == 'Fanduel': + min_stacks_var = st.number_input("Minimum stacks used", min_value = 0, max_value = 4, value = 1, step = 1, key = 'min_stacks_var_fd') + max_stacks_var = st.number_input("Maximum stacks used", min_value = 0, max_value = 4, value = 4, step = 1, key = 'max_stacks_var_fd') + + + if site_var == 'Draftkings': + raw_baselines = dk_roo_raw + if slate_type_var3 == 'Regular': + ROO_slice = raw_baselines + player_salaries = dict(zip(ROO_slice['Player'], ROO_slice['Salary'])) + column_names = dk_columns + elif slate_type_var3 == 'Showdown': + player_salaries = dict(zip(raw_baselines['Player'], raw_baselines['Salary'])) + column_names = dk_sd_columns + + + elif site_var == 'Fanduel': + raw_baselines = fd_roo_raw + if slate_type_var3 == 'Regular': + ROO_slice = raw_baselines + player_salaries = dict(zip(ROO_slice['Player'], ROO_slice['Salary'])) + column_names = fd_columns + elif slate_type_var3 == 'Showdown': + player_salaries = dict(zip(raw_baselines['Player'], raw_baselines['Salary'])) + column_names = fd_sd_columns - if slot_to_fill is not None: - # Avoid duplicates - if not player_row['Player'].iloc[0] in st.session_state['handbuilder_lineup']['Player'].values: - # Add the slot info - player_row = player_row.assign(Slot=slot_to_fill) - st.session_state['handbuilder_lineup'] = pd.concat( - [st.session_state['handbuilder_lineup'], player_row[['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot']]], - ignore_index=True + reg_dl_col, filtered_dl_col, blank_dl_col = st.columns([2, 2, 6]) + with reg_dl_col: + if st.button("Prepare full data export", key='data_export_button'): + name_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names) + data_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names) + if site_var == 'Draftkings': + if slate_type_var3 == 'Regular': + map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] + for col_idx in map_columns: + data_export[col_idx] = data_export[col_idx].map(dk_id_map) + elif slate_type_var3 == 'Showdown': + map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] + for col_idx in map_columns: + data_export[col_idx] = data_export[col_idx].map(dk_sd_id_map) + elif site_var == 'Fanduel': + if slate_type_var3 == 'Regular': + map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] + for col_idx in map_columns: + data_export[col_idx] = data_export[col_idx].map(fd_id_map) + elif slate_type_var3 == 'Showdown': + map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] + for col_idx in map_columns: + data_export[col_idx] = data_export[col_idx].map(fd_sd_id_map) + reg_opt_col, pm_opt_col = st.columns(2) + with reg_opt_col: + st.download_button( + label="Export optimals set (IDs)", + data=convert_df(data_export), + file_name='NFL_optimals_export.csv', + mime='text/csv', + key='export_optimals_ids_button' + ) + st.download_button( + label="Export optimals set (Names)", + data=convert_df(name_export), + file_name='NFL_optimals_export.csv', + mime='text/csv', + key='export_optimals_names_button' + ) + with pm_opt_col: + if site_var == 'Draftkings': + if slate_type_var3 == 'Regular': + data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif slate_type_var3 == 'Showdown': + data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif site_var == 'Fanduel': + if slate_type_var3 == 'Regular': + data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif slate_type_var3 == 'Showdown': + data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + st.download_button( + label="Portfolio Manager Export (IDs)", + data=convert_pm_df(data_export), + file_name='NFL_optimals_export.csv', + mime='text/csv', + key='export_pm_ids_button' + ) + + if site_var == 'Draftkings': + if slate_type_var3 == 'Regular': + name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif slate_type_var3 == 'Showdown': + name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif site_var == 'Fanduel': + if slate_type_var3 == 'Regular': + name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif slate_type_var3 == 'Showdown': + name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + st.download_button( + label="Portfolio Manager Export (Names)", + data=convert_pm_df(name_export), + file_name='NFL_optimals_export.csv', + mime='text/csv', + key='export_pm_names_button' ) - st.session_state['handbuilder_select_key'] += 1 - st.rerun() - - - with handbuilder_lineup_build_column: - st.subheader("Lineup Build") - - # --- EXPLICIT LINEUP ORDER --- - if site_var == 'Draftkings': - lineup_slots = ['QB', 'RB', 'RB', 'WR', 'WR', 'WR', 'TE', 'UTIL', 'DST'] - else: - lineup_slots = ['QB', 'RB', 'RB', 'WR', 'WR', 'WR', 'TE', 'UTIL', 'DST'] - display_columns = ['Slot', 'Player', 'Position', 'Team', 'Salary', 'Median', 'Own'] + with filtered_dl_col: + if st.button("Prepare full data export (Filtered)", key='data_export_filtered_button'): + name_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names) + data_export = pd.DataFrame(st.session_state.working_seed.copy(), columns=column_names) + if site_var == 'Draftkings': + if slate_type_var3 == 'Regular': + map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] + elif slate_type_var3 == 'Showdown': + map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] + for col_idx in map_columns: + data_export[col_idx] = data_export[col_idx].map(dk_id_map) + elif site_var == 'Fanduel': + if slate_type_var3 == 'Regular': + map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] + elif slate_type_var3 == 'Showdown': + map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] + for col_idx in map_columns: + data_export[col_idx] = data_export[col_idx].map(fd_id_map) + data_export = data_export[data_export['salary'] >= salary_min_var] + data_export = data_export[data_export['salary'] <= salary_max_var] + data_export = data_export[data_export['Team_count'] >= min_stacks_var] + data_export = data_export[data_export['Team_count'] <= max_stacks_var] - filled_lineup = st.session_state['handbuilder_lineup'] - display_rows = [] - used_indices = set() - if not filled_lineup.empty: - for slot in lineup_slots: - match = filled_lineup[(filled_lineup['Slot'] == slot) & (~filled_lineup.index.isin(used_indices))] - if not match.empty: - row = match.iloc[0] - used_indices.add(match.index[0]) - display_rows.append({ - 'Slot': slot, - 'Player': row['Player'], - 'Position': row['Position'], - 'Team': row['Team'], - 'Salary': row['Salary'], - 'Median': row['Median'], - '2x%': row['2x%'], - 'Own': row['Own'] + name_export = name_export[name_export['salary'] >= salary_min_var] + name_export = name_export[name_export['salary'] <= salary_max_var] + name_export = name_export[name_export['Team_count'] >= min_stacks_var] + name_export = name_export[name_export['Team_count'] <= max_stacks_var] + + reg_opt_col, pm_opt_col = st.columns(2) + with reg_opt_col: + st.download_button( + label="Export optimals set (IDs)", + data=convert_df(data_export), + file_name='NFL_optimals_export.csv', + mime='text/csv', + key='export_filtered_optimals_ids_button' + ) + st.download_button( + label="Export optimals set (Names)", + data=convert_df(name_export), + file_name='NFL_optimals_export.csv', + mime='text/csv', + key='export_filtered_optimals_names_button' + ) + with pm_opt_col: + if site_var == 'Draftkings': + if slate_type_var3 == 'Regular': + data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif slate_type_var3 == 'Showdown': + data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif site_var == 'Fanduel': + if slate_type_var3 == 'Regular': + data_export = data_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif slate_type_var3 == 'Showdown': + data_export = data_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + st.download_button( + label="Portfolio Manager Export (IDs)", + data=convert_pm_df(data_export), + file_name='NFL_optimals_export.csv', + mime='text/csv', + key='export_filtered_pm_ids_button' + ) + + if site_var == 'Draftkings': + if slate_type_var3 == 'Regular': + name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif slate_type_var3 == 'Showdown': + name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif site_var == 'Fanduel': + if slate_type_var3 == 'Regular': + name_export = name_export.set_index('QB').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + elif slate_type_var3 == 'Showdown': + name_export = name_export.set_index('CPT').drop(columns=['salary', 'proj', 'Team', 'Team_count', 'Secondary', 'Secondary_count', 'Own'], axis=1) + st.download_button( + label="Portfolio Manager Export (Names)", + data=convert_pm_df(name_export), + file_name='NFL_optimals_export.csv', + mime='text/csv', + key='export_filtered_pm_names_button' + ) + + if site_var == 'Draftkings': + if 'working_seed' in st.session_state: + st.session_state.working_seed = st.session_state.working_seed + if player_var1 == 'Specific Players': + st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)] + elif player_var1 == 'Full Slate': + st.session_state.working_seed = dk_lineups.copy() + st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names) + elif 'working_seed' not in st.session_state: + st.session_state.working_seed = dk_lineups.copy() + st.session_state.working_seed = st.session_state.working_seed + if player_var1 == 'Specific Players': + st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)] + elif player_var1 == 'Full Slate': + st.session_state.working_seed = dk_lineups.copy() + st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names) + + elif site_var == 'Fanduel': + if 'working_seed' in st.session_state: + st.session_state.working_seed = st.session_state.working_seed + if player_var1 == 'Specific Players': + st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)] + elif player_var1 == 'Full Slate': + st.session_state.working_seed = fd_lineups.copy() + st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names) + elif 'working_seed' not in st.session_state: + st.session_state.working_seed = fd_lineups.copy() + st.session_state.working_seed = st.session_state.working_seed + if player_var1 == 'Specific Players': + st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)] + elif player_var1 == 'Full Slate': + st.session_state.working_seed = fd_lineups.copy() + st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names) + st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['salary'] >= salary_min_var] + st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['salary'] <= salary_max_var] + st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['Team_count'] >= min_stacks_var] + st.session_state.data_export_display = st.session_state.data_export_display[st.session_state.data_export_display['Team_count'] <= max_stacks_var] + export_file = st.session_state.data_export_display.copy() + name_export = st.session_state.data_export_display.copy() + if site_var == 'Draftkings': + if slate_type_var3 == 'Regular': + map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] + elif slate_type_var3 == 'Showdown': + map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] + for col_idx in map_columns: + export_file[col_idx] = export_file[col_idx].map(dk_id_map) + elif site_var == 'Fanduel': + if slate_type_var3 == 'Regular': + map_columns = ['QB', 'RB1', 'RB2', 'WR1', 'WR2', 'WR3', 'TE', 'FLEX', 'DST'] + elif slate_type_var3 == 'Showdown': + map_columns = ['CPT', 'FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5'] + for col_idx in map_columns: + export_file[col_idx] = export_file[col_idx].map(fd_id_map) + + with st.container(): + if st.button("Reset Optimals", key='reset_optimals_button'): + for key in st.session_state.keys(): + del st.session_state[key] + if site_var == 'Draftkings': + st.session_state.working_seed = dk_lineups.copy() + elif site_var == 'Fanduel': + st.session_state.working_seed = fd_lineups.copy() + if 'data_export_display' in st.session_state: + 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') + st.download_button( + label="Export display optimals (IDs)", + data=convert_df(export_file), + file_name='NFL_display_optimals.csv', + mime='text/csv', + key='export_display_optimals_ids_button' + ) + st.download_button( + label="Export display optimals (Names)", + data=convert_df(name_export), + file_name='NFL_display_optimals.csv', + mime='text/csv', + key='export_display_optimals_names_button' + ) + + with st.container(): + if slate_type_var3 == 'Regular': + if 'working_seed' in st.session_state: + # Create a new dataframe with summary statistics + if site_var == 'Draftkings': + summary_df = pd.DataFrame({ + 'Metric': ['Min', 'Average', 'Max', 'STDdev'], + 'Salary': [ + np.min(st.session_state.working_seed[:,9]), + np.mean(st.session_state.working_seed[:,9]), + np.max(st.session_state.working_seed[:,9]), + np.std(st.session_state.working_seed[:,9]) + ], + 'Proj': [ + np.min(st.session_state.working_seed[:,10]), + np.mean(st.session_state.working_seed[:,10]), + np.max(st.session_state.working_seed[:,10]), + np.std(st.session_state.working_seed[:,10]) + ], + 'Own': [ + np.min(st.session_state.working_seed[:,15]), + np.mean(st.session_state.working_seed[:,15]), + np.max(st.session_state.working_seed[:,15]), + np.std(st.session_state.working_seed[:,15]) + ] + }) + elif site_var == 'Fanduel': + summary_df = pd.DataFrame({ + 'Metric': ['Min', 'Average', 'Max', 'STDdev'], + 'Salary': [ + np.min(st.session_state.working_seed[:,9]), + np.mean(st.session_state.working_seed[:,9]), + np.max(st.session_state.working_seed[:,9]), + np.std(st.session_state.working_seed[:,9]) + ], + 'Proj': [ + np.min(st.session_state.working_seed[:,10]), + np.mean(st.session_state.working_seed[:,10]), + np.max(st.session_state.working_seed[:,10]), + np.std(st.session_state.working_seed[:,10]) + ], + 'Own': [ + np.min(st.session_state.working_seed[:,15]), + np.mean(st.session_state.working_seed[:,15]), + np.max(st.session_state.working_seed[:,15]), + np.std(st.session_state.working_seed[:,15]) + ] + }) + elif slate_type_var3 == 'Showdown': + if 'working_seed' in st.session_state: + # Create a new dataframe with summary statistics + if site_var == 'Draftkings': + summary_df = pd.DataFrame({ + 'Metric': ['Min', 'Average', 'Max', 'STDdev'], + 'Salary': [ + np.min(st.session_state.working_seed[:,6]), + np.mean(st.session_state.working_seed[:,6]), + np.max(st.session_state.working_seed[:,6]), + np.std(st.session_state.working_seed[:,6]) + ], + 'Proj': [ + np.min(st.session_state.working_seed[:,7]), + np.mean(st.session_state.working_seed[:,7]), + np.max(st.session_state.working_seed[:,7]), + np.std(st.session_state.working_seed[:,7]) + ], + 'Own': [ + np.min(st.session_state.working_seed[:,12]), + np.mean(st.session_state.working_seed[:,12]), + np.max(st.session_state.working_seed[:,12]), + np.std(st.session_state.working_seed[:,12]) + ] }) - else: - display_rows.append({ - 'Slot': slot, - 'Player': '', - 'Position': '', - 'Team': '', - 'Salary': np.nan, - 'Median': np.nan, - '2x%': np.nan, - 'Own': np.nan + elif site_var == 'Fanduel': + summary_df = pd.DataFrame({ + 'Metric': ['Min', 'Average', 'Max', 'STDdev'], + 'Salary': [ + np.min(st.session_state.working_seed[:,6]), + np.mean(st.session_state.working_seed[:,6]), + np.max(st.session_state.working_seed[:,6]), + np.std(st.session_state.working_seed[:,6]) + ], + 'Proj': [ + np.min(st.session_state.working_seed[:,7]), + np.mean(st.session_state.working_seed[:,7]), + np.max(st.session_state.working_seed[:,7]), + np.std(st.session_state.working_seed[:,7]) + ], + 'Own': [ + np.min(st.session_state.working_seed[:,12]), + np.mean(st.session_state.working_seed[:,12]), + np.max(st.session_state.working_seed[:,12]), + np.std(st.session_state.working_seed[:,12]) + ] }) - lineup_display_df = pd.DataFrame(display_rows, columns=display_columns) - - # Show the lineup table with single-row selection for removal - event_remove = st.dataframe( - 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), - on_select="rerun", - selection_mode=["single-row"], - key="lineup_remove_dataframe", - height=445, - hide_index=True - ) - - # If a row is selected and not blank, remove that player from the lineup - if event_remove and "rows" in event_remove.selection and len(event_remove.selection["rows"]) > 0: - idx = event_remove.selection["rows"][0] - player_to_remove = lineup_display_df.iloc[idx]['Player'] - slot_to_remove = lineup_display_df.iloc[idx]['Slot'] - if player_to_remove: # Only remove if not blank - st.session_state['handbuilder_lineup'] = filled_lineup[ - ~((filled_lineup['Player'] == player_to_remove) & (filled_lineup['Slot'] == slot_to_remove)) - ] - st.rerun() + # Set the index of the summary dataframe as the "Metric" column + summary_df = summary_df.set_index('Metric') - # --- SUMMARY ROW --- - if not filled_lineup.empty: - total_salary = filled_lineup['Salary'].sum() - total_median = filled_lineup['Median'].sum() - avg_2x = filled_lineup['2x%'].mean() - total_own = filled_lineup['Own'].sum() - most_common_team = filled_lineup['Team'].mode()[0] if not filled_lineup['Team'].mode().empty else "" + # Display the summary dataframe + st.subheader("Optimal Statistics") + st.dataframe(summary_df.style.format({ + 'Salary': '{:.2f}', + 'Proj': '{:.2f}', + 'Own': '{:.2f}' + }).background_gradient(cmap='RdYlGn', axis=0, subset=['Salary', 'Proj', 'Own']), use_container_width=True, key='optimal_stats_dataframe') - summary_row = pd.DataFrame({ - 'Slot': [''], - 'Player': ['TOTAL'], - 'Position': [''], - 'Team': [most_common_team], - 'Salary': [total_salary], - 'Median': [total_median], - '2x%': [avg_2x], - 'Own': [total_own] - }) - summary_row = summary_row[['Salary', 'Median', 'Own']].head(max_players) + with st.container(): + display_freq_tab, seed_frame_freq_tab = st.tabs(["Display Frequency", "Seed Frame Frequency"]) + with display_freq_tab: + if 'data_export_display' in st.session_state: + if site_var == 'Draftkings': + if slate_type_var3 == 'Regular': + player_columns = st.session_state.data_export_display.iloc[:, :9] + elif slate_type_var3 == 'Showdown': + player_columns = st.session_state.data_export_display.iloc[:, :6] + elif site_var == 'Fanduel': + if slate_type_var3 == 'Regular': + player_columns = st.session_state.data_export_display.iloc[:, :9] + elif slate_type_var3 == 'Showdown': + player_columns = st.session_state.data_export_display.iloc[:, :6] + + # Flatten the DataFrame and count unique values + value_counts = player_columns.values.flatten().tolist() + value_counts = pd.Series(value_counts).value_counts() + + percentages = (value_counts / lineup_num_var * 100).round(2) + + # Create a DataFrame with the results + summary_df = pd.DataFrame({ + 'Player': value_counts.index, + 'Frequency': value_counts.values, + 'Percentage': percentages.values + }) + + # Sort by frequency in descending order + summary_df['Salary'] = summary_df['Player'].map(player_salaries) + summary_df = summary_df[['Player', 'Salary', 'Frequency', 'Percentage']] + summary_df = summary_df.sort_values('Frequency', ascending=False) + summary_df = summary_df.set_index('Player') + + # Display the table + st.write("Player Frequency Table:") + st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True, key='player_frequency_dataframe') - handbuilder_lineup_build_salary_column, handbuilder_lineup_build_median_column = st.columns([2, 3]) - - with handbuilder_lineup_build_salary_column: - if (max_players - len(filled_lineup)) > 0: - st.markdown(f""" -
- 💰 Per Player: ${round((max_salary - total_salary) / (max_players - len(filled_lineup)), 0)}   -
- """, - unsafe_allow_html=True) - else: - st.markdown(f""" -
- 💰 Leftover: ${round(max_salary - total_salary, 0)}   -
- """, - unsafe_allow_html=True) - - with handbuilder_lineup_build_median_column: - if total_salary <= max_salary: - st.markdown( - f""" -
- 💰 Salary: ${round(total_salary, 0)}   - 🔥 Median: {round(total_median, 2)}   -
- """, - unsafe_allow_html=True - ) - else: - st.markdown( - f""" -
- ❌ Salary: ${round(total_salary, 0)}   - 🔥 Median: {round(total_median, 2)}   -
- """, - unsafe_allow_html=True - ) - - # Optionally, add a button to clear the lineup - clear_col, save_col, export_col, clear_saved_col, blank_col = st.columns([2, 2, 2, 2, 12]) - with clear_col: - if st.button("Clear Lineup", key='clear_lineup_button'): - st.session_state['handbuilder_lineup'] = pd.DataFrame(columns=['Player', 'Position', 'Team', 'Salary', 'Median', '2x%', 'Own', 'Slot']) - st.rerun() - with save_col: - if st.button("Save Lineup", key='save_lineup_button'): - if 'saved_lineups' in st.session_state: - st.session_state['saved_lineups'].append(lineup_display_df['Player'].tolist()) - print(st.session_state['saved_lineups']) - else: - st.session_state['saved_lineups'] = [lineup_display_df['Player'].tolist()] - print(st.session_state['saved_lineups']) - st.rerun() - with export_col: - if 'saved_lineups' in st.session_state and st.session_state['saved_lineups']: - # Convert list of lists to numpy array - saved_lineups_array = np.array(st.session_state['saved_lineups']) - st.download_button( - label="Export Handbuilds", - data=convert_hb_df(saved_lineups_array, dk_hb_columns if site_var == 'Draftkings' else fd_hb_columns), - file_name='handbuilds_export.csv', - mime='text/csv', - key='export_handbuilds_button' - ) - else: - st.write("No saved lineups to export") - - if 'saved_lineups' in st.session_state: - st.table(pd.DataFrame(st.session_state['saved_lineups'], columns=dk_hb_columns if site_var == 'Draftkings' else fd_hb_columns)) - else: - st.write("No saved lineups") - - with clear_saved_col: - if st.button("Clear Saved", key='clear_saved_button'): - if 'saved_lineups' in st.session_state: - del st.session_state['saved_lineups'] - st.rerun() \ No newline at end of file + st.download_button( + label="Export player frequency", + data=convert_df_to_csv(summary_df), + file_name='NFL_player_frequency.csv', + mime='text/csv', + key='export_player_frequency_button' + ) + with seed_frame_freq_tab: + if 'working_seed' in st.session_state: + if site_var == 'Draftkings': + if slate_type_var3 == 'Regular': + player_columns = st.session_state.working_seed[:, :9] + elif slate_type_var3 == 'Showdown': + player_columns = st.session_state.working_seed[:, :6] + elif site_var == 'Fanduel': + if slate_type_var3 == 'Regular': + player_columns = st.session_state.working_seed[:, :9] + elif slate_type_var3 == 'Showdown': + player_columns = st.session_state.working_seed[:, :6] + + # Flatten the DataFrame and count unique values + value_counts = player_columns.flatten().tolist() + value_counts = pd.Series(value_counts).value_counts() + + percentages = (value_counts / len(st.session_state.working_seed) * 100).round(2) + # Create a DataFrame with the results + summary_df = pd.DataFrame({ + 'Player': value_counts.index, + 'Frequency': value_counts.values, + 'Percentage': percentages.values + }) + + # Sort by frequency in descending order + summary_df['Salary'] = summary_df['Player'].map(player_salaries) + summary_df = summary_df[['Player', 'Salary', 'Frequency', 'Percentage']] + summary_df = summary_df.sort_values('Frequency', ascending=False) + summary_df = summary_df.set_index('Player') + + # Display the table + st.write("Seed Frame Frequency Table:") + st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True, key='seed_frame_frequency_dataframe') + + st.download_button( + label="Export seed frame frequency", + data=convert_df_to_csv(summary_df), + file_name='NFL_seed_frame_frequency.csv', + mime='text/csv', + key='export_seed_frame_frequency_button' + )