James McCool
commited on
Commit
·
25fcef5
1
Parent(s):
f08f4f1
Enhance portfolio filtering and trimming options in app.py: introduce expandable sections for filter and trimming options, update portfolio trimming logic to support geomean calculations, and improve export functionality with stack count adjustments.
Browse files- app.py +123 -122
- global_func/predict_dupes.py +2 -1
- global_func/trim_portfolio.py +4 -1
app.py
CHANGED
@@ -780,132 +780,133 @@ with tab3:
|
|
780 |
)
|
781 |
|
782 |
col1, col2 = st.columns([1, 10])
|
783 |
-
if st.button('Trim Portfolio'):
|
784 |
-
st.session_state['portfolio'] = trim_portfolio(st.session_state['portfolio'], 'median', 'Own')
|
785 |
with col1:
|
786 |
-
with st.
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
|
800 |
-
|
801 |
-
|
802 |
-
|
803 |
-
|
804 |
-
|
|
|
|
|
|
|
|
|
|
|
805 |
|
806 |
with col2:
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
if
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
|
|
|
|
|
|
828 |
|
829 |
-
|
830 |
-
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
-
|
835 |
-
|
836 |
-
|
837 |
-
|
838 |
-
|
839 |
-
|
840 |
-
|
841 |
-
|
842 |
-
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
st.
|
848 |
-
|
849 |
-
|
850 |
-
|
851 |
-
|
852 |
-
|
853 |
-
|
854 |
-
|
855 |
-
|
856 |
-
|
857 |
-
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
872 |
-
|
873 |
-
|
874 |
-
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
879 |
-
|
880 |
-
|
881 |
-
|
882 |
-
|
883 |
-
|
884 |
-
|
885 |
-
|
886 |
-
|
887 |
-
|
888 |
-
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
902 |
-
|
903 |
-
|
904 |
-
|
905 |
-
submitted = st.form_submit_button("Submit")
|
906 |
-
if submitted:
|
907 |
-
st.write('Export portfolio updated!')
|
908 |
-
st.session_state['math_done'] = True
|
909 |
|
910 |
st.download_button(label="Download Portfolio", data=export_file.to_csv(index=False), file_name="portfolio.csv", mime="text/csv")
|
911 |
|
|
|
780 |
)
|
781 |
|
782 |
col1, col2 = st.columns([1, 10])
|
|
|
|
|
783 |
with col1:
|
784 |
+
with st.expander('Filter Options'):
|
785 |
+
with st.form(key='filter_form'):
|
786 |
+
max_dupes = st.number_input("Max acceptable dupes?", value=1000, min_value=1, step=1)
|
787 |
+
min_salary = st.number_input("Min acceptable salary?", value=1000, min_value=1000, step=100)
|
788 |
+
max_salary = st.number_input("Max acceptable salary?", value=60000, min_value=1000, step=100)
|
789 |
+
max_finish_percentile = st.number_input("Max acceptable finish percentile?", value=.50, min_value=0.005, step=.001)
|
790 |
+
min_lineup_edge = st.number_input("Min acceptable Lineup Edge?", value=-.5, min_value=-1.00, step=.001)
|
791 |
+
player_names = set()
|
792 |
+
for col in st.session_state['portfolio'].columns:
|
793 |
+
if col not in excluded_cols:
|
794 |
+
player_names.update(st.session_state['portfolio'][col].unique())
|
795 |
+
player_lock = st.multiselect("Lock players?", options=sorted(list(player_names)), default=[])
|
796 |
+
player_remove = st.multiselect("Remove players?", options=sorted(list(player_names)), default=[])
|
797 |
+
if stack_dict is not None:
|
798 |
+
stack_toggle = st.selectbox("Include specific stacks?", options=['All Stacks', 'Specific Stacks'], index=0)
|
799 |
+
stack_selections = st.multiselect("If Specific Stacks, Which to include?", options=sorted(list(set(stack_dict.values()))), default=[])
|
800 |
+
stack_remove = st.multiselect("If Specific Stacks, Which to remove?", options=sorted(list(set(stack_dict.values()))), default=[])
|
801 |
+
|
802 |
+
submitted = st.form_submit_button("Submit")
|
803 |
+
with st.expander('Trimming Options'):
|
804 |
+
performance_type = st.selectbox("Select sort type", ['median', 'Finish_percentile'])
|
805 |
+
own_type = st.selectbox("Select trimming variable type", ['Own', 'Geomean'])
|
806 |
+
if st.button('Trim Portfolio'):
|
807 |
+
st.session_state['portfolio'] = trim_portfolio(st.session_state['portfolio'], performance_type, own_type)
|
808 |
|
809 |
with col2:
|
810 |
+
st.session_state['portfolio'] = predict_dupes(st.session_state['portfolio'], map_dict, site_var, type_var, Contest_Size, strength_var)
|
811 |
+
st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Dupes'] <= max_dupes]
|
812 |
+
st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['salary'] >= min_salary]
|
813 |
+
st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['salary'] <= max_salary]
|
814 |
+
st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Finish_percentile'] <= max_finish_percentile]
|
815 |
+
st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Lineup Edge'] >= min_lineup_edge]
|
816 |
+
if stack_dict is not None:
|
817 |
+
if stack_toggle == 'All Stacks':
|
818 |
+
st.session_state['portfolio'] = st.session_state['portfolio']
|
819 |
+
st.session_state['portfolio'] = st.session_state['portfolio'][~st.session_state['portfolio']['Stack'].isin(stack_remove)]
|
820 |
+
else:
|
821 |
+
st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Stack'].isin(stack_selections)]
|
822 |
+
st.session_state['portfolio'] = st.session_state['portfolio'][~st.session_state['portfolio']['Stack'].isin(stack_remove)]
|
823 |
+
if player_remove:
|
824 |
+
# Create mask for lineups that contain any of the removed players
|
825 |
+
player_columns = [col for col in st.session_state['portfolio'].columns if col not in excluded_cols]
|
826 |
+
remove_mask = st.session_state['portfolio'][player_columns].apply(
|
827 |
+
lambda row: not any(player in list(row) for player in player_remove), axis=1
|
828 |
+
)
|
829 |
+
st.session_state['portfolio'] = st.session_state['portfolio'][remove_mask]
|
830 |
+
|
831 |
+
if player_lock:
|
832 |
+
# Create mask for lineups that contain all locked players
|
833 |
+
player_columns = [col for col in st.session_state['portfolio'].columns if col not in excluded_cols]
|
834 |
|
835 |
+
lock_mask = st.session_state['portfolio'][player_columns].apply(
|
836 |
+
lambda row: all(player in list(row) for player in player_lock), axis=1
|
837 |
+
)
|
838 |
+
st.session_state['portfolio'] = st.session_state['portfolio'][lock_mask]
|
839 |
+
export_file = st.session_state['portfolio'].copy()
|
840 |
+
st.session_state['portfolio'] = st.session_state['portfolio'].sort_values(by='median', ascending=False)
|
841 |
+
if csv_file is not None:
|
842 |
+
player_columns = [col for col in st.session_state['portfolio'].columns if col not in excluded_cols]
|
843 |
+
for col in player_columns:
|
844 |
+
export_file[col] = export_file[col].map(st.session_state['export_dict'])
|
845 |
+
with st.expander("Download options"):
|
846 |
+
if stack_dict is not None:
|
847 |
+
with st.form(key='stack_form'):
|
848 |
+
st.subheader("Stack Count Adjustments")
|
849 |
+
st.info("This allows you to fine tune the stacks that you wish to export. If you want to make sure you don't export any of a specific stack you can 0 it out.")
|
850 |
+
# Create a container for stack value inputs
|
851 |
+
sort_container = st.container()
|
852 |
+
with sort_container:
|
853 |
+
sort_var = st.selectbox("Sort export portfolio by:", options=['median', 'Lineup Edge', 'Own'])
|
854 |
+
|
855 |
+
# Get unique stack values
|
856 |
+
unique_stacks = sorted(list(set(stack_dict.values())))
|
857 |
+
|
858 |
+
# Create a dictionary to store stack multipliers
|
859 |
+
if 'stack_multipliers' not in st.session_state:
|
860 |
+
st.session_state.stack_multipliers = {stack: 0.0 for stack in unique_stacks}
|
861 |
+
|
862 |
+
# Create columns for the stack inputs
|
863 |
+
num_cols = 6 # Number of columns to display
|
864 |
+
for i in range(0, len(unique_stacks), num_cols):
|
865 |
+
cols = st.columns(num_cols)
|
866 |
+
for j, stack in enumerate(unique_stacks[i:i+num_cols]):
|
867 |
+
with cols[j]:
|
868 |
+
# Create a unique key for each number input
|
869 |
+
key = f"stack_count_{stack}"
|
870 |
+
# Get the current count of this stack in the portfolio
|
871 |
+
current_stack_count = len(st.session_state['portfolio'][st.session_state['portfolio']['Stack'] == stack])
|
872 |
+
# Create number input with current value and max value based on actual count
|
873 |
+
st.session_state.stack_multipliers[stack] = st.number_input(
|
874 |
+
f"{stack} count",
|
875 |
+
min_value=0.0,
|
876 |
+
max_value=float(current_stack_count),
|
877 |
+
value=0.0,
|
878 |
+
step=1.0,
|
879 |
+
key=key
|
880 |
+
)
|
881 |
+
|
882 |
+
# Create a copy of the portfolio
|
883 |
+
portfolio_copy = st.session_state['portfolio'].copy()
|
884 |
+
|
885 |
+
# Create a list to store selected rows
|
886 |
+
selected_rows = []
|
887 |
+
|
888 |
+
# For each stack, select the top N rows based on the count value
|
889 |
+
for stack in unique_stacks:
|
890 |
+
if stack in st.session_state.stack_multipliers:
|
891 |
+
count = int(st.session_state.stack_multipliers[stack])
|
892 |
+
# Get rows for this stack
|
893 |
+
stack_rows = portfolio_copy[portfolio_copy['Stack'] == stack]
|
894 |
+
# Sort by median and take top N rows
|
895 |
+
top_rows = stack_rows.nlargest(count, sort_var)
|
896 |
+
selected_rows.append(top_rows)
|
897 |
+
|
898 |
+
# Combine all selected rows
|
899 |
+
portfolio_copy = pd.concat(selected_rows)
|
900 |
+
|
901 |
+
# Update export_file with filtered data
|
902 |
+
export_file = portfolio_copy.copy()
|
903 |
+
for col in export_file.columns:
|
904 |
+
if col not in excluded_cols:
|
905 |
+
export_file[col] = export_file[col].map(st.session_state['export_dict'])
|
906 |
+
|
907 |
+
submitted = st.form_submit_button("Submit")
|
908 |
+
if submitted:
|
909 |
+
st.write('Export portfolio updated!')
|
|
|
|
|
|
|
|
|
|
|
910 |
|
911 |
st.download_button(label="Download Portfolio", data=export_file.to_csv(index=False), file_name="portfolio.csv", mime="text/csv")
|
912 |
|
global_func/predict_dupes.py
CHANGED
@@ -3,6 +3,7 @@ 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':
|
@@ -181,7 +182,7 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
|
|
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['
|
185 |
portfolio = portfolio.drop(columns=dup_count_columns)
|
186 |
portfolio = portfolio.drop(columns=own_columns)
|
187 |
portfolio = portfolio.drop(columns=calc_columns)
|
|
|
3 |
import pandas as pd
|
4 |
import time
|
5 |
from fuzzywuzzy import process
|
6 |
+
from scipy.stats import gmean
|
7 |
|
8 |
def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, strength_var):
|
9 |
if strength_var == 'Weak':
|
|
|
182 |
portfolio['Lineup Edge'] = portfolio['Win%'] * ((.5 - portfolio['Finish_percentile']) * (Contest_Size / 2.5))
|
183 |
portfolio['Lineup Edge'] = portfolio.apply(lambda row: row['Lineup Edge'] / (row['Dupes'] + 1) if row['Dupes'] > 0 else row['Lineup Edge'], axis=1)
|
184 |
portfolio['Lineup Edge'] = portfolio['Lineup Edge'] - portfolio['Lineup Edge'].mean()
|
185 |
+
portfolio['Geomean'] = portfolio[own_columns].apply(lambda row: gmean(row), axis=1)
|
186 |
portfolio = portfolio.drop(columns=dup_count_columns)
|
187 |
portfolio = portfolio.drop(columns=own_columns)
|
188 |
portfolio = portfolio.drop(columns=calc_columns)
|
global_func/trim_portfolio.py
CHANGED
@@ -1,5 +1,8 @@
|
|
1 |
def trim_portfolio(portfolio, performance_type, own_type):
|
2 |
-
|
|
|
|
|
|
|
3 |
rows_to_drop = []
|
4 |
curr_own_type_max = working_portfolio.loc[0, own_type]
|
5 |
|
|
|
1 |
def trim_portfolio(portfolio, performance_type, own_type):
|
2 |
+
if performance_type == 'Finish_percentile':
|
3 |
+
working_portfolio = portfolio.sort_values(by=performance_type, ascending = True).reset_index(drop=True)
|
4 |
+
else:
|
5 |
+
working_portfolio = portfolio.sort_values(by=performance_type, ascending = False).reset_index(drop=True)
|
6 |
rows_to_drop = []
|
7 |
curr_own_type_max = working_portfolio.loc[0, own_type]
|
8 |
|