James McCool
commited on
Commit
·
1748ccd
1
Parent(s):
89f3a60
Add stack exposure calculations and refactor player exposure handling in app.py
Browse files- Introduced the create_stack_exposures function to calculate stack exposures, enhancing data analysis capabilities.
- Updated app.py to utilize the new function, improving the organization and clarity of stack exposure data presentation.
- Refactored player exposure handling to incorporate new session state variables for field player and stack frames, ensuring consistent data management across user interactions.
- app.py +33 -57
- global_func/clean_player_name.py +0 -16
- global_func/create_stack_exposures.py +33 -0
- global_func/find_csv_mismatches.py +0 -93
- global_func/highlight_rows.py +0 -29
- global_func/load_csv.py +0 -24
- global_func/load_ss_file.py +0 -34
- global_func/optimize_lineup.py +0 -74
- global_func/predict_dupes.py +0 -188
app.py
CHANGED
|
@@ -11,6 +11,7 @@ from global_func.load_contest_file import load_contest_file
|
|
| 11 |
from global_func.load_file import load_file
|
| 12 |
from global_func.find_name_mismatches import find_name_mismatches
|
| 13 |
from global_func.create_player_exposures import create_player_exposures
|
|
|
|
| 14 |
|
| 15 |
player_exposure_format = {'Exposure Overall': '{:.2%}', 'Exposure Top 1%': '{:.2%}', 'Exposure Top 5%': '{:.2%}', 'Exposure Top 10%': '{:.2%}', 'Exposure Top 20%': '{:.2%}'}
|
| 16 |
if 'calc_toggle' not in st.session_state:
|
|
@@ -171,6 +172,8 @@ with tab2:
|
|
| 171 |
working_df['percentile_finish'] = working_df['index'].rank(pct=True)
|
| 172 |
working_df['finish'] = working_df['index']
|
| 173 |
working_df = working_df.drop(['sorted', 'index'], axis=1)
|
|
|
|
|
|
|
| 174 |
|
| 175 |
with st.expander("Info and filters"):
|
| 176 |
if st.button('Clear data', key='reset3'):
|
|
@@ -237,81 +240,54 @@ with tab2:
|
|
| 237 |
with st.container():
|
| 238 |
tab1, tab2, tab3 = st.tabs(['Player Used Info', 'Stack Used Info', 'Duplication Info'])
|
| 239 |
with tab1:
|
| 240 |
-
st.
|
|
|
|
| 241 |
if entry_parse_var == 'All':
|
| 242 |
st.session_state['player_frame'] = create_player_exposures(working_df, player_columns)
|
| 243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
sort_values(by='Exposure Overall', ascending=False).
|
| 245 |
style.background_gradient(cmap='RdYlGn').
|
| 246 |
-
format(formatter='{:.2%}', subset=
|
| 247 |
hide_index=True)
|
| 248 |
else:
|
| 249 |
st.session_state['player_frame'] = create_player_exposures(working_df, player_columns, entry_names)
|
| 250 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
sort_values(by='Exposure Overall', ascending=False).
|
| 252 |
style.background_gradient(cmap='RdYlGn').
|
| 253 |
-
format(formatter='{:.2%}', subset=
|
| 254 |
hide_index=True)
|
| 255 |
with tab2:
|
|
|
|
|
|
|
| 256 |
if entry_parse_var == 'All':
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
stacks_len_1per = len(working_df[working_df['percentile_finish'] <= 0.01])
|
| 264 |
-
stacks_len_5per = len(working_df[working_df['percentile_finish'] <= 0.05])
|
| 265 |
-
stacks_len_10per = len(working_df[working_df['percentile_finish'] <= 0.10])
|
| 266 |
-
stacks_len_20per = len(working_df[working_df['percentile_finish'] <= 0.20])
|
| 267 |
-
each_set_name = ['Overall', ' Top 1%', ' Top 5%', 'Top 10%', 'Top 20%']
|
| 268 |
-
each_stacks_set = [overall_stacks, top_1per_stacks, top_5per_stacks, top_10per_stacks, top_20per_stacks]
|
| 269 |
-
each_stacks_len_set = [stacks_contest_len, stacks_len_1per, stacks_len_5per, stacks_len_10per, stacks_len_20per]
|
| 270 |
-
stack_count_var = 0
|
| 271 |
-
for each_stack in each_stacks_set:
|
| 272 |
-
stack_frame = each_stack.to_frame().reset_index().rename(columns={'index': 'Stack', 'count': 'Count'})
|
| 273 |
-
stack_frame['Percent'] = stack_frame['Count'] / each_stacks_len_set[stack_count_var]
|
| 274 |
-
stack_frame = stack_frame[['Stack', 'Percent']]
|
| 275 |
-
stack_frame = stack_frame.rename(columns={'Percent': f'Exposure {each_set_name[stack_count_var]}'})
|
| 276 |
-
if 'stack_frame' not in st.session_state:
|
| 277 |
-
st.session_state['stack_frame'] = stack_frame
|
| 278 |
-
else:
|
| 279 |
-
st.session_state['stack_frame'] = pd.merge(st.session_state['stack_frame'], stack_frame, on='Stack', how='outer')
|
| 280 |
-
stack_count_var += 1
|
| 281 |
-
st.dataframe(st.session_state['stack_frame'].
|
| 282 |
sort_values(by='Exposure Overall', ascending=False).
|
| 283 |
style.background_gradient(cmap='RdYlGn').
|
| 284 |
-
format(formatter='{:.2%}', subset=
|
| 285 |
hide_index=True)
|
| 286 |
else:
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
stacks_len_1per = len(working_df[working_df['percentile_finish'] <= 0.01])
|
| 294 |
-
stacks_len_5per = len(working_df[working_df['percentile_finish'] <= 0.05])
|
| 295 |
-
stacks_len_10per = len(working_df[working_df['percentile_finish'] <= 0.10])
|
| 296 |
-
stacks_len_20per = len(working_df[working_df['percentile_finish'] <= 0.20])
|
| 297 |
-
each_set_name = ['Overall', ' Top 1%', ' Top 5%', 'Top 10%', 'Top 20%']
|
| 298 |
-
each_stacks_set = [overall_stacks, top_1per_stacks, top_5per_stacks, top_10per_stacks, top_20per_stacks]
|
| 299 |
-
each_stacks_len_set = [stacks_contest_len, stacks_len_1per, stacks_len_5per, stacks_len_10per, stacks_len_20per]
|
| 300 |
-
stack_count_var = 0
|
| 301 |
-
for each_stack in each_stacks_set:
|
| 302 |
-
stack_frame = each_stack.to_frame().reset_index().rename(columns={'index': 'Stack', 'count': 'Count'})
|
| 303 |
-
stack_frame['Percent'] = stack_frame['Count'] / each_stacks_len_set[stack_count_var]
|
| 304 |
-
stack_frame = stack_frame[['Stack', 'Percent']]
|
| 305 |
-
stack_frame = stack_frame.rename(columns={'Percent': f'Exposure {each_set_name[stack_count_var]}'})
|
| 306 |
-
if 'stack_frame' not in st.session_state:
|
| 307 |
-
st.session_state['stack_frame'] = stack_frame
|
| 308 |
-
else:
|
| 309 |
-
st.session_state['stack_frame'] = pd.merge(st.session_state['stack_frame'], stack_frame, on='Stack', how='outer')
|
| 310 |
-
stack_count_var += 1
|
| 311 |
-
st.dataframe(st.session_state['stack_frame'].
|
| 312 |
sort_values(by='Exposure Overall', ascending=False).
|
| 313 |
style.background_gradient(cmap='RdYlGn').
|
| 314 |
-
format(formatter='{:.2%}', subset=
|
| 315 |
hide_index=True)
|
| 316 |
with tab3:
|
| 317 |
st.write('holding')
|
|
|
|
| 11 |
from global_func.load_file import load_file
|
| 12 |
from global_func.find_name_mismatches import find_name_mismatches
|
| 13 |
from global_func.create_player_exposures import create_player_exposures
|
| 14 |
+
from global_func.create_stack_exposures import create_stack_exposures
|
| 15 |
|
| 16 |
player_exposure_format = {'Exposure Overall': '{:.2%}', 'Exposure Top 1%': '{:.2%}', 'Exposure Top 5%': '{:.2%}', 'Exposure Top 10%': '{:.2%}', 'Exposure Top 20%': '{:.2%}'}
|
| 17 |
if 'calc_toggle' not in st.session_state:
|
|
|
|
| 172 |
working_df['percentile_finish'] = working_df['index'].rank(pct=True)
|
| 173 |
working_df['finish'] = working_df['index']
|
| 174 |
working_df = working_df.drop(['sorted', 'index'], axis=1)
|
| 175 |
+
st.session_state['field_player_frame'] = create_player_exposures(working_df, player_columns)
|
| 176 |
+
st.session_state['field_stack_frame'] = create_stack_exposures(working_df)
|
| 177 |
|
| 178 |
with st.expander("Info and filters"):
|
| 179 |
if st.button('Clear data', key='reset3'):
|
|
|
|
| 240 |
with st.container():
|
| 241 |
tab1, tab2, tab3 = st.tabs(['Player Used Info', 'Stack Used Info', 'Duplication Info'])
|
| 242 |
with tab1:
|
| 243 |
+
player_view_var = st.radio("View Exposures by:", ['Percentage used', 'Relation to the field'], key='player_view_var')
|
| 244 |
+
|
| 245 |
if entry_parse_var == 'All':
|
| 246 |
st.session_state['player_frame'] = create_player_exposures(working_df, player_columns)
|
| 247 |
+
if player_view_var == 'Percentage used':
|
| 248 |
+
player_frame_display = st.session_state['player_frame']
|
| 249 |
+
elif player_view_var == 'Relation to the field':
|
| 250 |
+
player_frame_display = st.session_state['player_frame'] - st.session_state['field_player_frame']
|
| 251 |
+
st.dataframe(player_frame_display.
|
| 252 |
sort_values(by='Exposure Overall', ascending=False).
|
| 253 |
style.background_gradient(cmap='RdYlGn').
|
| 254 |
+
format(formatter='{:.2%}', subset=player_frame_display.select_dtypes(include=['number']).columns),
|
| 255 |
hide_index=True)
|
| 256 |
else:
|
| 257 |
st.session_state['player_frame'] = create_player_exposures(working_df, player_columns, entry_names)
|
| 258 |
+
if player_view_var == 'Percentage used':
|
| 259 |
+
player_frame_display = st.session_state['player_frame']
|
| 260 |
+
elif player_view_var == 'Relation to the field':
|
| 261 |
+
player_frame_display = st.session_state['player_frame'] - st.session_state['field_player_frame']
|
| 262 |
+
st.dataframe(player_frame_display.
|
| 263 |
sort_values(by='Exposure Overall', ascending=False).
|
| 264 |
style.background_gradient(cmap='RdYlGn').
|
| 265 |
+
format(formatter='{:.2%}', subset=player_frame_display.select_dtypes(include=['number']).columns),
|
| 266 |
hide_index=True)
|
| 267 |
with tab2:
|
| 268 |
+
stack_view_var = st.radio('View Stack Exposures by:', ['Percentage used', 'Relation to the field'], key = 'stack_view_var')
|
| 269 |
+
|
| 270 |
if entry_parse_var == 'All':
|
| 271 |
+
st.session_state['stack_frame'] = create_stack_exposures(working_df)
|
| 272 |
+
if stack_view_var == 'Percentage used':
|
| 273 |
+
stack_frame_display = st.session_state['stack_frame']
|
| 274 |
+
elif stack_view_var == 'Relation to the field':
|
| 275 |
+
stack_frame_display = st.session_state['stack_frame'] - st.session_state['field_stack_frame']
|
| 276 |
+
st.dataframe(stack_frame_display.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
sort_values(by='Exposure Overall', ascending=False).
|
| 278 |
style.background_gradient(cmap='RdYlGn').
|
| 279 |
+
format(formatter='{:.2%}', subset=stack_frame_display.select_dtypes(include=['number']).columns),
|
| 280 |
hide_index=True)
|
| 281 |
else:
|
| 282 |
+
st.session_state['stack_frame'] = create_stack_exposures(working_df, entry_names)
|
| 283 |
+
if stack_view_var == 'Percentage used':
|
| 284 |
+
stack_frame_display = st.session_state['stack_frame']
|
| 285 |
+
elif stack_view_var == 'Relation to the field':
|
| 286 |
+
stack_frame_display = st.session_state['stack_frame'] - st.session_state['field_stack_frame']
|
| 287 |
+
st.dataframe(stack_frame_display.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
sort_values(by='Exposure Overall', ascending=False).
|
| 289 |
style.background_gradient(cmap='RdYlGn').
|
| 290 |
+
format(formatter='{:.2%}', subset=stack_frame_display.select_dtypes(include=['number']).columns),
|
| 291 |
hide_index=True)
|
| 292 |
with tab3:
|
| 293 |
st.write('holding')
|
global_func/clean_player_name.py
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
-
import time
|
| 5 |
-
from fuzzywuzzy import process
|
| 6 |
-
|
| 7 |
-
def clean_player_name(name):
|
| 8 |
-
# Handle colon case first (remove everything before colon)
|
| 9 |
-
if ':' in name:
|
| 10 |
-
name = name.split(':')[1].strip()
|
| 11 |
-
|
| 12 |
-
# Handle parentheses case (remove everything after opening parenthesis)
|
| 13 |
-
if '(' in name:
|
| 14 |
-
name = name.split('(')[0].strip()
|
| 15 |
-
|
| 16 |
-
return name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
global_func/create_stack_exposures.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pandas as pd
|
| 2 |
+
|
| 3 |
+
def create_stack_exposures(df: pd.DataFrame, entrants: list = None):
|
| 4 |
+
stack_exposures = pd.DataFrame()
|
| 5 |
+
if entrants is not None:
|
| 6 |
+
overall_stacks = pd.Series(list(df[df['BaseName'].isin(entrants)]['stack'])).value_counts()
|
| 7 |
+
else:
|
| 8 |
+
overall_stacks = pd.Series(list(df['stack'])).value_counts()
|
| 9 |
+
top_1per_stacks = pd.Series(list(df[df['percentile_finish'] <= 0.01]['stack'])).value_counts()
|
| 10 |
+
top_5per_stacks = pd.Series(list(df[df['percentile_finish'] <= 0.05]['stack'])).value_counts()
|
| 11 |
+
top_10per_stacks = pd.Series(list(df[df['percentile_finish'] <= 0.10]['stack'])).value_counts()
|
| 12 |
+
top_20per_stacks = pd.Series(list(df[df['percentile_finish'] <= 0.20]['stack'])).value_counts()
|
| 13 |
+
stacks_contest_len = len(df)
|
| 14 |
+
stacks_len_1per = len(df[df['percentile_finish'] <= 0.01])
|
| 15 |
+
stacks_len_5per = len(df[df['percentile_finish'] <= 0.05])
|
| 16 |
+
stacks_len_10per = len(df[df['percentile_finish'] <= 0.10])
|
| 17 |
+
stacks_len_20per = len(df[df['percentile_finish'] <= 0.20])
|
| 18 |
+
each_set_name = ['Overall', ' Top 1%', ' Top 5%', 'Top 10%', 'Top 20%']
|
| 19 |
+
each_stacks_set = [overall_stacks, top_1per_stacks, top_5per_stacks, top_10per_stacks, top_20per_stacks]
|
| 20 |
+
each_stacks_len_set = [stacks_contest_len, stacks_len_1per, stacks_len_5per, stacks_len_10per, stacks_len_20per]
|
| 21 |
+
stack_count_var = 0
|
| 22 |
+
for each_stack in each_stacks_set:
|
| 23 |
+
stack_frame = each_stack.to_frame().reset_index().rename(columns={'index': 'Stack', 'count': 'Count'})
|
| 24 |
+
stack_frame['Percent'] = stack_frame['Count'] / each_stacks_len_set[stack_count_var]
|
| 25 |
+
stack_frame = stack_frame[['Stack', 'Percent']]
|
| 26 |
+
stack_frame = stack_frame.rename(columns={'Percent': f'Exposure {each_set_name[stack_count_var]}'})
|
| 27 |
+
if len(stack_exposures) == 0:
|
| 28 |
+
stack_exposures = stack_frame
|
| 29 |
+
else:
|
| 30 |
+
stack_exposures = pd.merge(stack_exposures, stack_frame, on='Stack', how='outer')
|
| 31 |
+
stack_count_var += 1
|
| 32 |
+
|
| 33 |
+
return stack_exposures
|
global_func/find_csv_mismatches.py
DELETED
|
@@ -1,93 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
-
from fuzzywuzzy import process
|
| 5 |
-
|
| 6 |
-
def find_csv_mismatches(csv_df, projections_df):
|
| 7 |
-
# Create copies of the dataframes to avoid modifying the originals
|
| 8 |
-
csv_df = csv_df.copy()
|
| 9 |
-
projections_df = projections_df.copy()
|
| 10 |
-
|
| 11 |
-
if 'Name' not in csv_df.columns:
|
| 12 |
-
st.error("No 'Name' column found in CSV file")
|
| 13 |
-
return csv_df
|
| 14 |
-
|
| 15 |
-
if 'player_names' not in projections_df.columns:
|
| 16 |
-
st.error("No 'player_names' column found in projections file")
|
| 17 |
-
return csv_df
|
| 18 |
-
|
| 19 |
-
# Get unique player names from CSV and projections
|
| 20 |
-
csv_players = set(csv_df['Name'].dropna().unique())
|
| 21 |
-
projection_players = set(projections_df['player_names'].unique())
|
| 22 |
-
projection_players_list = list(csv_players)
|
| 23 |
-
|
| 24 |
-
# Find players in CSV that are missing from projections
|
| 25 |
-
players_missing_from_projections = list(projection_players - csv_players)
|
| 26 |
-
|
| 27 |
-
# Automatically handle 100% matches before starting interactive process
|
| 28 |
-
players_to_process = []
|
| 29 |
-
for player in players_missing_from_projections:
|
| 30 |
-
if not isinstance(player, str):
|
| 31 |
-
st.warning(f"Skipping non-string value: {player}")
|
| 32 |
-
continue
|
| 33 |
-
closest_matches = process.extract(player, projection_players_list, limit=1)
|
| 34 |
-
if closest_matches[0][1] == 100: # If perfect match found
|
| 35 |
-
match_name = closest_matches[0][0]
|
| 36 |
-
# Update CSV DataFrame to use the projection name
|
| 37 |
-
csv_df.loc[csv_df['Name'] == player, 'Name'] = match_name
|
| 38 |
-
st.success(f"Automatically matched '{player}' with '{match_name}' (100% match)")
|
| 39 |
-
else:
|
| 40 |
-
players_to_process.append(player)
|
| 41 |
-
|
| 42 |
-
# Initialize session state for tracking current player if not exists
|
| 43 |
-
if 'csv_current_player_index' not in st.session_state:
|
| 44 |
-
st.session_state.csv_current_player_index = 0
|
| 45 |
-
st.session_state.csv_players_to_process = players_to_process
|
| 46 |
-
|
| 47 |
-
# Display results
|
| 48 |
-
if players_missing_from_projections:
|
| 49 |
-
st.warning("Players in CSV but missing from projections")
|
| 50 |
-
|
| 51 |
-
# Display remaining players
|
| 52 |
-
remaining_players = st.session_state.csv_players_to_process[st.session_state.csv_current_player_index:]
|
| 53 |
-
st.info(f"Remaining players to process ({len(remaining_players)}):\n" +
|
| 54 |
-
"\n".join(f"- {player}" for player in remaining_players))
|
| 55 |
-
|
| 56 |
-
if st.session_state.csv_current_player_index < len(st.session_state.csv_players_to_process):
|
| 57 |
-
current_player = st.session_state.csv_players_to_process[st.session_state.csv_current_player_index]
|
| 58 |
-
|
| 59 |
-
# Find the top 3 closest matches
|
| 60 |
-
closest_matches = process.extract(current_player, projection_players_list, limit=3)
|
| 61 |
-
|
| 62 |
-
st.write(f"**Missing Player {st.session_state.csv_current_player_index + 1} of {len(st.session_state.csv_players_to_process)}:** {current_player}")
|
| 63 |
-
|
| 64 |
-
# Create radio buttons for selection
|
| 65 |
-
options = [f"{match[0]} ({match[1]}%)" for match in closest_matches]
|
| 66 |
-
options.append("None of these")
|
| 67 |
-
|
| 68 |
-
selected_option = st.radio(
|
| 69 |
-
f"Select correct match:",
|
| 70 |
-
options,
|
| 71 |
-
key=f"csv_radio_{current_player}"
|
| 72 |
-
)
|
| 73 |
-
|
| 74 |
-
if st.button("Confirm Selection", key="csv_confirm"):
|
| 75 |
-
if selected_option != "None of these":
|
| 76 |
-
selected_name = selected_option.split(" (")[0]
|
| 77 |
-
# Update CSV DataFrame
|
| 78 |
-
csv_df.loc[csv_df['Name'] == current_player, 'Name'] = selected_name
|
| 79 |
-
st.success(f"Replaced '{current_player}' with '{selected_name}'")
|
| 80 |
-
st.session_state['csv_file'] = csv_df
|
| 81 |
-
|
| 82 |
-
# Move to next player
|
| 83 |
-
st.session_state.csv_current_player_index += 1
|
| 84 |
-
st.rerun()
|
| 85 |
-
else:
|
| 86 |
-
st.success("All players have been processed!")
|
| 87 |
-
# Reset the index for future runs
|
| 88 |
-
st.session_state.csv_current_player_index = 0
|
| 89 |
-
st.session_state.csv_players_to_process = []
|
| 90 |
-
else:
|
| 91 |
-
st.success("All CSV players found in projections!")
|
| 92 |
-
|
| 93 |
-
return csv_df
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
global_func/highlight_rows.py
DELETED
|
@@ -1,29 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
-
import time
|
| 5 |
-
from fuzzywuzzy import process
|
| 6 |
-
|
| 7 |
-
def highlight_changes(row):
|
| 8 |
-
original_row = st.session_state['portfolio'].iloc[row.name]
|
| 9 |
-
colors = [''] * len(row)
|
| 10 |
-
for i, (orig, new) in enumerate(zip(original_row, row)):
|
| 11 |
-
if orig != new:
|
| 12 |
-
colors[i] = 'background-color: yellow'
|
| 13 |
-
return colors
|
| 14 |
-
|
| 15 |
-
def highlight_changes_winners(row):
|
| 16 |
-
original_row = st.session_state['optimized_df_medians'].iloc[row.name]
|
| 17 |
-
colors = [''] * len(row)
|
| 18 |
-
for i, (orig, new) in enumerate(zip(original_row, row)):
|
| 19 |
-
if orig != new:
|
| 20 |
-
colors[i] = 'background-color: aqua'
|
| 21 |
-
return colors
|
| 22 |
-
|
| 23 |
-
def highlight_changes_losers(row):
|
| 24 |
-
original_row = st.session_state['optimized_df_winners'].iloc[row.name]
|
| 25 |
-
colors = [''] * len(row)
|
| 26 |
-
for i, (orig, new) in enumerate(zip(original_row, row)):
|
| 27 |
-
if orig != new:
|
| 28 |
-
colors[i] = 'background-color: darksalmon'
|
| 29 |
-
return colors
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
global_func/load_csv.py
DELETED
|
@@ -1,24 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
-
import time
|
| 5 |
-
from fuzzywuzzy import process
|
| 6 |
-
|
| 7 |
-
def load_csv(upload):
|
| 8 |
-
if upload is not None:
|
| 9 |
-
try:
|
| 10 |
-
if upload.name.endswith('.csv'):
|
| 11 |
-
df = pd.read_csv(upload)
|
| 12 |
-
try:
|
| 13 |
-
df['Name + ID'] = df['Name'] + ' (' + df['ID'].astype(str) + ')'
|
| 14 |
-
except:
|
| 15 |
-
pass
|
| 16 |
-
else:
|
| 17 |
-
st.error('Please upload either a CSV or Excel file')
|
| 18 |
-
return None
|
| 19 |
-
|
| 20 |
-
return df
|
| 21 |
-
except Exception as e:
|
| 22 |
-
st.error(f'Error loading file: {str(e)}')
|
| 23 |
-
return None
|
| 24 |
-
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
global_func/load_ss_file.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
-
import time
|
| 5 |
-
from fuzzywuzzy import process
|
| 6 |
-
|
| 7 |
-
def load_ss_file(lineups, csv_file):
|
| 8 |
-
df = csv_file.copy()
|
| 9 |
-
try:
|
| 10 |
-
name_dict = dict(zip(df['ID'], df['Name']))
|
| 11 |
-
except:
|
| 12 |
-
name_dict = dict(zip(df['Id'], df['Nickname']))
|
| 13 |
-
|
| 14 |
-
# Now load and process the lineups file
|
| 15 |
-
try:
|
| 16 |
-
if lineups.name.endswith('.csv'):
|
| 17 |
-
lineups_df = pd.read_csv(lineups)
|
| 18 |
-
elif lineups.name.endswith(('.xls', '.xlsx')):
|
| 19 |
-
lineups_df = pd.read_excel(lineups)
|
| 20 |
-
else:
|
| 21 |
-
st.error('Please upload either a CSV or Excel file for lineups')
|
| 22 |
-
return None, None
|
| 23 |
-
|
| 24 |
-
export_df = lineups_df.copy()
|
| 25 |
-
|
| 26 |
-
# Map the IDs to names
|
| 27 |
-
for col in lineups_df.columns:
|
| 28 |
-
lineups_df[col] = lineups_df[col].map(name_dict)
|
| 29 |
-
|
| 30 |
-
return export_df, lineups_df
|
| 31 |
-
|
| 32 |
-
except Exception as e:
|
| 33 |
-
st.error(f'Error loading lineups file: {str(e)}')
|
| 34 |
-
return None, None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
global_func/optimize_lineup.py
DELETED
|
@@ -1,74 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
-
import time
|
| 5 |
-
from fuzzywuzzy import process
|
| 6 |
-
|
| 7 |
-
def optimize_lineup(row):
|
| 8 |
-
current_lineup = []
|
| 9 |
-
total_salary = 0
|
| 10 |
-
salary_cap = 50000
|
| 11 |
-
used_players = set()
|
| 12 |
-
|
| 13 |
-
# Convert row to dictionary with roster positions
|
| 14 |
-
roster = {}
|
| 15 |
-
for col, player in zip(row.index, row):
|
| 16 |
-
if col not in ['salary', 'median', 'Own', 'Finish_percentile', 'Dupes', 'Lineup Edge']:
|
| 17 |
-
roster[col] = {
|
| 18 |
-
'name': player,
|
| 19 |
-
'position': map_dict['pos_map'].get(player, '').split('/'),
|
| 20 |
-
'team': map_dict['team_map'].get(player, ''),
|
| 21 |
-
'salary': map_dict['salary_map'].get(player, 0),
|
| 22 |
-
'median': map_dict['proj_map'].get(player, 0),
|
| 23 |
-
'ownership': map_dict['own_map'].get(player, 0)
|
| 24 |
-
}
|
| 25 |
-
total_salary += roster[col]['salary']
|
| 26 |
-
used_players.add(player)
|
| 27 |
-
|
| 28 |
-
# Optimize each roster position in random order
|
| 29 |
-
roster_positions = list(roster.items())
|
| 30 |
-
random.shuffle(roster_positions)
|
| 31 |
-
|
| 32 |
-
for roster_pos, current in roster_positions:
|
| 33 |
-
# Skip optimization for players from removed teams
|
| 34 |
-
if current['team'] in remove_teams_var:
|
| 35 |
-
continue
|
| 36 |
-
|
| 37 |
-
valid_positions = position_rules[roster_pos]
|
| 38 |
-
better_options = []
|
| 39 |
-
|
| 40 |
-
# Find valid replacements for this roster position
|
| 41 |
-
for pos in valid_positions:
|
| 42 |
-
if pos in position_groups:
|
| 43 |
-
pos_options = [
|
| 44 |
-
p for p in position_groups[pos]
|
| 45 |
-
if p['median'] > current['median']
|
| 46 |
-
and (total_salary - current['salary'] + p['salary']) <= salary_cap
|
| 47 |
-
and p['player_names'] not in used_players
|
| 48 |
-
and any(valid_pos in p['positions'] for valid_pos in valid_positions)
|
| 49 |
-
and map_dict['team_map'].get(p['player_names']) not in remove_teams_var # Check team restriction
|
| 50 |
-
]
|
| 51 |
-
better_options.extend(pos_options)
|
| 52 |
-
|
| 53 |
-
if better_options:
|
| 54 |
-
# Remove duplicates
|
| 55 |
-
better_options = {opt['player_names']: opt for opt in better_options}.values()
|
| 56 |
-
|
| 57 |
-
# Sort by median projection and take the best one
|
| 58 |
-
best_replacement = max(better_options, key=lambda x: x['median'])
|
| 59 |
-
|
| 60 |
-
# Update the lineup and tracking variables
|
| 61 |
-
used_players.remove(current['name'])
|
| 62 |
-
used_players.add(best_replacement['player_names'])
|
| 63 |
-
total_salary = total_salary - current['salary'] + best_replacement['salary']
|
| 64 |
-
roster[roster_pos] = {
|
| 65 |
-
'name': best_replacement['player_names'],
|
| 66 |
-
'position': map_dict['pos_map'][best_replacement['player_names']].split('/'),
|
| 67 |
-
'team': map_dict['team_map'][best_replacement['player_names']],
|
| 68 |
-
'salary': best_replacement['salary'],
|
| 69 |
-
'median': best_replacement['median'],
|
| 70 |
-
'ownership': best_replacement['ownership']
|
| 71 |
-
}
|
| 72 |
-
|
| 73 |
-
# Return optimized lineup maintaining original column order
|
| 74 |
-
return [roster[pos]['name'] for pos in row.index if pos in roster]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
global_func/predict_dupes.py
DELETED
|
@@ -1,188 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import numpy as np
|
| 3 |
-
import pandas as pd
|
| 4 |
-
import time
|
| 5 |
-
from fuzzywuzzy import process
|
| 6 |
-
|
| 7 |
-
def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, strength_var):
|
| 8 |
-
if strength_var == 'Weak':
|
| 9 |
-
dupes_multiplier = .75
|
| 10 |
-
percentile_multiplier = .90
|
| 11 |
-
elif strength_var == 'Average':
|
| 12 |
-
dupes_multiplier = 1.00
|
| 13 |
-
percentile_multiplier = 1.00
|
| 14 |
-
elif strength_var == 'Sharp':
|
| 15 |
-
dupes_multiplier = 1.25
|
| 16 |
-
percentile_multiplier = 1.10
|
| 17 |
-
max_ownership = max(maps_dict['own_map'].values()) / 100
|
| 18 |
-
average_ownership = np.mean(list(maps_dict['own_map'].values())) / 100
|
| 19 |
-
if site_var == 'Fanduel':
|
| 20 |
-
if type_var == 'Showdown':
|
| 21 |
-
dup_count_columns = ['CPT_Own_percent_rank', 'FLEX1_Own_percent_rank', 'FLEX2_Own_percent_rank', 'FLEX3_Own_percent_rank', 'FLEX4_Own_percent_rank']
|
| 22 |
-
own_columns = ['CPT_Own', 'FLEX1_Own', 'FLEX2_Own', 'FLEX3_Own', 'FLEX4_Own']
|
| 23 |
-
calc_columns = ['own_product', 'own_average', 'own_sum', 'avg_own_rank', 'dupes_calc', 'low_own_count', 'own_ratio', 'Ref_Proj', 'Max_Proj', 'Min_Proj', 'Avg_Ref', 'own_ratio']
|
| 24 |
-
flex_ownerships = pd.concat([
|
| 25 |
-
portfolio.iloc[:,1].map(maps_dict['own_map']),
|
| 26 |
-
portfolio.iloc[:,2].map(maps_dict['own_map']),
|
| 27 |
-
portfolio.iloc[:,3].map(maps_dict['own_map']),
|
| 28 |
-
portfolio.iloc[:,4].map(maps_dict['own_map'])
|
| 29 |
-
])
|
| 30 |
-
flex_rank = flex_ownerships.rank(pct=True)
|
| 31 |
-
|
| 32 |
-
# Assign ranks back to individual columns using the same rank scale
|
| 33 |
-
portfolio['CPT_Own_percent_rank'] = portfolio.iloc[:,0].map(maps_dict['cpt_own_map']).rank(pct=True)
|
| 34 |
-
portfolio['FLEX1_Own_percent_rank'] = portfolio.iloc[:,1].map(maps_dict['own_map']).map(lambda x: flex_rank[flex_ownerships == x].iloc[0])
|
| 35 |
-
portfolio['FLEX2_Own_percent_rank'] = portfolio.iloc[:,2].map(maps_dict['own_map']).map(lambda x: flex_rank[flex_ownerships == x].iloc[0])
|
| 36 |
-
portfolio['FLEX3_Own_percent_rank'] = portfolio.iloc[:,3].map(maps_dict['own_map']).map(lambda x: flex_rank[flex_ownerships == x].iloc[0])
|
| 37 |
-
portfolio['FLEX4_Own_percent_rank'] = portfolio.iloc[:,4].map(maps_dict['own_map']).map(lambda x: flex_rank[flex_ownerships == x].iloc[0])
|
| 38 |
-
|
| 39 |
-
portfolio['CPT_Own'] = portfolio.iloc[:,0].map(maps_dict['cpt_own_map']) / 100
|
| 40 |
-
portfolio['FLEX1_Own'] = portfolio.iloc[:,1].map(maps_dict['own_map']) / 100
|
| 41 |
-
portfolio['FLEX2_Own'] = portfolio.iloc[:,2].map(maps_dict['own_map']) / 100
|
| 42 |
-
portfolio['FLEX3_Own'] = portfolio.iloc[:,3].map(maps_dict['own_map']) / 100
|
| 43 |
-
portfolio['FLEX4_Own'] = portfolio.iloc[:,4].map(maps_dict['own_map']) / 100
|
| 44 |
-
|
| 45 |
-
portfolio['own_product'] = (portfolio[own_columns].product(axis=1))
|
| 46 |
-
portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
|
| 47 |
-
portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
|
| 48 |
-
portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
|
| 49 |
-
|
| 50 |
-
# Calculate dupes formula
|
| 51 |
-
portfolio['dupes_calc'] = (portfolio['own_product'] * portfolio['avg_own_rank']) * Contest_Size + ((portfolio['salary'] - (60000 - portfolio['Own'])) / 100) - ((60000 - portfolio['salary']) / 100)
|
| 52 |
-
portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier
|
| 53 |
-
|
| 54 |
-
# Round and handle negative values
|
| 55 |
-
portfolio['Dupes'] = np.where(
|
| 56 |
-
np.round(portfolio['dupes_calc'], 0) <= 0,
|
| 57 |
-
0,
|
| 58 |
-
np.round(portfolio['dupes_calc'], 0) - 1
|
| 59 |
-
)
|
| 60 |
-
if type_var == 'Classic':
|
| 61 |
-
num_players = len([col for col in portfolio.columns if col not in ['salary', 'median', 'Own']])
|
| 62 |
-
dup_count_columns = [f'player_{i}_percent_rank' for i in range(1, num_players + 1)]
|
| 63 |
-
own_columns = [f'player_{i}_own' for i in range(1, num_players + 1)]
|
| 64 |
-
calc_columns = ['own_product', 'own_average', 'own_sum', 'avg_own_rank', 'dupes_calc', 'low_own_count', 'own_ratio', 'Ref_Proj', 'Max_Proj', 'Min_Proj', 'Avg_Ref', 'own_ratio']
|
| 65 |
-
for i in range(1, num_players + 1):
|
| 66 |
-
portfolio[f'player_{i}_percent_rank'] = portfolio.iloc[:,i-1].map(maps_dict['own_percent_rank'])
|
| 67 |
-
portfolio[f'player_{i}_own'] = portfolio.iloc[:,i-1].map(maps_dict['own_map']) / 100
|
| 68 |
-
|
| 69 |
-
portfolio['own_product'] = (portfolio[own_columns].product(axis=1))
|
| 70 |
-
portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
|
| 71 |
-
portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
|
| 72 |
-
portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
|
| 73 |
-
|
| 74 |
-
portfolio['dupes_calc'] = (portfolio['own_product'] * portfolio['avg_own_rank']) * Contest_Size + ((portfolio['salary'] - (60000 - portfolio['Own'])) / 100) - ((60000 - portfolio['salary']) / 100)
|
| 75 |
-
portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier
|
| 76 |
-
# Round and handle negative values
|
| 77 |
-
portfolio['Dupes'] = np.where(
|
| 78 |
-
np.round(portfolio['dupes_calc'], 0) <= 0,
|
| 79 |
-
0,
|
| 80 |
-
np.round(portfolio['dupes_calc'], 0) - 1
|
| 81 |
-
)
|
| 82 |
-
|
| 83 |
-
elif site_var == 'Draftkings':
|
| 84 |
-
if type_var == 'Showdown':
|
| 85 |
-
dup_count_columns = ['CPT_Own_percent_rank', 'FLEX1_Own_percent_rank', 'FLEX2_Own_percent_rank', 'FLEX3_Own_percent_rank', 'FLEX4_Own_percent_rank', 'FLEX5_Own_percent_rank']
|
| 86 |
-
own_columns = ['CPT_Own', 'FLEX1_Own', 'FLEX2_Own', 'FLEX3_Own', 'FLEX4_Own', 'FLEX5_Own']
|
| 87 |
-
calc_columns = ['own_product', 'own_average', 'own_sum', 'avg_own_rank', 'dupes_calc', 'low_own_count', 'Ref_Proj', 'Max_Proj', 'Min_Proj', 'Avg_Ref', 'own_ratio']
|
| 88 |
-
flex_ownerships = pd.concat([
|
| 89 |
-
portfolio.iloc[:,1].map(maps_dict['own_map']),
|
| 90 |
-
portfolio.iloc[:,2].map(maps_dict['own_map']),
|
| 91 |
-
portfolio.iloc[:,3].map(maps_dict['own_map']),
|
| 92 |
-
portfolio.iloc[:,4].map(maps_dict['own_map']),
|
| 93 |
-
portfolio.iloc[:,5].map(maps_dict['own_map'])
|
| 94 |
-
])
|
| 95 |
-
flex_rank = flex_ownerships.rank(pct=True)
|
| 96 |
-
|
| 97 |
-
# Assign ranks back to individual columns using the same rank scale
|
| 98 |
-
portfolio['CPT_Own_percent_rank'] = portfolio.iloc[:,0].map(maps_dict['cpt_own_map']).rank(pct=True)
|
| 99 |
-
portfolio['FLEX1_Own_percent_rank'] = portfolio.iloc[:,1].map(maps_dict['own_map']).map(lambda x: flex_rank[flex_ownerships == x].iloc[0])
|
| 100 |
-
portfolio['FLEX2_Own_percent_rank'] = portfolio.iloc[:,2].map(maps_dict['own_map']).map(lambda x: flex_rank[flex_ownerships == x].iloc[0])
|
| 101 |
-
portfolio['FLEX3_Own_percent_rank'] = portfolio.iloc[:,3].map(maps_dict['own_map']).map(lambda x: flex_rank[flex_ownerships == x].iloc[0])
|
| 102 |
-
portfolio['FLEX4_Own_percent_rank'] = portfolio.iloc[:,4].map(maps_dict['own_map']).map(lambda x: flex_rank[flex_ownerships == x].iloc[0])
|
| 103 |
-
portfolio['FLEX5_Own_percent_rank'] = portfolio.iloc[:,5].map(maps_dict['own_map']).map(lambda x: flex_rank[flex_ownerships == x].iloc[0])
|
| 104 |
-
|
| 105 |
-
portfolio['CPT_Own'] = portfolio.iloc[:,0].map(maps_dict['cpt_own_map']) / 100
|
| 106 |
-
portfolio['FLEX1_Own'] = portfolio.iloc[:,1].map(maps_dict['own_map']) / 100
|
| 107 |
-
portfolio['FLEX2_Own'] = portfolio.iloc[:,2].map(maps_dict['own_map']) / 100
|
| 108 |
-
portfolio['FLEX3_Own'] = portfolio.iloc[:,3].map(maps_dict['own_map']) / 100
|
| 109 |
-
portfolio['FLEX4_Own'] = portfolio.iloc[:,4].map(maps_dict['own_map']) / 100
|
| 110 |
-
portfolio['FLEX5_Own'] = portfolio.iloc[:,5].map(maps_dict['own_map']) / 100
|
| 111 |
-
|
| 112 |
-
portfolio['own_product'] = (portfolio[own_columns].product(axis=1))
|
| 113 |
-
portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
|
| 114 |
-
portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
|
| 115 |
-
portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
|
| 116 |
-
|
| 117 |
-
# Calculate dupes formula
|
| 118 |
-
portfolio['dupes_calc'] = (portfolio['own_product'] * portfolio['avg_own_rank']) * Contest_Size + ((portfolio['salary'] - (50000 - portfolio['Own'])) / 100) - ((50000 - portfolio['salary']) / 100)
|
| 119 |
-
portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier
|
| 120 |
-
|
| 121 |
-
# Round and handle negative values
|
| 122 |
-
portfolio['Dupes'] = np.where(
|
| 123 |
-
np.round(portfolio['dupes_calc'], 0) <= 0,
|
| 124 |
-
0,
|
| 125 |
-
np.round(portfolio['dupes_calc'], 0) - 1
|
| 126 |
-
)
|
| 127 |
-
if type_var == 'Classic':
|
| 128 |
-
num_players = len([col for col in portfolio.columns if col not in ['salary', 'median', 'Own']])
|
| 129 |
-
dup_count_columns = [f'player_{i}_percent_rank' for i in range(1, num_players + 1)]
|
| 130 |
-
own_columns = [f'player_{i}_own' for i in range(1, num_players + 1)]
|
| 131 |
-
calc_columns = ['own_product', 'own_average', 'own_sum', 'avg_own_rank', 'dupes_calc', 'low_own_count', 'Ref_Proj', 'Max_Proj', 'Min_Proj', 'Avg_Ref', 'own_ratio']
|
| 132 |
-
for i in range(1, num_players + 1):
|
| 133 |
-
portfolio[f'player_{i}_percent_rank'] = portfolio.iloc[:,i-1].map(maps_dict['own_percent_rank'])
|
| 134 |
-
portfolio[f'player_{i}_own'] = portfolio.iloc[:,i-1].map(maps_dict['own_map']) / 100
|
| 135 |
-
|
| 136 |
-
portfolio['own_product'] = (portfolio[own_columns].product(axis=1))
|
| 137 |
-
portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
|
| 138 |
-
portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
|
| 139 |
-
portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
|
| 140 |
-
|
| 141 |
-
portfolio['dupes_calc'] = (portfolio['own_product'] * portfolio['avg_own_rank']) * Contest_Size + ((portfolio['salary'] - (50000 - portfolio['Own'])) / 100) - ((50000 - portfolio['salary']) / 100)
|
| 142 |
-
portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier
|
| 143 |
-
# Round and handle negative values
|
| 144 |
-
portfolio['Dupes'] = np.where(
|
| 145 |
-
np.round(portfolio['dupes_calc'], 0) <= 0,
|
| 146 |
-
0,
|
| 147 |
-
np.round(portfolio['dupes_calc'], 0) - 1
|
| 148 |
-
)
|
| 149 |
-
|
| 150 |
-
portfolio['Dupes'] = np.round(portfolio['Dupes'], 0)
|
| 151 |
-
portfolio['own_ratio'] = np.where(
|
| 152 |
-
portfolio[own_columns].isin([max_ownership]).any(axis=1),
|
| 153 |
-
portfolio['own_sum'] / portfolio['own_average'],
|
| 154 |
-
(portfolio['own_sum'] - max_ownership) / portfolio['own_average']
|
| 155 |
-
)
|
| 156 |
-
percentile_cut_scalar = portfolio['median'].max() # Get scalar value
|
| 157 |
-
if type_var == 'Classic':
|
| 158 |
-
own_ratio_nerf = 2
|
| 159 |
-
elif type_var == 'Showdown':
|
| 160 |
-
own_ratio_nerf = 1.5
|
| 161 |
-
portfolio['Finish_percentile'] = portfolio.apply(
|
| 162 |
-
lambda row: .0005 if (row['own_ratio'] - own_ratio_nerf) / ((10 * (row['median'] / percentile_cut_scalar)) / 2) < .0005
|
| 163 |
-
else (row['own_ratio'] - own_ratio_nerf) / ((10 * (row['median'] / percentile_cut_scalar)) / 2),
|
| 164 |
-
axis=1
|
| 165 |
-
)
|
| 166 |
-
|
| 167 |
-
portfolio['Ref_Proj'] = portfolio['median'].max()
|
| 168 |
-
portfolio['Max_Proj'] = portfolio['Ref_Proj'] + 10
|
| 169 |
-
portfolio['Min_Proj'] = portfolio['Ref_Proj'] - 10
|
| 170 |
-
portfolio['Avg_Ref'] = (portfolio['Max_Proj'] + portfolio['Min_Proj']) / 2
|
| 171 |
-
portfolio['Win%'] = (((portfolio['median'] / portfolio['Avg_Ref']) - (0.1 + ((portfolio['Ref_Proj'] - portfolio['median'])/100))) / (Contest_Size / 1000)) / 10
|
| 172 |
-
max_allowed_win = (1 / Contest_Size) * 5
|
| 173 |
-
portfolio['Win%'] = portfolio['Win%'] / portfolio['Win%'].max() * max_allowed_win
|
| 174 |
-
|
| 175 |
-
portfolio['Finish_percentile'] = portfolio['Finish_percentile'] + .005 + (.005 * (Contest_Size / 10000))
|
| 176 |
-
portfolio['Finish_percentile'] = portfolio['Finish_percentile'] * percentile_multiplier
|
| 177 |
-
portfolio['Win%'] = portfolio['Win%'] * (1 - portfolio['Finish_percentile'])
|
| 178 |
-
|
| 179 |
-
portfolio['low_own_count'] = portfolio[own_columns].apply(lambda row: (row < 0.10).sum(), axis=1)
|
| 180 |
-
portfolio['Finish_percentile'] = portfolio.apply(lambda row: row['Finish_percentile'] if row['low_own_count'] <= 0 else row['Finish_percentile'] / row['low_own_count'], axis=1)
|
| 181 |
-
portfolio['Lineup Edge'] = portfolio['Win%'] * ((.5 - portfolio['Finish_percentile']) * (Contest_Size / 2.5))
|
| 182 |
-
portfolio['Lineup Edge'] = portfolio.apply(lambda row: row['Lineup Edge'] / (row['Dupes'] + 1) if row['Dupes'] > 0 else row['Lineup Edge'], axis=1)
|
| 183 |
-
portfolio['Lineup Edge'] = portfolio['Lineup Edge'] - portfolio['Lineup Edge'].mean()
|
| 184 |
-
portfolio = portfolio.drop(columns=dup_count_columns)
|
| 185 |
-
portfolio = portfolio.drop(columns=own_columns)
|
| 186 |
-
portfolio = portfolio.drop(columns=calc_columns)
|
| 187 |
-
|
| 188 |
-
return portfolio
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|