James McCool
Refactor UI layout and add custom tab styling for NHL Pivot Finder
ca1122e
import numpy as np
from numpy import random
import pandas as pd
import streamlit as st
import pymongo
st.set_page_config(layout="wide")
@st.cache_resource
def init_conn():
uri = st.secrets['mongo_uri']
client = pymongo.MongoClient(uri, retryWrites=True, serverSelectionTimeoutMS=500000)
db = client["NHL_Database"]
return db
db = init_conn()
wrong_acro = ['WSH', 'AZ']
right_acro = ['WAS', 'ARI']
game_format = {'Win Percentage': '{:.2%}','First Inning Lead Percentage': '{:.2%}',
'Fifth Inning Lead Percentage': '{:.2%}', '8+ runs': '{:.2%}', 'DK LevX': '{:.2%}', 'FD LevX': '{:.2%}'}
team_roo_format = {'Top Score%': '{:.2%}','0 Runs': '{:.2%}', '1 Run': '{:.2%}', '2 Runs': '{:.2%}', '3 Runs': '{:.2%}', '4 Runs': '{:.2%}',
'5 Runs': '{:.2%}','6 Runs': '{:.2%}', '7 Runs': '{:.2%}', '8 Runs': '{:.2%}', '9 Runs': '{:.2%}', '10 Runs': '{:.2%}'}
player_roo_format = {'Top_finish': '{:.2%}','Top_5_finish': '{:.2%}', 'Top_10_finish': '{:.2%}', '20+%': '{:.2%}', '2x%': '{:.2%}', '3x%': '{:.2%}',
'4x%': '{:.2%}','GPP%': '{:.2%}'}
st.markdown("""
<style>
/* Tab styling */
.stTabs [data-baseweb="tab-list"] {
gap: 8px;
padding: 4px;
}
.stTabs [data-baseweb="tab"] {
height: 50px;
white-space: pre-wrap;
background-color: #FFD700;
color: white;
border-radius: 10px;
gap: 1px;
padding: 10px 20px;
font-weight: bold;
transition: all 0.3s ease;
}
.stTabs [aria-selected="true"] {
background-color: #DAA520;
color: white;
}
.stTabs [data-baseweb="tab"]:hover {
background-color: #DAA520;
cursor: pointer;
}
</style>""", unsafe_allow_html=True)
@st.cache_resource(ttl = 599)
def player_stat_table():
collection = db["Player_Level_ROO"]
cursor = collection.find()
load_display = pd.DataFrame(cursor)
load_display.replace('', np.nan, inplace=True)
player_stats = load_display.copy()
dk_load_display = load_display[load_display['Site'] == 'Draftkings']
fd_load_display = load_display[load_display['Site'] == 'Fanduel']
dk_load_display = dk_load_display.sort_values(by='Own', ascending=False)
fd_load_display = fd_load_display.sort_values(by='Own', ascending=False)
dk_load_display = dk_load_display.dropna(subset=['Own'])
fd_load_display = fd_load_display.dropna(subset=['Own'])
dk_roo_raw = dk_load_display
fd_roo_raw = fd_load_display
return player_stats, dk_roo_raw, fd_roo_raw
@st.cache_data
def convert_df_to_csv(df):
return df.to_csv().encode('utf-8')
player_stats, dk_roo_raw, fd_roo_raw = player_stat_table()
opp_dict = dict(zip(dk_roo_raw.Team, dk_roo_raw.Opp))
t_stamp = f"Last Update: " + str(dk_roo_raw['timestamp'][0]) + f" CST"
st.header("NHL Pivot Finder Tool")
with st.expander("Info and Filters"):
st.info(t_stamp)
if st.button("Load/Reset Data", key='reset1'):
st.cache_data.clear()
for key in st.session_state.keys():
del st.session_state[key]
player_stats, dk_roo_raw, fd_roo_raw = player_stat_table()
opp_dict = dict(zip(dk_roo_raw.Team, dk_roo_raw.Opp))
t_stamp = f"Last Update: " + str(dk_roo_raw['timestamp'][0]) + f" CST"
site_var1 = st.radio("What site are you working with?", ('Draftkings', 'Fanduel'), key='site_var1')
if site_var1 == 'Draftkings':
raw_baselines = dk_roo_raw[dk_roo_raw['Slate'] == 'Main Slate']
raw_baselines = raw_baselines.sort_values(by='Own', ascending=False)
elif site_var1 == 'Fanduel':
raw_baselines = fd_roo_raw[fd_roo_raw['Slate'] == 'Main Slate']
raw_baselines = raw_baselines.sort_values(by='Own', ascending=False)
check_seq = st.radio("Do you want to check a single player or the top 10 in ownership?", ('Single Player', 'Top X Owned'), key='check_seq')
if check_seq == 'Single Player':
player_check = st.selectbox('Select player to create comps', options = raw_baselines['Player'].unique(), key='dk_player')
elif check_seq == 'Top X Owned':
top_x_var = st.number_input('How many players would you like to check?', min_value = 1, max_value = 10, value = 5, step = 1)
Salary_var = st.number_input('Acceptable +/- Salary range', min_value = 0, max_value = 1000, value = 300, step = 100)
Median_var = st.number_input('Acceptable +/- Median range', min_value = 0, max_value = 10, value = 3, step = 1)
pos_var1 = st.radio("Compare to all positions or specific positions?", ('All Positions', 'Specific Positions'), key='pos_var1')
if pos_var1 == 'Specific Positions':
pos_var_list = st.multiselect('Which positions would you like to include?', options = raw_baselines['Position'].unique(), key='pos_var_list')
elif pos_var1 == 'All Positions':
pos_var_list = raw_baselines.Position.values.tolist()
split_var1 = st.radio("Are you running the full slate or certain games?", ('Full Slate Run', 'Specific Games'), key='split_var1')
if split_var1 == 'Specific Games':
team_var1 = st.multiselect('Which teams would you like to include?', options = raw_baselines['Team'].unique(), key='team_var1')
elif split_var1 == 'Full Slate Run':
team_var1 = raw_baselines.Team.values.tolist()
placeholder = st.empty()
displayholder = st.empty()
if st.button('Simulate appropriate pivots'):
with placeholder:
if site_var1 == 'Draftkings':
working_roo = raw_baselines
working_roo.replace('', 0, inplace=True)
if site_var1 == 'Fanduel':
working_roo = raw_baselines
working_roo.replace('', 0, inplace=True)
own_dict = dict(zip(working_roo.Player, working_roo.Own))
team_dict = dict(zip(working_roo.Player, working_roo.Team))
opp_dict = dict(zip(working_roo.Player, working_roo.Opp))
pos_dict = dict(zip(working_roo.Player, working_roo.Position))
total_sims = 1000
if check_seq == 'Single Player':
player_var = working_roo.loc[working_roo['Player'] == player_check]
player_var = player_var.reset_index()
working_roo = working_roo[working_roo['Position'].isin(pos_var_list)]
working_roo = working_roo[working_roo['Team'].isin(team_var1)]
working_roo = working_roo.loc[(working_roo['Salary'] >= player_var['Salary'][0] - Salary_var) & (working_roo['Salary'] <= player_var['Salary'][0] + Salary_var)]
working_roo = working_roo.loc[(working_roo['Median'] >= player_var['Median'][0] - Median_var) & (working_roo['Median'] <= player_var['Median'][0] + Median_var)]
flex_file = working_roo[['Player', 'Position', 'Salary', 'Median']]
flex_file['Floor_raw'] = flex_file['Median'] * .25
flex_file['Ceiling_raw'] = flex_file['Median'] * 2
flex_file['Floor'] = np.where(flex_file['Position'] == 'G', flex_file['Median'] * .5, flex_file['Floor_raw'])
flex_file['Floor'] = np.where(flex_file['Position'] == 'D', flex_file['Median'] * .1, flex_file['Floor_raw'])
flex_file['Ceiling'] = np.where(flex_file['Position'] == 'G', flex_file['Median'] * 1.75, flex_file['Ceiling_raw'])
flex_file['Ceiling'] = np.where(flex_file['Position'] == 'D', flex_file['Median'] * 1.75, flex_file['Ceiling_raw'])
flex_file['STD'] = flex_file['Median'] / 3
flex_file = flex_file[['Player', 'Position', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD']]
hold_file = flex_file.copy()
overall_file = flex_file.copy()
salary_file = flex_file.copy()
overall_players = overall_file[['Player']]
for x in range(0,total_sims):
salary_file[x] = salary_file['Salary']
overall_file[x] = random.normal(overall_file['Median'],overall_file['STD'])
salary_file=salary_file.drop(['Player', 'Position', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
salary_file = salary_file.div(1000)
overall_file=overall_file.drop(['Player', 'Position', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
players_only = hold_file[['Player']]
raw_lineups_file = players_only
for x in range(0,total_sims):
maps_dict = {'proj_map':dict(zip(hold_file.Player,overall_file[x]))}
raw_lineups_file[x] = sum([raw_lineups_file['Player'].map(maps_dict['proj_map'])])
players_only[x] = raw_lineups_file[x].rank(ascending=False)
players_only=players_only.drop(['Player'], axis=1)
salary_2x_check = (overall_file - (salary_file*2))
salary_3x_check = (overall_file - (salary_file*3))
salary_4x_check = (overall_file - (salary_file*4))
players_only['Average_Rank'] = players_only.mean(axis=1)
players_only['Top_finish'] = players_only[players_only == 1].count(axis=1)/total_sims
players_only['Top_5_finish'] = players_only[players_only <= 5].count(axis=1)/total_sims
players_only['Top_10_finish'] = players_only[players_only <= 10].count(axis=1)/total_sims
players_only['20+%'] = overall_file[overall_file >= 20].count(axis=1)/float(total_sims)
players_only['2x%'] = salary_2x_check[salary_2x_check >= 1].count(axis=1)/float(total_sims)
players_only['3x%'] = salary_3x_check[salary_3x_check >= 1].count(axis=1)/float(total_sims)
players_only['4x%'] = salary_4x_check[salary_4x_check >= 1].count(axis=1)/float(total_sims)
players_only['Player'] = hold_file[['Player']]
final_outcomes = players_only[['Player', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%']]
final_Proj = pd.merge(hold_file, final_outcomes, on="Player")
final_Proj = final_Proj[['Player', 'Position', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%']]
final_Proj['Own'] = final_Proj['Player'].map(own_dict)
final_Proj['Team'] = final_Proj['Player'].map(team_dict)
final_Proj['Opp'] = final_Proj['Player'].map(opp_dict)
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']]
final_Proj['Projection Rank'] = final_Proj.Median.rank(pct = True)
final_Proj['Own Rank'] = final_Proj.Own.rank(pct = True)
final_Proj['LevX'] = 0
final_Proj['LevX'] = np.where(final_Proj['Position'] == 'C', final_Proj[['Projection Rank', 'Top_5_finish']].mean(axis=1) + final_Proj['20+%'] - final_Proj['Own Rank'], final_Proj['LevX'])
final_Proj['LevX'] = np.where(final_Proj['Position'] == 'W', final_Proj[['Projection Rank', 'Top_5_finish']].mean(axis=1) + final_Proj['20+%'] - final_Proj['Own Rank'], final_Proj['LevX'])
final_Proj['LevX'] = np.where(final_Proj['Position'] == 'D', final_Proj[['Projection Rank', '2x%']].mean(axis=1) + final_Proj['4x%'] - final_Proj['Own Rank'], final_Proj['LevX'])
final_Proj['LevX'] = np.where(final_Proj['Position'] == 'G', final_Proj[['Projection Rank', '2x%']].mean(axis=1) + final_Proj['4x%'] - final_Proj['Own Rank'], final_Proj['LevX'])
final_Proj['CPT_Own'] = final_Proj['Own'] / 4
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', 'LevX']]
final_Proj = final_Proj.set_index('Player')
st.session_state.final_Proj = final_Proj.sort_values(by='Top_finish', ascending=False)
elif check_seq == 'Top X Owned':
if pos_var1 == 'Specific Positions':
raw_baselines = raw_baselines[raw_baselines['Position'].isin(pos_var_list)]
player_check = raw_baselines['Player'].head(top_x_var).tolist()
final_proj_list = []
for players in player_check:
players_pos = pos_dict[players]
player_var = working_roo.loc[working_roo['Player'] == players]
player_var = player_var.reset_index()
working_roo_temp = working_roo[working_roo['Position'] == players_pos]
working_roo_temp = working_roo_temp[working_roo_temp['Team'].isin(team_var1)]
working_roo_temp = working_roo_temp.loc[(working_roo_temp['Salary'] >= player_var['Salary'][0] - Salary_var) & (working_roo_temp['Salary'] <= player_var['Salary'][0] + Salary_var)]
working_roo_temp = working_roo_temp.loc[(working_roo_temp['Median'] >= player_var['Median'][0] - Median_var) & (working_roo_temp['Median'] <= player_var['Median'][0] + Median_var)]
flex_file = working_roo_temp[['Player', 'Position', 'Salary', 'Median']]
flex_file['Floor_raw'] = flex_file['Median'] * .25
flex_file['Ceiling_raw'] = flex_file['Median'] * 2
flex_file['Floor'] = np.where(flex_file['Position'] == 'G', flex_file['Median'] * .5, flex_file['Floor_raw'])
flex_file['Floor'] = np.where(flex_file['Position'] == 'D', flex_file['Median'] * .1, flex_file['Floor_raw'])
flex_file['Ceiling'] = np.where(flex_file['Position'] == 'G', flex_file['Median'] * 1.75, flex_file['Ceiling_raw'])
flex_file['Ceiling'] = np.where(flex_file['Position'] == 'D', flex_file['Median'] * 1.75, flex_file['Ceiling_raw'])
flex_file['STD'] = flex_file['Median'] / 3
flex_file = flex_file[['Player', 'Position', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD']]
hold_file = flex_file.copy()
overall_file = flex_file.copy()
salary_file = flex_file.copy()
overall_players = overall_file[['Player']]
for x in range(0,total_sims):
salary_file[x] = salary_file['Salary']
overall_file[x] = random.normal(overall_file['Median'],overall_file['STD'])
salary_file=salary_file.drop(['Player', 'Position', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
salary_file = salary_file.div(1000)
overall_file=overall_file.drop(['Player', 'Position', 'Salary', 'Floor', 'Median', 'Ceiling', 'STD'], axis=1)
players_only = hold_file[['Player']]
raw_lineups_file = players_only
for x in range(0,total_sims):
maps_dict = {'proj_map':dict(zip(hold_file.Player,overall_file[x]))}
raw_lineups_file[x] = sum([raw_lineups_file['Player'].map(maps_dict['proj_map'])])
players_only[x] = raw_lineups_file[x].rank(ascending=False)
players_only=players_only.drop(['Player'], axis=1)
salary_2x_check = (overall_file - (salary_file*2))
salary_3x_check = (overall_file - (salary_file*3))
salary_4x_check = (overall_file - (salary_file*4))
players_only['Average_Rank'] = players_only.mean(axis=1)
players_only['Top_finish'] = players_only[players_only == 1].count(axis=1)/total_sims
players_only['Top_5_finish'] = players_only[players_only <= 5].count(axis=1)/total_sims
players_only['Top_10_finish'] = players_only[players_only <= 10].count(axis=1)/total_sims
players_only['20+%'] = overall_file[overall_file >= 20].count(axis=1)/float(total_sims)
players_only['2x%'] = salary_2x_check[salary_2x_check >= 1].count(axis=1)/float(total_sims)
players_only['3x%'] = salary_3x_check[salary_3x_check >= 1].count(axis=1)/float(total_sims)
players_only['4x%'] = salary_4x_check[salary_4x_check >= 1].count(axis=1)/float(total_sims)
players_only['Player'] = hold_file[['Player']]
final_outcomes = players_only[['Player', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%']]
final_Proj = pd.merge(hold_file, final_outcomes, on="Player")
final_Proj = final_Proj[['Player', 'Position', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%']]
final_Proj['Own'] = final_Proj['Player'].map(own_dict)
final_Proj['Team'] = final_Proj['Player'].map(team_dict)
final_Proj['Opp'] = final_Proj['Player'].map(opp_dict)
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']]
final_Proj['Projection Rank'] = final_Proj.Median.rank(pct = True)
final_Proj['Own Rank'] = final_Proj.Own.rank(pct = True)
final_Proj['LevX'] = 0
final_Proj['LevX'] = np.where(final_Proj['Position'] == 'C', final_Proj[['Projection Rank', 'Top_5_finish']].mean(axis=1) + final_Proj['20+%'] - final_Proj['Own Rank'], final_Proj['LevX'])
final_Proj['LevX'] = np.where(final_Proj['Position'] == 'W', final_Proj[['Projection Rank', 'Top_5_finish']].mean(axis=1) + final_Proj['20+%'] - final_Proj['Own Rank'], final_Proj['LevX'])
final_Proj['LevX'] = np.where(final_Proj['Position'] == 'D', final_Proj[['Projection Rank', '2x%']].mean(axis=1) + final_Proj['4x%'] - final_Proj['Own Rank'], final_Proj['LevX'])
final_Proj['LevX'] = np.where(final_Proj['Position'] == 'G', final_Proj[['Projection Rank', '2x%']].mean(axis=1) + final_Proj['4x%'] - final_Proj['Own Rank'], final_Proj['LevX'])
final_Proj['CPT_Own'] = final_Proj['Own'] / 4
final_Proj['Pivot_source'] = players
final_Proj = final_Proj[['Player', 'Pivot_source', 'Position', 'Team', 'Opp', 'Salary', 'Floor', 'Median', 'Ceiling', 'Top_finish', 'Top_5_finish', 'Top_10_finish', '20+%', '2x%', '3x%', '4x%', 'Own', 'LevX']]
final_Proj = final_Proj.sort_values(by='Top_finish', ascending=False)
final_proj_list.append(final_Proj)
st.write(f'finished run for {players}')
# Concatenate all the final_Proj dataframes
final_Proj_combined = pd.concat(final_proj_list)
final_Proj_combined = final_Proj_combined.sort_values(by='LevX', ascending=False)
final_Proj_combined = final_Proj_combined[final_Proj_combined['Player'] != final_Proj_combined['Pivot_source']]
st.session_state.final_Proj = final_Proj_combined.reset_index(drop=True) # Assign the combined dataframe back to final_Proj
placeholder.empty()
with displayholder.container():
if 'final_Proj' in st.session_state:
st.dataframe(st.session_state.final_Proj.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(player_roo_format, precision=2), use_container_width = True)
st.download_button(
label="Export Tables",
data=convert_df_to_csv(st.session_state.final_Proj),
file_name='NHL_pivot_export.csv',
mime='text/csv',
)
else:
st.write("Run some pivots my dude/dudette")