Spaces:
Running
Running
James McCool
Refactor user configuration for fantasy football settings to support default values for flex percentages, multipliers, and VORP limiters. Enhanced handling of both nested and flat dictionary structures for improved flexibility in league settings.
6a4d277
import pandas as pd | |
import numpy as np | |
import requests | |
import math | |
import streamlit as st | |
Dwain_proj = 'https://sheetdb.io/api/v1/svino07zkd6j6?sheet=2025_NFL_Proj_Dist' | |
dwain_ranks = 'https://sheetdb.io/api/v1/ax8b1ms11bbzt?sheet=Dwain_Season' | |
# Default configuration dictionaries | |
type_flex_percentiles = { | |
'Half PPR': { | |
'RB': .40, | |
'WR': .55, | |
'TE': .05, | |
}, | |
'PPR': { | |
'RB': .40, | |
'WR': .55, | |
'TE': .05, | |
}, | |
'Standard': { | |
'RB': .40, | |
'WR': .55, | |
'TE': .05, | |
}, | |
'Superflex': { | |
'QB': .95, | |
'RB': .02, | |
'WR': .03, | |
'TE': .00, | |
}, | |
'TE Premium': { | |
'RB': .35, | |
'WR': .50, | |
'TE': .15, | |
} | |
} | |
pos_vorp_limiters = { | |
'PPR': { | |
'QB': .5, | |
'RB': .75, | |
'WR': .75, | |
'TE': .5, | |
}, | |
'Standard': { | |
'QB': .25, | |
'RB': .75, | |
'WR': .75, | |
'TE': .5, | |
}, | |
'Superflex': { | |
'QB': .5, | |
'RB': .75, | |
'WR': .75, | |
'TE': .5, | |
}, | |
'TE Premium': { | |
'QB': .5, | |
'RB': .75, | |
'WR': .75, | |
'TE': .5, | |
}, | |
'Half PPR': { | |
'QB': .5, | |
'RB': .75, | |
'WR': .75, | |
'TE': .5, | |
}, | |
} | |
flex_multipliers = { | |
'Half PPR': { | |
'QB': 2, | |
'RB': 2, | |
'WR': 2, | |
'TE': 2, | |
}, | |
'PPR': { | |
'QB': 2, | |
'RB': 2, | |
'WR': 2, | |
'TE': 2, | |
}, | |
'Standard': { | |
'QB': 2, | |
'RB': 2, | |
'WR': 2, | |
'TE': 2, | |
}, | |
'Superflex': { | |
'QB': 4, | |
'RB': 2, | |
'WR': 2, | |
'TE': 2, | |
}, | |
'TE Premium': { | |
'QB': 2, | |
'RB': 2, | |
'WR': 2, | |
'TE': 2, | |
}, | |
} | |
base_settings = { | |
'TEAMS': 12, | |
'QB': 1, | |
'RB': 2, | |
'WR': 3, | |
'TE': 1, | |
'FLEX': 1, | |
'BENCH': 6, | |
'TYPE': 'Half PPR' | |
} | |
league_settings = { | |
'TEAMS': 12, | |
'QB': 1, | |
'RB': 2, | |
'WR': 3, | |
'TE': 1, | |
'FLEX': 2, | |
'BENCH': 6, | |
'TYPE': 'Superflex' | |
} | |
def create_user_config_interface(): | |
"""Create Streamlit interface for user configuration""" | |
st.sidebar.header("League Configuration") | |
# League Type Selection | |
league_type = st.sidebar.selectbox( | |
"League Type", | |
['Half PPR', 'PPR', 'Standard', 'Superflex', 'TE Premium'], | |
index=0 | |
) | |
# League Settings | |
st.sidebar.subheader("League Settings") | |
teams = st.sidebar.number_input("Number of Teams", min_value=8, max_value=16, value=12) | |
qb_starters = st.sidebar.number_input("QB Starters", min_value=1, max_value=2, value=1) | |
rb_starters = st.sidebar.number_input("RB Starters", min_value=1, max_value=3, value=2) | |
wr_starters = st.sidebar.number_input("WR Starters", min_value=1, max_value=4, value=3) | |
te_starters = st.sidebar.number_input("TE Starters", min_value=1, max_value=2, value=1) | |
flex_spots = st.sidebar.number_input("Flex Spots", min_value=0, max_value=3, value=1) | |
bench_spots = st.sidebar.number_input("Bench Spots", min_value=0, max_value=10, value=6) | |
# Update league settings based on user input | |
user_league_settings = { | |
'TEAMS': teams, | |
'QB': qb_starters, | |
'RB': rb_starters, | |
'WR': wr_starters, | |
'TE': te_starters, | |
'FLEX': flex_spots, | |
'BENCH': bench_spots, | |
'TYPE': league_type | |
} | |
# Flex Percentiles Configuration | |
st.sidebar.subheader("Flex Position Percentiles") | |
if league_type == 'Superflex': | |
qb_flex_pct = st.sidebar.slider("QB Flex %", 0.0, 1.0, 0.95, 0.01) | |
rb_flex_pct = st.sidebar.slider("RB Flex %", 0.0, 1.0, 0.02, 0.01) | |
wr_flex_pct = st.sidebar.slider("WR Flex %", 0.0, 1.0, 0.03, 0.01) | |
te_flex_pct = st.sidebar.slider("TE Flex %", 0.0, 1.0, 0.00, 0.01) | |
user_flex_percentiles = { | |
'QB': qb_flex_pct, | |
'RB': rb_flex_pct, | |
'WR': wr_flex_pct, | |
'TE': te_flex_pct, | |
} | |
else: | |
# Get default values for the selected league type | |
default_rb = type_flex_percentiles.get(league_type, {}).get('RB', 0.4) | |
default_wr = type_flex_percentiles.get(league_type, {}).get('WR', 0.55) | |
default_te = type_flex_percentiles.get(league_type, {}).get('TE', 0.05) | |
rb_flex_pct = st.sidebar.slider("RB Flex %", 0.0, 1.0, default_rb, 0.01) | |
wr_flex_pct = st.sidebar.slider("WR Flex %", 0.0, 1.0, default_wr, 0.01) | |
te_flex_pct = st.sidebar.slider("TE Flex %", 0.0, 1.0, default_te, 0.01) | |
user_flex_percentiles = { | |
'RB': rb_flex_pct, | |
'WR': wr_flex_pct, | |
'TE': te_flex_pct, | |
} | |
# Flex Multipliers Configuration | |
st.sidebar.subheader("Position Multipliers") | |
default_qb_mult = flex_multipliers.get(league_type, {}).get('QB', 2.0) | |
default_rb_mult = flex_multipliers.get(league_type, {}).get('RB', 2.0) | |
default_wr_mult = flex_multipliers.get(league_type, {}).get('WR', 2.0) | |
default_te_mult = flex_multipliers.get(league_type, {}).get('TE', 2.0) | |
qb_mult = st.sidebar.number_input("QB Multiplier", min_value=1.0, max_value=5.0, value=float(default_qb_mult), step=0.5) | |
rb_mult = st.sidebar.number_input("RB Multiplier", min_value=1.0, max_value=5.0, value=float(default_rb_mult), step=0.5) | |
wr_mult = st.sidebar.number_input("WR Multiplier", min_value=1.0, max_value=5.0, value=float(default_wr_mult), step=0.5) | |
te_mult = st.sidebar.number_input("TE Multiplier", min_value=1.0, max_value=5.0, value=float(default_te_mult), step=0.5) | |
user_flex_multipliers = { | |
'QB': qb_mult, | |
'RB': rb_mult, | |
'WR': wr_mult, | |
'TE': te_mult, | |
} | |
# VORP Limiters Configuration | |
st.sidebar.subheader("VORP Rank Adjustments") | |
default_qb_vorp = pos_vorp_limiters.get(league_type, {}).get('QB', 0.5) | |
default_rb_vorp = pos_vorp_limiters.get(league_type, {}).get('RB', 0.75) | |
default_wr_vorp = pos_vorp_limiters.get(league_type, {}).get('WR', 0.75) | |
default_te_vorp = pos_vorp_limiters.get(league_type, {}).get('TE', 0.5) | |
qb_vorp_lim = st.sidebar.slider("QB VORP Limiter", 0.0, 1.0, default_qb_vorp, 0.01) | |
rb_vorp_lim = st.sidebar.slider("RB VORP Limiter", 0.0, 1.0, default_rb_vorp, 0.01) | |
wr_vorp_lim = st.sidebar.slider("WR VORP Limiter", 0.0, 1.0, default_wr_vorp, 0.01) | |
te_vorp_lim = st.sidebar.slider("TE VORP Limiter", 0.0, 1.0, default_te_vorp, 0.01) | |
user_pos_vorp_limiters = { | |
'QB': qb_vorp_lim, | |
'RB': rb_vorp_lim, | |
'WR': wr_vorp_lim, | |
'TE': te_vorp_lim, | |
} | |
return user_league_settings, user_flex_percentiles, user_flex_multipliers, user_pos_vorp_limiters | |
def load_projections_data(api: str) -> pd.DataFrame: | |
calc_columns = ['Ru Yds', 'Ru TDs', 'Rec', 'Rec Yds', 'Rec TDs', 'P Yds', 'P TDs', 'INTs'] | |
ppr_values = [.1, 6, 1, .1, 6, .04, 4, -1] | |
halfPpr_values = [.1, 6, .5, .1, 6, .04, 4, -1] | |
standard_values = [.1, 6, 0, .1, 6, .04, 4, -1] | |
init_data = requests.get(api) | |
proj_dataframe = pd.DataFrame(init_data.json()) | |
for col in calc_columns: | |
proj_dataframe[col] = proj_dataframe[col].astype(float) | |
proj_dataframe['halfPpr'] = proj_dataframe[calc_columns].dot(halfPpr_values) | |
proj_dataframe['ppr'] = proj_dataframe[calc_columns].dot(ppr_values) | |
proj_dataframe['standard'] = proj_dataframe[calc_columns].dot(standard_values) | |
fpts_df = proj_dataframe[['Name', 'SR_ID', 'Pos', 'halfPpr', 'ppr', 'standard']] | |
return fpts_df | |
def load_ranks_data(api: str) -> pd.DataFrame: | |
init_data = requests.get(api) | |
ranks_dataframe = pd.DataFrame(init_data.json()) | |
ranks_dict = dict(zip(ranks_dataframe['SR_ID'], ranks_dataframe['Rank'])) | |
return ranks_dict | |
def create_position_frames(frame: pd.DataFrame, ranks: dict) -> pd.DataFrame: | |
qb_frame = frame[frame['Pos'] == 'QB'].sort_values(by='halfPpr', ascending=False) | |
rb_frame = frame[frame['Pos'] == 'RB'].sort_values(by='halfPpr', ascending=False) | |
wr_frame = frame[frame['Pos'] == 'WR'].sort_values(by='halfPpr', ascending=False) | |
te_frame = frame[frame['Pos'] == 'TE'].sort_values(by='halfPpr', ascending=False) | |
for slice in [qb_frame, rb_frame, wr_frame, te_frame]: | |
slice['Rank'] = slice['SR_ID'].map(ranks).replace(np.nan, 0).astype(int) | |
slice = slice[slice['Rank'] != 0] | |
slice = slice.sort_values(by='Rank', ascending=True) | |
overall_frame = pd.concat([qb_frame, rb_frame, wr_frame, te_frame]).reset_index(drop=True) | |
return overall_frame | |
def designate_custom_position_reqs(league_settings: dict, flex_percentiles: dict, flex_multipliers: dict) -> dict: | |
qb_base = league_settings['QB'] * league_settings['TEAMS'] | |
rb_base = league_settings['RB'] * league_settings['TEAMS'] | |
wr_base = league_settings['WR'] * league_settings['TEAMS'] | |
te_base = league_settings['TE'] * league_settings['TEAMS'] | |
# Handle both nested and flat dictionary structures | |
if 'QB' in flex_percentiles: | |
# User configuration (flat structure) | |
qb_rv_index = math.ceil((qb_base) * flex_multipliers['QB']) | |
rb_rv_index = math.ceil((rb_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles['RB'])) * flex_multipliers['RB']) | |
wr_rv_index = math.ceil((wr_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles['WR'])) * flex_multipliers['WR']) | |
te_rv_index = math.ceil((te_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles['TE'])) * flex_multipliers['TE']) | |
else: | |
# Default configuration (nested structure) | |
qb_rv_index = math.ceil((qb_base) * flex_multipliers[league_settings['TYPE']]['QB']) | |
rb_rv_index = math.ceil((rb_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles[league_settings['TYPE']]['RB'])) * flex_multipliers[league_settings['TYPE']]['RB']) | |
wr_rv_index = math.ceil((wr_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles[league_settings['TYPE']]['WR'])) * flex_multipliers[league_settings['TYPE']]['WR']) | |
te_rv_index = math.ceil((te_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles[league_settings['TYPE']]['TE'])) * flex_multipliers[league_settings['TYPE']]['TE']) | |
print(f"Need {qb_rv_index} for QB in {league_settings['TEAMS']} teams with type {league_settings['TYPE']}") | |
print(f"Need {rb_rv_index} for RB in {league_settings['TEAMS']} teams with type {league_settings['TYPE']}") | |
print(f"Need {wr_rv_index} for WR in {league_settings['TEAMS']} teams with type {league_settings['TYPE']}") | |
print(f"Need {te_rv_index} for TE in {league_settings['TEAMS']} teams with type {league_settings['TYPE']}") | |
pos_reqs = { | |
'QB': qb_rv_index, | |
'RB': rb_rv_index, | |
'WR': wr_rv_index, | |
'TE': te_rv_index, | |
} | |
return pos_reqs | |
def designate_base_position_reqs(league_settings: dict, flex_percentiles: dict, flex_multipliers: dict) -> dict: | |
qb_base = league_settings['QB'] * league_settings['TEAMS'] | |
rb_base = league_settings['RB'] * league_settings['TEAMS'] | |
wr_base = league_settings['WR'] * league_settings['TEAMS'] | |
te_base = league_settings['TE'] * league_settings['TEAMS'] | |
# Handle both nested and flat dictionary structures | |
if 'QB' in flex_percentiles: | |
# User configuration (flat structure) | |
qb_rv_index = math.ceil(qb_base * flex_multipliers['QB']) | |
rb_rv_index = math.ceil((rb_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles['RB'])) * flex_multipliers['RB']) | |
wr_rv_index = math.ceil((wr_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles['WR'])) * flex_multipliers['WR']) | |
te_rv_index = math.ceil((te_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles['TE'])) * flex_multipliers['TE']) | |
else: | |
# Default configuration (nested structure) | |
qb_rv_index = math.ceil(qb_base * flex_multipliers[league_settings['TYPE']]['QB']) | |
rb_rv_index = math.ceil((rb_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles[league_settings['TYPE']]['RB'])) * flex_multipliers[league_settings['TYPE']]['RB']) | |
wr_rv_index = math.ceil((wr_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles[league_settings['TYPE']]['WR'])) * flex_multipliers[league_settings['TYPE']]['WR']) | |
te_rv_index = math.ceil((te_base + ((league_settings['TEAMS'] * league_settings['FLEX']) * flex_percentiles[league_settings['TYPE']]['TE'])) * flex_multipliers[league_settings['TYPE']]['TE']) | |
print(f"Need {qb_rv_index} for QB in {league_settings['TEAMS']} teams with type {league_settings['TYPE']}") | |
print(f"Need {rb_rv_index} for RB in {league_settings['TEAMS']} teams with type {league_settings['TYPE']}") | |
print(f"Need {wr_rv_index} for WR in {league_settings['TEAMS']} teams with type {league_settings['TYPE']}") | |
print(f"Need {te_rv_index} for TE in {league_settings['TEAMS']} teams with type {league_settings['TYPE']}") | |
pos_reqs = { | |
'QB': qb_rv_index, | |
'RB': rb_rv_index, | |
'WR': wr_rv_index, | |
'TE': te_rv_index, | |
} | |
return pos_reqs | |
def create_halfPpr_rv(frame: pd.DataFrame, pos_reqs: dict) -> dict: | |
rv_dict = {} | |
for positions in ['QB', 'RB', 'WR', 'TE']: | |
rv_dict[f'{positions}'] = frame[frame['Pos'] == positions].head(pos_reqs[positions]).reset_index(drop=True)['halfPpr'].tail(1).values[0] | |
return rv_dict | |
def create_custom_rv(frame: pd.DataFrame, pos_reqs: dict, league_settings: dict) -> dict: | |
if league_settings['TYPE'] == 'Half PPR': | |
rv_type = 'halfPpr' | |
elif league_settings['TYPE'] == 'PPR': | |
rv_type = 'ppr' | |
elif league_settings['TYPE'] == 'Standard': | |
rv_type = 'standard' | |
elif league_settings['TYPE'] == 'Superflex': | |
rv_type = 'halfPpr' | |
rv_dict = {} | |
for positions in ['QB', 'RB', 'WR', 'TE']: | |
rv_dict[f'{positions}'] = frame[frame['Pos'] == positions].head(pos_reqs[positions]).reset_index(drop=True)[rv_type].tail(1).values[0] | |
return rv_dict | |
def assign_vorp(frame: pd.DataFrame, halfPpr_rv: dict, custom_rv: dict, league_settings: dict, pos_vorp_limiters: dict) -> pd.DataFrame: | |
if league_settings['TYPE'] == 'Half PPR': | |
rv_type = 'halfPpr' | |
elif league_settings['TYPE'] == 'PPR': | |
rv_type = 'ppr' | |
elif league_settings['TYPE'] == 'Standard': | |
rv_type = 'standard' | |
elif league_settings['TYPE'] == 'Superflex': | |
rv_type = 'halfPpr' | |
vorp_frame = pd.DataFrame() | |
for positions in ['QB', 'RB', 'WR', 'TE']: | |
pos_frame = frame[frame['Pos'] == positions] | |
pos_frame = pos_frame[pos_frame['Rank'] != 0].reset_index(drop=True) | |
pos_frame = pos_frame.sort_values(by='Rank', ascending=True) | |
pos_frame['halfPpr_rv'] = halfPpr_rv[positions] | |
pos_frame['custom_rv'] = custom_rv[positions] | |
pos_frame['halfPpr_VORP'] = pos_frame['halfPpr'] - halfPpr_rv[positions] | |
pos_frame['custom_VORP'] = pos_frame[rv_type] - custom_rv[positions] | |
vorp_frame = pd.concat([vorp_frame, pos_frame]).reset_index(drop=True) | |
vorp_frame['halfPpr_vorp_rank'] = vorp_frame['halfPpr_VORP'].rank(method='max', ascending=False) | |
vorp_frame['custom_vorp_rank'] = vorp_frame['custom_VORP'].rank(method='max', ascending=False) | |
vorp_frame['vorp_diff'] = vorp_frame['halfPpr_vorp_rank'] - vorp_frame['custom_vorp_rank'] | |
# Handle both nested and flat dictionary structures | |
for positions in ['QB', 'RB', 'WR', 'TE']: | |
if 'QB' in pos_vorp_limiters: | |
# User configuration (flat structure) | |
limiter = pos_vorp_limiters[positions] | |
else: | |
# Default configuration (nested structure) | |
limiter = pos_vorp_limiters[league_settings['TYPE']][positions] | |
vorp_frame.loc[vorp_frame['Pos'] == positions, 'Rank_Adjust'] = (vorp_frame['Rank'] - (vorp_frame['vorp_diff'] * limiter)).astype(float) | |
vorp_frame['custom_rank'] = vorp_frame['Rank_Adjust'].rank(method='first', ascending=True) | |
print(vorp_frame.sort_values(by='custom_vorp_rank', ascending=True).head(50)) | |
return vorp_frame.sort_values(by='custom_rank', ascending=True) | |
def main(): | |
st.title("Fantasy Football VORP Calculator") | |
st.write("Configure your league settings and analyze player values") | |
# Get user configuration | |
user_league_settings, user_flex_percentiles, user_flex_multipliers, user_pos_vorp_limiters = create_user_config_interface() | |
# Load data | |
try: | |
projections_df = load_projections_data(Dwain_proj) | |
ranks_dict = load_ranks_data(dwain_ranks) | |
# Create position frames | |
position_df = create_position_frames(projections_df, ranks_dict) | |
# Calculate position requirements | |
pos_reqs = designate_custom_position_reqs(user_league_settings, user_flex_percentiles, user_flex_multipliers) | |
# Calculate replacement values | |
halfPpr_rv = create_halfPpr_rv(position_df, pos_reqs) | |
custom_rv = create_custom_rv(position_df, pos_reqs, user_league_settings) | |
# Calculate VORP and rankings | |
final_df = assign_vorp(position_df, halfPpr_rv, custom_rv, user_league_settings, user_pos_vorp_limiters) | |
# Display results | |
st.header("Player Rankings") | |
st.dataframe(final_df[['Name', 'Pos', 'Rank', 'custom_rank', 'halfPpr', 'custom_VORP', 'halfPpr_VORP']].head(50)) | |
# Position breakdown | |
st.header("Position Breakdown") | |
for pos in ['QB', 'RB', 'WR', 'TE']: | |
pos_df = final_df[final_df['Pos'] == pos].head(20) | |
st.subheader(f"Top {pos}s") | |
st.dataframe(pos_df[['Name', 'Rank', 'custom_rank', 'halfPpr', 'custom_VORP']]) | |
except Exception as e: | |
st.error(f"Error loading data: {str(e)}") | |
st.info("Please check your internet connection and try again.") | |
if __name__ == "__main__": | |
main() | |