NBA_Macro_Research / src /streamlit_app.py
James McCool
adjusting for no games on slate
b8b533c
import streamlit as st
st.set_page_config(layout="wide")
for name in dir():
if not name.startswith('_'):
del globals()[name]
import numpy as np
import pandas as pd
import streamlit as st
import os
import requests
NBA_DATA = os.getenv('NBA_DATA')
percentages_format = {'Pts% Boost': '{:.2%}', 'Reb% Boost': '{:.2%}', 'Ast% Boost': '{:.2%}', '3p% Boost': '{:.2%}',
'Stl Boost%': '{:.2%}', 'Blk Boost%': '{:.2%}', 'TOV Boost%': '{:.2%}', 'FPPM Boost': '{:.2%}',
'Team FPPM Boost': '{:.2%}'}
team_macro_format = {'FGM%': '{:.2%}', 'FG3M%': '{:.2%}', 'FTM%': '{:.2%}'}
macro_minutes_cols = ['Team', 'Games', 'MIN', 'FGA/m', 'FG3A/m', 'FTA/m', 'PTS/m', 'REB/m', 'AST/m', 'STL/m', 'BLK/m', 'Fantasy/m', 'FD_Fantasy/m']
remove_minutes_cols = ['MIN', 'FGA/m', 'FG3A/m', 'FTA/m', 'PTS/m', 'REB/m', 'AST/m', 'STL/m', 'BLK/m', 'Fantasy/m', 'FD_Fantasy/m']
st.markdown("""
<style>
/* Tab styling */
.stElementContainer [data-baseweb="button-group"] {
gap: 2.000rem;
padding: 4px;
}
.stElementContainer [kind="segmented_control"] {
height: 2.000rem;
white-space: pre-wrap;
background-color: #DAA520;
color: white;
border-radius: 20px;
gap: 1px;
padding: 10px 20px;
font-weight: bold;
transition: all 0.3s ease;
}
.stElementContainer [kind="segmented_controlActive"] {
height: 3.000rem;
background-color: #DAA520;
border: 3px solid #FFD700;
border-radius: 10px;
color: black;
}
.stElementContainer [kind="segmented_control"]:hover {
background-color: #FFD700;
cursor: pointer;
}
div[data-baseweb="select"] > div {
background-color: #DAA520;
color: white;
}
</style>""", unsafe_allow_html=True)
@st.cache_resource(ttl = 600)
def init_DEM():
json_matchups = requests.get(NBA_DATA + '?sheet=DEM%20Matchups').json()
raw_display = pd.DataFrame(json_matchups)
raw_display = raw_display.reset_index(drop=True)
matchups = raw_display[raw_display['Var'] != ""]
matchups_dict = dict(zip(matchups['Team'], matchups['Opp']))
master_dem_json = requests.get(NBA_DATA + '?sheet=Master_DEM_Calc').json()
master_dem_frame = pd.DataFrame(master_dem_json)
raw_display = master_dem_frame[master_dem_frame['Position'] == 'PG']
raw_display = raw_display.reset_index(drop=True)
cols_to_check = ['Pts% Boost', 'Reb% Boost', 'Ast% Boost', '3p% Boost', 'Stl Boost%', 'Blk Boost%', 'TOV Boost%', 'FPPM Boost']
raw_display.loc[:, cols_to_check] = raw_display.loc[:, cols_to_check].replace({'%': ''}, regex=True).astype(float) / 100
raw_display = raw_display.apply(pd.to_numeric, errors='coerce').fillna(raw_display)
raw_display['position'] = 'Point Guard'
pg_dem = raw_display[raw_display['Acro'] != ""]
raw_display = master_dem_frame[master_dem_frame['Position'] == 'SG']
raw_display = raw_display.reset_index(drop=True)
cols_to_check = ['Pts% Boost', 'Reb% Boost', 'Ast% Boost', '3p% Boost', 'Stl Boost%', 'Blk Boost%', 'TOV Boost%', 'FPPM Boost']
raw_display.loc[:, cols_to_check] = raw_display.loc[:, cols_to_check].replace({'%': ''}, regex=True).astype(float) / 100
raw_display = raw_display.apply(pd.to_numeric, errors='coerce').fillna(raw_display)
raw_display['position'] = 'Shooting Guard'
sg_dem = raw_display[raw_display['Acro'] != ""]
raw_display = master_dem_frame[master_dem_frame['Position'] == 'SF']
raw_display = raw_display.reset_index(drop=True)
cols_to_check = ['Pts% Boost', 'Reb% Boost', 'Ast% Boost', '3p% Boost', 'Stl Boost%', 'Blk Boost%', 'TOV Boost%', 'FPPM Boost']
raw_display.loc[:, cols_to_check] = raw_display.loc[:, cols_to_check].replace({'%': ''}, regex=True).astype(float) / 100
raw_display = raw_display.apply(pd.to_numeric, errors='coerce').fillna(raw_display)
raw_display['position'] = 'Small Forward'
sf_dem = raw_display[raw_display['Acro'] != ""]
raw_display = master_dem_frame[master_dem_frame['Position'] == 'PF']
raw_display = raw_display.reset_index(drop=True)
cols_to_check = ['Pts% Boost', 'Reb% Boost', 'Ast% Boost', '3p% Boost', 'Stl Boost%', 'Blk Boost%', 'TOV Boost%', 'FPPM Boost']
raw_display.loc[:, cols_to_check] = raw_display.loc[:, cols_to_check].replace({'%': ''}, regex=True).astype(float) / 100
raw_display = raw_display.apply(pd.to_numeric, errors='coerce').fillna(raw_display)
raw_display['position'] = 'Power Forward'
pf_dem = raw_display[raw_display['Acro'] != ""]
raw_display = master_dem_frame[master_dem_frame['Position'] == 'C']
raw_display = raw_display.reset_index(drop=True)
cols_to_check = ['Pts% Boost', 'Reb% Boost', 'Ast% Boost', '3p% Boost', 'Stl Boost%', 'Blk Boost%', 'TOV Boost%', 'FPPM Boost']
raw_display.loc[:, cols_to_check] = raw_display.loc[:, cols_to_check].replace({'%': ''}, regex=True).astype(float) / 100
raw_display = raw_display.apply(pd.to_numeric, errors='coerce').fillna(raw_display)
raw_display['position'] = 'Center'
c_dem = raw_display[raw_display['Acro'] != ""]
overall_dem = pd.concat([pg_dem, sg_dem, sf_dem, pf_dem, c_dem])
overall_dem = overall_dem[['Acro', 'G', 'Pts% Boost', 'Reb% Boost', 'Ast% Boost', '3p% Boost',
'Stl Boost%', 'Blk Boost%', 'TOV Boost%', 'FPPM', 'FPPM Boost', 'position']]
overall_dem['Team'] = overall_dem['Acro'] + '-' + overall_dem['position']
overall_dem['Team FPPM Boost'] = overall_dem.groupby('Acro', sort=False)['FPPM Boost'].transform('mean')
overall_dem = overall_dem.reset_index()
export_dem = overall_dem[['Team', 'Acro', 'G', 'Pts% Boost', 'Reb% Boost', 'Ast% Boost', '3p% Boost',
'Stl Boost%', 'Blk Boost%', 'TOV Boost%', 'FPPM', 'FPPM Boost', 'Team FPPM Boost', 'position']]
return export_dem, matchups, matchups_dict
@st.cache_resource(ttl = 600)
def init_macro_tables():
json_matchups = requests.get(NBA_DATA + '?sheet=Site_Info').json()
raw_display = pd.DataFrame(json_matchups)
raw_display = raw_display.reset_index(drop=True)
dk_main_slate = raw_display['DK Main'].values.tolist()
fd_main_slate = raw_display['FD Main'].values.tolist()
team_macro_json = requests.get(NBA_DATA + '?sheet=Team_Macro').json()
team_macro_frame = pd.DataFrame(team_macro_json)
team_macro_frame = team_macro_frame.reset_index(drop=True)
team_macro_frame = team_macro_frame[team_macro_frame['Team'] != ""]
team_off_frame = team_macro_frame[team_macro_frame['Data'] == 'Off']
team_off_frame = team_off_frame.reset_index(drop=True)
team_off_frame = team_off_frame.drop(columns=['PLUS_MINUS', 'PF', 'Data'], axis=1)
team_off_frame = team_off_frame.apply(pd.to_numeric, errors='coerce').fillna(team_off_frame)
team_off_frame['PTS/m'] = team_off_frame['PTS'] / team_off_frame['MIN']
team_off_frame['REB/m'] = team_off_frame['REB'] / team_off_frame['MIN']
team_off_frame['AST/m'] = team_off_frame['AST'] / team_off_frame['MIN']
team_off_frame['STL/m'] = team_off_frame['STL'] / team_off_frame['MIN']
team_off_frame['BLK/m'] = team_off_frame['BLK'] / team_off_frame['MIN']
team_def_frame = team_macro_frame[team_macro_frame['Data'] == 'Def']
team_def_frame = team_def_frame.reset_index(drop=True)
team_def_frame = team_def_frame.drop(columns=['PLUS_MINUS', 'PF', 'Data'], axis=1)
team_def_frame = team_def_frame.apply(pd.to_numeric, errors='coerce').fillna(team_def_frame)
team_def_frame['PTS/m'] = team_def_frame['PTS'] / team_def_frame['MIN']
team_def_frame['REB/m'] = team_def_frame['REB'] / team_def_frame['MIN']
team_def_frame['AST/m'] = team_def_frame['AST'] / team_def_frame['MIN']
team_def_frame['STL/m'] = team_def_frame['STL'] / team_def_frame['MIN']
team_def_frame['BLK/m'] = team_def_frame['BLK'] / team_def_frame['MIN']
team_combo_json = requests.get(NBA_DATA + '?sheet=Team%20Combo%20Data').json()
team_combo_frame = pd.DataFrame(team_combo_json)
team_combo_frame = team_combo_frame.reset_index(drop=True)
team_combo_frame = team_combo_frame.apply(pd.to_numeric, errors='coerce').fillna(team_combo_frame)
try:
team_matchup_json = requests.get(NBA_DATA + '?sheet=Team%20Matchups').json()
team_matchup_frame = pd.DataFrame(team_matchup_json)
team_matchup_frame = team_matchup_frame[team_matchup_frame['t_Pts'] != ""]
team_matchup_frame = team_matchup_frame.reset_index(drop=True)
team_matchup_frame = team_matchup_frame.apply(pd.to_numeric, errors='coerce').fillna(team_matchup_frame)
except:
team_matchup_frame = pd.DataFrame()
return team_off_frame, team_def_frame, team_combo_frame, team_matchup_frame, dk_main_slate, fd_main_slate
def convert_df_to_csv(df):
return df.to_csv().encode('utf-8')
selected_tab = st.segmented_control(
"Select Tab",
options=["DEM Matchups", "Team Macro Tables"],
selection_mode='single',
default='DEM Matchups',
width='stretch',
label_visibility='collapsed',
key='tab_selector'
)
if selected_tab == 'DEM Matchups':
overall_dem, matchups, matchups_dict = init_DEM()
col1, col2 = st.columns([1, 9])
with col1:
if st.button("Reset Data", key='reset1'):
st.cache_data.clear()
overall_dem, matchups, matchups_dict = init_DEM()
split_var1 = st.radio("View all teams or just this main slate's matchups?", ('Slate Matchups', 'All'), key='split_var1')
if split_var1 == 'Slate Matchups':
view_var1 = matchups.Opp.values.tolist()
split_var2 = st.radio("Would you like to view all teams or specific ones?", ('All', 'Specific Teams'), key='split_var2')
if split_var2 == 'Specific Teams':
team_var1 = st.multiselect('Which teams would you like to include in the tables?', options = view_var1, key='team_var1')
elif split_var2 == 'All':
team_var1 = view_var1
split_var3 = st.radio("Would you like to view all positions or specific ones?", ('All', 'Specific Positions'), key='split_var3')
if split_var3 == 'Specific Positions':
pos_var1 = st.multiselect('Which teams would you like to include in the tables?', options = overall_dem['position'].unique(), key='pos_var1')
elif split_var3 == 'All':
pos_var1 = overall_dem.position.values.tolist()
if split_var1 == 'All':
split_var2 = st.radio("Would you like to view all teams or specific ones?", ('All', 'Specific Teams'), key='split_var2')
if split_var2 == 'Specific Teams':
team_var1 = st.multiselect('Which teams would you like to include in the tables?', options = overall_dem['Acro'].unique(), key='team_var1')
elif split_var2 == 'All':
team_var1 = overall_dem.Acro.values.tolist()
split_var3 = st.radio("Would you like to view all positions or specific ones?", ('All', 'Specific Positions'), key='split_var3')
if split_var3 == 'Specific Positions':
pos_var1 = st.multiselect('Which teams would you like to include in the tables?', options = overall_dem['position'].unique(), key='pos_var1')
elif split_var3 == 'All':
pos_var1 = overall_dem.position.values.tolist()
with col2:
if split_var1 == 'Slate Matchups':
dem_display = overall_dem[overall_dem['Acro'].isin(view_var1)]
dem_display['Team (Getting Boost)'] = dem_display['Acro'].map(matchups_dict)
dem_display.rename(columns={"Acro": "Opp (Giving Boost)"}, inplace = True)
dem_display = dem_display[['Team (Getting Boost)', 'Opp (Giving Boost)', 'G', 'Pts% Boost', 'Reb% Boost', 'Ast% Boost', '3p% Boost',
'Stl Boost%', 'Blk Boost%', 'TOV Boost%', 'FPPM', 'FPPM Boost', 'Team FPPM Boost', 'position']]
dem_display = dem_display[dem_display['Team (Getting Boost)'].isin(team_var1)]
dem_display = dem_display[dem_display['position'].isin(pos_var1)]
dem_display = dem_display.sort_values(by='FPPM Boost', ascending=False)
elif split_var1 == 'All':
dem_display = overall_dem[overall_dem['Acro'].isin(team_var1)]
dem_display = dem_display[dem_display['position'].isin(pos_var1)]
dem_display = dem_display.sort_values(by='FPPM Boost', ascending=False)
dem_display.rename(columns={"Team": "Team (Giving Boost)"}, inplace = True)
dem_display = dem_display.set_index('Team (Giving Boost)')
st.dataframe(dem_display.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(percentages_format, precision=2), use_container_width = True)
st.download_button(
label="Export DEM Numbers",
data=convert_df_to_csv(overall_dem),
file_name='DEM_export.csv',
mime='text/csv',
)
if selected_tab == 'Team Macro Tables':
team_off_frame, team_def_frame, team_combo_frame, team_matchup_frame, dk_main_slate, fd_main_slate = init_macro_tables()
col1, col2 = st.columns([1, 9])
with col1:
if st.button("Reset Data", key='reset2'):
st.cache_data.clear()
team_off_frame, team_def_frame, team_combo_frame, team_matchup_frame, dk_main_slate, fd_main_slate = init_macro_tables()
macro_split_var = st.radio("View all teams or just this main slate's matchups?", ('DK Main Slate', 'FD Main Slate', 'All'), key='macro_split_var')
if macro_split_var == 'DK Main Slate':
macro_view_var = dk_main_slate
if macro_split_var == 'FD Main Slate':
macro_view_var = fd_main_slate
if macro_split_var == 'All':
macro_view_var = team_off_frame.Team.values.tolist()
table_var2 = st.selectbox("Select Table", options=['Team Offense', 'Team Defense', 'Team Combo', 'Team Matchups'], key='table_var2')
display_type2 = st.selectbox("Select Display Type", options=['Minutes', 'Total'], key='display_type2')
with col2:
if table_var2 == 'Team Offense':
if display_type2 == 'Minutes':
team_macro_display = team_off_frame.loc[:, team_off_frame.columns.isin(macro_minutes_cols)]
team_macro_display = team_macro_display[team_macro_display['Team'].isin(macro_view_var)]
team_macro_display = team_macro_display[['Team', 'Games', 'MIN', 'FGA/m', 'FG3A/m', 'FTA/m', 'PTS/m', 'REB/m', 'AST/m', 'STL/m', 'BLK/m', 'Fantasy/m', 'FD_Fantasy/m']]
elif display_type2 == 'Total':
team_macro_display = team_off_frame.loc[:, ~team_off_frame.columns.isin(remove_minutes_cols)]
team_macro_display = team_macro_display[team_macro_display['Team'].isin(macro_view_var)]
elif table_var2 == 'Team Defense':
if display_type2 == 'Minutes':
team_macro_display = team_def_frame.loc[:, team_def_frame.columns.isin(macro_minutes_cols)]
team_macro_display = team_macro_display[team_macro_display['Team'].isin(macro_view_var)]
team_macro_display = team_macro_display[['Team', 'Games', 'MIN', 'FGA/m', 'FG3A/m', 'FTA/m', 'PTS/m', 'REB/m', 'AST/m', 'STL/m', 'BLK/m', 'Fantasy/m', 'FD_Fantasy/m']]
elif display_type2 == 'Total':
team_macro_display = team_def_frame.loc[:, ~team_def_frame.columns.isin(remove_minutes_cols)]
team_macro_display = team_macro_display[team_macro_display['Team'].isin(macro_view_var)]
elif table_var2 == 'Team Combo':
team_macro_display = team_combo_frame
team_macro_display = team_macro_display[team_macro_display['Team'].isin(macro_view_var)]
elif table_var2 == 'Team Matchups':
team_macro_display = team_matchup_frame
team_macro_display = team_macro_display[team_macro_display['Team'].isin(macro_view_var)]
st.dataframe(team_macro_display.set_index('Team').style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(team_macro_format, precision=2), use_container_width = True)
st.download_button(
label="Export Table Data",
data=convert_df_to_csv(team_macro_display),
file_name='Team_Macro_export.csv',
mime='text/csv',
)