James McCool
commited on
Commit
·
d015d8c
1
Parent(s):
8b34fed
Add reassess_edge function in reassess_edge.py for recalculating lineup metrics based on player performance, and update app.py to utilize this function for improved edge assessments in player lineups.
Browse files- app.py +2 -1
- global_func/reassess_edge.py +61 -5
app.py
CHANGED
|
@@ -26,6 +26,7 @@ from global_func.reduce_volatility_preset import reduce_volatility_preset
|
|
| 26 |
from global_func.analyze_player_combos import analyze_player_combos
|
| 27 |
from global_func.stratification_function import stratification_function
|
| 28 |
from global_func.exposure_spread import exposure_spread
|
|
|
|
| 29 |
|
| 30 |
freq_format = {'Finish_percentile': '{:.2%}', 'Lineup Edge': '{:.2%}', 'Win%': '{:.2%}'}
|
| 31 |
stacking_sports = ['MLB', 'NHL', 'NFL', 'LOL']
|
|
@@ -1526,7 +1527,7 @@ with tab2:
|
|
| 1526 |
st.session_state['export_base']['salary'] = st.session_state['export_base']['salary'].astype('uint16')
|
| 1527 |
|
| 1528 |
print(st.session_state['export_base'].head(10))
|
| 1529 |
-
st.session_state['export_base'] =
|
| 1530 |
st.session_state['export_merge'] = st.session_state['export_base'].copy()
|
| 1531 |
|
| 1532 |
with st.container():
|
|
|
|
| 26 |
from global_func.analyze_player_combos import analyze_player_combos
|
| 27 |
from global_func.stratification_function import stratification_function
|
| 28 |
from global_func.exposure_spread import exposure_spread
|
| 29 |
+
from global_func.reassess_edge import reassess_edge
|
| 30 |
|
| 31 |
freq_format = {'Finish_percentile': '{:.2%}', 'Lineup Edge': '{:.2%}', 'Win%': '{:.2%}'}
|
| 32 |
stacking_sports = ['MLB', 'NHL', 'NFL', 'LOL']
|
|
|
|
| 1527 |
st.session_state['export_base']['salary'] = st.session_state['export_base']['salary'].astype('uint16')
|
| 1528 |
|
| 1529 |
print(st.session_state['export_base'].head(10))
|
| 1530 |
+
st.session_state['export_base'] = reassess_edge(st.session_state['export_base'], st.session_state['export_base'], st.session_state['map_dict'], site_var, type_var, Contest_Size, strength_var, sport_var, salary_max)
|
| 1531 |
st.session_state['export_merge'] = st.session_state['export_base'].copy()
|
| 1532 |
|
| 1533 |
with st.container():
|
global_func/reassess_edge.py
CHANGED
|
@@ -9,9 +9,62 @@ import pandas as pd
|
|
| 9 |
import numpy as np
|
| 10 |
import math
|
| 11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
def reassess_dupes(row: pd.Series, salary_max: int) -> float:
|
| 13 |
return math.ceil(row['Dupes'] + ((row['salary_diff'] / 100) + ((salary_max + (salary_max - row['salary'])) / 100)) * (1 - (row['own_diff'] / 100))).clip(lower=0)
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
def reassess_edge(refactored_frame: pd.DataFrame, original_frame: pd.DataFrame, maps_dict: dict, site_var: str, type_var: str, Contest_Size: int, strength_var: str, sport_var: str, salary_max: int) -> pd.DataFrame:
|
| 16 |
orig_df = original_frame.copy()
|
| 17 |
orig_df = orig_df.reset_index(drop=True)
|
|
@@ -24,12 +77,15 @@ def reassess_edge(refactored_frame: pd.DataFrame, original_frame: pd.DataFrame,
|
|
| 24 |
|
| 25 |
change_mask = refactored_df[refactored_df['salary_diff'] != 0 | refactored_df['median_diff'] != 0 | refactored_df['own_diff'] != 0]
|
| 26 |
|
|
|
|
|
|
|
|
|
|
| 27 |
for lineups in change_mask.index:
|
| 28 |
refactored_df.loc[lineups, 'Dupes'] = reassess_dupes(refactored_df.loc[lineups, :], salary_max)
|
| 29 |
-
refactored_df.loc[lineups, 'Finish_percentile'] = refactored_df.loc[lineups, 'Finish_percentile']
|
| 30 |
-
refactored_df.loc[lineups, 'Win%'] = refactored_df.loc[lineups, 'Win%']
|
| 31 |
-
refactored_df.loc[lineups, 'Edge'] = refactored_df.loc[lineups,
|
| 32 |
-
refactored_df.loc[lineups, 'Weighted Own'] = refactored_df.
|
| 33 |
-
refactored_df.loc[lineups, 'Geomean'] = refactored_df.loc[lineups,
|
| 34 |
|
| 35 |
return refactored_df
|
|
|
|
| 9 |
import numpy as np
|
| 10 |
import math
|
| 11 |
|
| 12 |
+
def calculate_weighted_ownership_vectorized(ownership_array):
|
| 13 |
+
"""
|
| 14 |
+
Vectorized version of calculate_weighted_ownership using NumPy operations.
|
| 15 |
+
|
| 16 |
+
Args:
|
| 17 |
+
ownership_array: 2D array of ownership values (rows x players)
|
| 18 |
+
|
| 19 |
+
Returns:
|
| 20 |
+
array: Calculated weighted ownership values for each row
|
| 21 |
+
"""
|
| 22 |
+
# Convert percentages to decimals and handle NaN values
|
| 23 |
+
ownership_array = np.where(np.isnan(ownership_array), 0, ownership_array) / 100
|
| 24 |
+
|
| 25 |
+
# Calculate row means
|
| 26 |
+
row_means = np.mean(ownership_array, axis=1, keepdims=True)
|
| 27 |
+
|
| 28 |
+
# Calculate average of each value with the overall mean
|
| 29 |
+
value_means = (ownership_array + row_means) / 2
|
| 30 |
+
|
| 31 |
+
# Take average of all those means
|
| 32 |
+
avg_of_means = np.mean(value_means, axis=1)
|
| 33 |
+
|
| 34 |
+
# Multiply by count of values
|
| 35 |
+
weighted = avg_of_means * ownership_array.shape[1]
|
| 36 |
+
|
| 37 |
+
# Subtract (max - min) for each row
|
| 38 |
+
row_max = np.max(ownership_array, axis=1)
|
| 39 |
+
row_min = np.min(ownership_array, axis=1)
|
| 40 |
+
weighted = weighted - (row_max - row_min)
|
| 41 |
+
|
| 42 |
+
# Convert back to percentage form
|
| 43 |
+
return weighted * 10000
|
| 44 |
+
|
| 45 |
+
def calculate_weighted_ownership_wrapper(row_ownerships):
|
| 46 |
+
"""
|
| 47 |
+
Wrapper function for the original calculate_weighted_ownership to work with Pandas .apply()
|
| 48 |
+
|
| 49 |
+
Args:
|
| 50 |
+
row_ownerships: Series containing ownership values in percentage form
|
| 51 |
+
|
| 52 |
+
Returns:
|
| 53 |
+
float: Calculated weighted ownership value
|
| 54 |
+
"""
|
| 55 |
+
# Convert Series to 2D array for vectorized function
|
| 56 |
+
ownership_array = row_ownerships.values.reshape(1, -1)
|
| 57 |
+
return calculate_weighted_ownership_vectorized(ownership_array)[0]
|
| 58 |
+
|
| 59 |
def reassess_dupes(row: pd.Series, salary_max: int) -> float:
|
| 60 |
return math.ceil(row['Dupes'] + ((row['salary_diff'] / 100) + ((salary_max + (salary_max - row['salary'])) / 100)) * (1 - (row['own_diff'] / 100))).clip(lower=0)
|
| 61 |
|
| 62 |
+
def reassess_edge(row: pd.Series, Contest_Size: int) -> float:
|
| 63 |
+
row['Lineup Edge'] = row['Win%'] * ((.5 - row['Finish_percentile']) * (Contest_Size / 2.5))
|
| 64 |
+
row['Lineup Edge'] = row.apply(lambda row: row['Lineup Edge'] / (row['Dupes'] + 1) if row['Dupes'] > 0 else row['Lineup Edge'], axis=1)
|
| 65 |
+
|
| 66 |
+
return row['Lineup Edge'] - row['Lineup Edge'].mean()
|
| 67 |
+
|
| 68 |
def reassess_edge(refactored_frame: pd.DataFrame, original_frame: pd.DataFrame, maps_dict: dict, site_var: str, type_var: str, Contest_Size: int, strength_var: str, sport_var: str, salary_max: int) -> pd.DataFrame:
|
| 69 |
orig_df = original_frame.copy()
|
| 70 |
orig_df = orig_df.reset_index(drop=True)
|
|
|
|
| 77 |
|
| 78 |
change_mask = refactored_df[refactored_df['salary_diff'] != 0 | refactored_df['median_diff'] != 0 | refactored_df['own_diff'] != 0]
|
| 79 |
|
| 80 |
+
num_players = len([col for col in refactored_df.columns if col not in ['salary', 'median', 'Own', 'Finish_percentile', 'Win%', 'Edge', 'Weighted Own', 'Geomean', 'salary_diff', 'median_diff', 'own_diff']])
|
| 81 |
+
own_columns = [f'player_{i}_own' for i in range(1, num_players + 1)]
|
| 82 |
+
|
| 83 |
for lineups in change_mask.index:
|
| 84 |
refactored_df.loc[lineups, 'Dupes'] = reassess_dupes(refactored_df.loc[lineups, :], salary_max)
|
| 85 |
+
refactored_df.loc[lineups, 'Finish_percentile'] = refactored_df.loc[lineups, 'Finish_percentile']
|
| 86 |
+
refactored_df.loc[lineups, 'Win%'] = refactored_df.loc[lineups, 'Win%']
|
| 87 |
+
refactored_df.loc[lineups, 'Edge'] = reassess_edge(refactored_df.loc[lineups, :], Contest_Size)
|
| 88 |
+
refactored_df.loc[lineups, 'Weighted Own'] = refactored_df[own_columns].apply(calculate_weighted_ownership_wrapper, axis=1)
|
| 89 |
+
refactored_df.loc[lineups, 'Geomean'] = np.power((refactored_df.loc[lineups, own_columns] * 100).product(axis=1), 1 / len(own_columns))
|
| 90 |
|
| 91 |
return refactored_df
|