testing_vorp_logic / src /streamlit_app.py
James McCool
Implement Fantasy Football VORP Calculator with user-configurable league settings and data loading from external APIs. Added functions for calculating player rankings, replacement values, and VORP metrics. Enhanced user interface for league configuration and data display.
3573b17
raw
history blame
15.8 kB
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:
rb_flex_pct = st.sidebar.slider("RB Flex %", 0.0, 1.0, type_flex_percentiles[league_type]['RB'], 0.01)
wr_flex_pct = st.sidebar.slider("WR Flex %", 0.0, 1.0, type_flex_percentiles[league_type]['WR'], 0.01)
te_flex_pct = st.sidebar.slider("TE Flex %", 0.0, 1.0, type_flex_percentiles[league_type]['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")
qb_mult = st.sidebar.number_input("QB Multiplier", min_value=1.0, max_value=5.0, value=float(flex_multipliers[league_type]['QB']), step=0.5)
rb_mult = st.sidebar.number_input("RB Multiplier", min_value=1.0, max_value=5.0, value=float(flex_multipliers[league_type]['RB']), step=0.5)
wr_mult = st.sidebar.number_input("WR Multiplier", min_value=1.0, max_value=5.0, value=float(flex_multipliers[league_type]['WR']), step=0.5)
te_mult = st.sidebar.number_input("TE Multiplier", min_value=1.0, max_value=5.0, value=float(flex_multipliers[league_type]['TE']), 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")
qb_vorp_lim = st.sidebar.slider("QB VORP Limiter", 0.0, 1.0, pos_vorp_limiters[league_type]['QB'], 0.01)
rb_vorp_lim = st.sidebar.slider("RB VORP Limiter", 0.0, 1.0, pos_vorp_limiters[league_type]['RB'], 0.01)
wr_vorp_lim = st.sidebar.slider("WR VORP Limiter", 0.0, 1.0, pos_vorp_limiters[league_type]['WR'], 0.01)
te_vorp_lim = st.sidebar.slider("TE VORP Limiter", 0.0, 1.0, pos_vorp_limiters[league_type]['TE'], 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']
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']
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']
for positions in ['QB', 'RB', 'WR', 'TE']:
vorp_frame.loc[vorp_frame['Pos'] == positions, 'Rank_Adjust'] = (vorp_frame['Rank'] - (vorp_frame['vorp_diff'] * pos_vorp_limiters[league_settings['TYPE']][positions])).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()