James McCool commited on
Commit
37c59a0
·
1 Parent(s): c300f43

Refactor portfolio handling in app.py: replace direct references to the portfolio with a working_frame variable for improved clarity and maintainability, ensuring consistent data processing across various operations.

Browse files
Files changed (1) hide show
  1. app.py +169 -179
app.py CHANGED
@@ -187,7 +187,6 @@ with tab1:
187
 
188
  # Update the portfolio in session state
189
  st.session_state['portfolio'] = portfolio
190
- st.session_state['origin_portfolio'] = st.session_state['portfolio'].copy()
191
 
192
  # Store the match dictionary for reference
193
  st.session_state['portfolio_to_projection_matches'] = portfolio_match_dict
@@ -244,6 +243,7 @@ with tab1:
244
  st.write(f"- {name}")
245
  else:
246
  st.success("All portfolio names were matched to projections!")
 
247
  st.session_state['origin_portfolio'] = st.session_state['portfolio'].copy()
248
 
249
  # with tab2:
@@ -761,6 +761,7 @@ with tab1:
761
  with tab2:
762
  if 'portfolio' in st.session_state and 'projections_df' in st.session_state:
763
 
 
764
  excluded_cols = ['salary', 'median', 'Own', 'Finish_percentile', 'Dupes', 'Stack', 'Win%', 'Lineup Edge', 'Weighted Own', 'Geomean']
765
  with st.container():
766
 
@@ -768,7 +769,7 @@ with tab2:
768
  with col1:
769
  site_var = st.selectbox("Select Site", ['Draftkings', 'Fanduel'])
770
  if st.button('Reset Portfolio', key='reset_port'):
771
- st.session_state['portfolio'] = st.session_state['origin_portfolio'].copy()
772
  if st.button('Clear data', key='reset3'):
773
  st.session_state.clear()
774
 
@@ -780,9 +781,34 @@ with tab2:
780
  Contest_Size = st.number_input("Enter Contest Size", value=25000, min_value=1, step=1)
781
  strength_var = st.selectbox("Select field strength", ['Average', 'Sharp', 'Weak'])
782
 
783
- if site_var == 'Draftkings':
784
- if type_var == 'Classic':
785
- if sport_var == 'CS2':
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
786
  map_dict = {
787
  'pos_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['position'])),
788
  'team_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['team'])),
@@ -794,19 +820,7 @@ with tab2:
794
  'cpt_proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'] * 1.5)),
795
  'cpt_own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['captain ownership']))
796
  }
797
- elif sport_var != 'CS2':
798
- map_dict = {
799
- 'pos_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['position'])),
800
- 'team_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['team'])),
801
- 'salary_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'])),
802
- 'proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'])),
803
- 'own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'])),
804
- 'own_percent_rank':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'].rank(pct=True))),
805
- 'cpt_salary_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'])),
806
- 'cpt_proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'] * 1.5)),
807
- 'cpt_own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['captain ownership']))
808
- }
809
- elif type_var == 'Showdown':
810
  map_dict = {
811
  'pos_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['position'])),
812
  'team_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['team'])),
@@ -814,72 +828,61 @@ with tab2:
814
  'proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'])),
815
  'own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'])),
816
  'own_percent_rank':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'].rank(pct=True))),
817
- 'cpt_salary_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'] * 1.5)),
818
  'cpt_proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'] * 1.5)),
819
  'cpt_own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['captain ownership']))
820
  }
821
- elif site_var == 'Fanduel':
822
- map_dict = {
823
- 'pos_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['position'])),
824
- 'team_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['team'])),
825
- 'salary_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'])),
826
- 'proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'])),
827
- 'own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'])),
828
- 'own_percent_rank':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'].rank(pct=True))),
829
- 'cpt_salary_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'])),
830
- 'cpt_proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'] * 1.5)),
831
- 'cpt_own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['captain ownership']))
832
- }
833
- if type_var == 'Classic':
834
- if sport_var == 'CS2':
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
835
  # Calculate salary (CPT uses cpt_salary_map, others use salary_map)
836
- st.session_state['portfolio']['salary'] = st.session_state['portfolio'].apply(
837
  lambda row: map_dict['cpt_salary_map'].get(row.iloc[0], 0) +
838
  sum(map_dict['salary_map'].get(player, 0) for player in row.iloc[1:]),
839
  axis=1
840
  )
841
 
842
  # Calculate median (CPT uses cpt_proj_map, others use proj_map)
843
- st.session_state['portfolio']['median'] = st.session_state['portfolio'].apply(
844
  lambda row: map_dict['cpt_proj_map'].get(row.iloc[0], 0) +
845
  sum(map_dict['proj_map'].get(player, 0) for player in row.iloc[1:]),
846
  axis=1
847
  )
848
 
849
  # Calculate ownership (CPT uses cpt_own_map, others use own_map)
850
- st.session_state['portfolio']['Own'] = st.session_state['portfolio'].apply(
851
  lambda row: map_dict['cpt_own_map'].get(row.iloc[0], 0) +
852
  sum(map_dict['own_map'].get(player, 0) for player in row.iloc[1:]),
853
  axis=1
854
  )
855
 
856
- elif sport_var != 'CS2':
857
- st.session_state['portfolio']['salary'] = st.session_state['portfolio'].apply(lambda row: sum(map_dict['salary_map'].get(player, 0) for player in row), axis=1)
858
- st.session_state['portfolio']['median'] = st.session_state['portfolio'].apply(lambda row: sum(map_dict['proj_map'].get(player, 0) for player in row), axis=1)
859
- st.session_state['portfolio']['Own'] = st.session_state['portfolio'].apply(lambda row: sum(map_dict['own_map'].get(player, 0) for player in row), axis=1)
860
- if stack_dict is not None:
861
- st.session_state['portfolio']['Stack'] = st.session_state['portfolio'].index.map(stack_dict)
862
- elif type_var == 'Showdown':
863
- # Calculate salary (CPT uses cpt_salary_map, others use salary_map)
864
- st.session_state['portfolio']['salary'] = st.session_state['portfolio'].apply(
865
- lambda row: map_dict['cpt_salary_map'].get(row.iloc[0], 0) +
866
- sum(map_dict['salary_map'].get(player, 0) for player in row.iloc[1:]),
867
- axis=1
868
- )
869
-
870
- # Calculate median (CPT uses cpt_proj_map, others use proj_map)
871
- st.session_state['portfolio']['median'] = st.session_state['portfolio'].apply(
872
- lambda row: map_dict['cpt_proj_map'].get(row.iloc[0], 0) +
873
- sum(map_dict['proj_map'].get(player, 0) for player in row.iloc[1:]),
874
- axis=1
875
- )
876
-
877
- # Calculate ownership (CPT uses cpt_own_map, others use own_map)
878
- st.session_state['portfolio']['Own'] = st.session_state['portfolio'].apply(
879
- lambda row: map_dict['cpt_own_map'].get(row.iloc[0], 0) +
880
- sum(map_dict['own_map'].get(player, 0) for player in row.iloc[1:]),
881
- axis=1
882
- )
883
  col1, col2 = st.columns([2, 8])
884
  with col1:
885
  if 'trimming_dict_maxes' not in st.session_state:
@@ -903,16 +906,70 @@ with tab2:
903
  stack_remove = st.multiselect("If Specific Stacks, Which to remove?", options=sorted(list(set(stack_dict.values()))), default=[])
904
 
905
  submitted = st.form_submit_button("Submit")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
906
  with st.expander('Micro Filter Options'):
907
  with st.form(key='micro_filter_form'):
908
  player_names = set()
909
- for col in st.session_state['portfolio'].columns:
910
  if col not in excluded_cols:
911
- player_names.update(st.session_state['portfolio'][col].unique())
912
  player_lock = st.multiselect("Lock players?", options=sorted(list(player_names)), default=[])
913
  player_remove = st.multiselect("Remove players?", options=sorted(list(player_names)), default=[])
914
 
915
  submitted = st.form_submit_button("Submit")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
916
  with st.expander('Trimming Options'):
917
  st.info("Make sure you filter before trimming if you want to filter, trimming before a filter will reset your portfolio")
918
  with st.form(key='trim_form'):
@@ -942,93 +999,26 @@ with tab2:
942
  submitted = st.form_submit_button("Trim")
943
  if submitted:
944
  st.write('initiated')
945
- st.session_state['portfolio'] = predict_dupes(st.session_state['portfolio'], map_dict, site_var, type_var, Contest_Size, strength_var, sport_var)
946
  if 'trimming_dict_maxes' not in st.session_state:
947
  st.session_state['trimming_dict_maxes'] = {
948
- 'Own': st.session_state['portfolio']['Own'].max(),
949
- 'Geomean': st.session_state['portfolio']['Geomean'].max(),
950
- 'Weighted Own': st.session_state['portfolio']['Weighted Own'].max(),
951
- 'median': st.session_state['portfolio']['median'].max(),
952
- 'Finish_percentile': st.session_state['portfolio']['Finish_percentile'].max()
953
  }
954
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Dupes'] <= max_dupes]
955
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['salary'] >= min_salary]
956
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['salary'] <= max_salary]
957
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Finish_percentile'] <= max_finish_percentile]
958
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Lineup Edge'] >= min_lineup_edge]
959
- if stack_dict is not None:
960
- if stack_toggle == 'All Stacks':
961
- st.session_state['portfolio'] = st.session_state['portfolio']
962
- st.session_state['portfolio'] = st.session_state['portfolio'][~st.session_state['portfolio']['Stack'].isin(stack_remove)]
963
- else:
964
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Stack'].isin(stack_selections)]
965
- st.session_state['portfolio'] = st.session_state['portfolio'][~st.session_state['portfolio']['Stack'].isin(stack_remove)]
966
- if player_remove:
967
- # Create mask for lineups that contain any of the removed players
968
- player_columns = [col for col in st.session_state['portfolio'].columns if col not in excluded_cols]
969
- remove_mask = st.session_state['portfolio'][player_columns].apply(
970
- lambda row: not any(player in list(row) for player in player_remove), axis=1
971
- )
972
- st.session_state['portfolio'] = st.session_state['portfolio'][remove_mask]
973
 
974
- if player_lock:
975
- # Create mask for lineups that contain all locked players
976
- player_columns = [col for col in st.session_state['portfolio'].columns if col not in excluded_cols]
977
-
978
- lock_mask = st.session_state['portfolio'][player_columns].apply(
979
- lambda row: all(player in list(row) for player in player_lock), axis=1
980
- )
981
- st.session_state['portfolio'] = st.session_state['portfolio'][lock_mask]
982
-
983
- st.session_state['portfolio'] = trim_portfolio(st.session_state['portfolio'], trim_slack_var, performance_type, own_type, performance_threshold_high, performance_threshold_low, own_threshold_high, own_threshold_low)
984
- st.session_state['portfolio'] = st.session_state['portfolio'].sort_values(by='median', ascending=False)
985
- st.session_state['export_merge'] = st.session_state['portfolio'].copy()
986
 
987
  with col2:
988
- st.session_state['portfolio'] = predict_dupes(st.session_state['portfolio'], map_dict, site_var, type_var, Contest_Size, strength_var, sport_var)
989
- if 'trimming_dict_maxes' not in st.session_state:
990
- st.session_state['trimming_dict_maxes'] = {
991
- 'Own': st.session_state['portfolio']['Own'].max(),
992
- 'Geomean': st.session_state['portfolio']['Geomean'].max(),
993
- 'Weighted Own': st.session_state['portfolio']['Weighted Own'].max(),
994
- 'median': st.session_state['portfolio']['median'].max(),
995
- 'Finish_percentile': st.session_state['portfolio']['Finish_percentile'].max()
996
- }
997
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Dupes'] <= max_dupes]
998
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['salary'] >= min_salary]
999
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['salary'] <= max_salary]
1000
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Finish_percentile'] <= max_finish_percentile]
1001
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Lineup Edge'] >= min_lineup_edge]
1002
- if stack_dict is not None:
1003
- if stack_toggle == 'All Stacks':
1004
- st.session_state['portfolio'] = st.session_state['portfolio']
1005
- st.session_state['portfolio'] = st.session_state['portfolio'][~st.session_state['portfolio']['Stack'].isin(stack_remove)]
1006
- else:
1007
- st.session_state['portfolio'] = st.session_state['portfolio'][st.session_state['portfolio']['Stack'].isin(stack_selections)]
1008
- st.session_state['portfolio'] = st.session_state['portfolio'][~st.session_state['portfolio']['Stack'].isin(stack_remove)]
1009
- if player_remove:
1010
- # Create mask for lineups that contain any of the removed players
1011
- player_columns = [col for col in st.session_state['portfolio'].columns if col not in excluded_cols]
1012
- remove_mask = st.session_state['portfolio'][player_columns].apply(
1013
- lambda row: not any(player in list(row) for player in player_remove), axis=1
1014
- )
1015
- st.session_state['portfolio'] = st.session_state['portfolio'][remove_mask]
1016
-
1017
- if player_lock:
1018
- # Create mask for lineups that contain all locked players
1019
- player_columns = [col for col in st.session_state['portfolio'].columns if col not in excluded_cols]
1020
-
1021
- lock_mask = st.session_state['portfolio'][player_columns].apply(
1022
- lambda row: all(player in list(row) for player in player_lock), axis=1
1023
- )
1024
- st.session_state['portfolio'] = st.session_state['portfolio'][lock_mask]
1025
- st.session_state['portfolio'] = st.session_state['portfolio'].sort_values(by='median', ascending=False)
1026
- st.session_state['export_merge'] = st.session_state['portfolio'].copy()
1027
  with st.expander("Download options"):
1028
  if stack_dict is not None:
1029
  download_type = st.selectbox("Simple or Advanced Download?", options=['Simple', 'Advanced'], key='download_choice')
1030
  if download_type == 'Simple':
1031
- st.session_state['export_file'] = st.session_state['portfolio'].copy()
1032
  for col in st.session_state['export_file'].columns:
1033
  if col not in excluded_cols:
1034
  st.session_state['export_file'][col] = st.session_state['export_file'][col].map(st.session_state['export_dict'])
@@ -1057,7 +1047,7 @@ with tab2:
1057
  # Create a unique key for each number input
1058
  key = f"stack_count_{stack}"
1059
  # Get the current count of this stack in the portfolio
1060
- current_stack_count = len(st.session_state['portfolio'][st.session_state['portfolio']['Stack'] == stack])
1061
  # Create number input with current value and max value based on actual count
1062
  st.session_state.stack_multipliers[stack] = st.number_input(
1063
  f"{stack} count",
@@ -1068,7 +1058,7 @@ with tab2:
1068
  key=key
1069
  )
1070
 
1071
- portfolio_copy = st.session_state['portfolio'].copy()
1072
 
1073
  submitted = st.form_submit_button("Submit")
1074
  if submitted:
@@ -1095,9 +1085,9 @@ with tab2:
1095
  st.session_state['export_file'][col] = st.session_state['export_file'][col].map(st.session_state['export_dict'])
1096
  st.write('Export portfolio updated!')
1097
  else:
1098
- st.session_state['export_file'] = st.session_state['portfolio'].copy()
1099
  if 'export_base' not in st.session_state:
1100
- st.session_state['export_base'] = pd.DataFrame(columns=st.session_state['portfolio'].columns)
1101
  for col in st.session_state['export_file'].columns:
1102
  if col not in excluded_cols:
1103
  st.session_state['export_file'][col] = st.session_state['export_file'][col].map(st.session_state['export_dict'])
@@ -1114,10 +1104,10 @@ with tab2:
1114
 
1115
  # display_frame_source = st.selectbox("Display:", options=['Portfolio', 'Export Base'], key='display_frame_source')
1116
  # if display_frame_source == 'Portfolio':
1117
- # display_frame = st.session_state['portfolio']
1118
  # elif display_frame_source == 'Export Base':
1119
  # display_frame = st.session_state['export_base']
1120
- display_frame = st.session_state['portfolio']
1121
  total_rows = len(display_frame)
1122
  rows_per_page = 500
1123
  total_pages = (total_rows + rows_per_page - 1) // rows_per_page # Ceiling division
@@ -1159,26 +1149,26 @@ with tab2:
1159
 
1160
  # Create player summary dataframe
1161
  player_stats = []
1162
- player_columns = [col for col in st.session_state['portfolio'].columns if col not in excluded_cols]
1163
 
1164
  if type_var == 'Showdown':
1165
  for player in player_names:
1166
  # Create mask for lineups where this player is Captain (first column)
1167
- cpt_mask = st.session_state['portfolio'][player_columns[0]] == player
1168
 
1169
  if cpt_mask.any():
1170
  player_stats.append({
1171
  'Player': f"{player} (CPT)",
1172
  'Lineup Count': cpt_mask.sum(),
1173
- 'Avg Median': st.session_state['portfolio'][cpt_mask]['median'].mean(),
1174
- 'Avg Own': st.session_state['portfolio'][cpt_mask]['Own'].mean(),
1175
- 'Avg Dupes': st.session_state['portfolio'][cpt_mask]['Dupes'].mean(),
1176
- 'Avg Finish %': st.session_state['portfolio'][cpt_mask]['Finish_percentile'].mean(),
1177
- 'Avg Lineup Edge': st.session_state['portfolio'][cpt_mask]['Lineup Edge'].mean(),
1178
  })
1179
 
1180
  # Create mask for lineups where this player is FLEX (other columns)
1181
- flex_mask = st.session_state['portfolio'][player_columns[1:]].apply(
1182
  lambda row: player in list(row), axis=1
1183
  )
1184
 
@@ -1186,32 +1176,32 @@ with tab2:
1186
  player_stats.append({
1187
  'Player': f"{player} (FLEX)",
1188
  'Lineup Count': flex_mask.sum(),
1189
- 'Avg Median': st.session_state['portfolio'][flex_mask]['median'].mean(),
1190
- 'Avg Own': st.session_state['portfolio'][flex_mask]['Own'].mean(),
1191
- 'Avg Dupes': st.session_state['portfolio'][flex_mask]['Dupes'].mean(),
1192
- 'Avg Finish %': st.session_state['portfolio'][flex_mask]['Finish_percentile'].mean(),
1193
- 'Avg Lineup Edge': st.session_state['portfolio'][flex_mask]['Lineup Edge'].mean(),
1194
  })
1195
  else:
1196
  if sport_var == 'CS2':
1197
  # Handle Captain positions
1198
  for player in player_names:
1199
  # Create mask for lineups where this player is Captain (first column)
1200
- cpt_mask = st.session_state['portfolio'][player_columns[0]] == player
1201
 
1202
  if cpt_mask.any():
1203
  player_stats.append({
1204
  'Player': f"{player} (CPT)",
1205
  'Lineup Count': cpt_mask.sum(),
1206
- 'Avg Median': st.session_state['portfolio'][cpt_mask]['median'].mean(),
1207
- 'Avg Own': st.session_state['portfolio'][cpt_mask]['Own'].mean(),
1208
- 'Avg Dupes': st.session_state['portfolio'][cpt_mask]['Dupes'].mean(),
1209
- 'Avg Finish %': st.session_state['portfolio'][cpt_mask]['Finish_percentile'].mean(),
1210
- 'Avg Lineup Edge': st.session_state['portfolio'][cpt_mask]['Lineup Edge'].mean(),
1211
  })
1212
 
1213
  # Create mask for lineups where this player is FLEX (other columns)
1214
- flex_mask = st.session_state['portfolio'][player_columns[1:]].apply(
1215
  lambda row: player in list(row), axis=1
1216
  )
1217
 
@@ -1219,16 +1209,16 @@ with tab2:
1219
  player_stats.append({
1220
  'Player': f"{player} (FLEX)",
1221
  'Lineup Count': flex_mask.sum(),
1222
- 'Avg Median': st.session_state['portfolio'][flex_mask]['median'].mean(),
1223
- 'Avg Own': st.session_state['portfolio'][flex_mask]['Own'].mean(),
1224
- 'Avg Dupes': st.session_state['portfolio'][flex_mask]['Dupes'].mean(),
1225
- 'Avg Finish %': st.session_state['portfolio'][flex_mask]['Finish_percentile'].mean(),
1226
- 'Avg Lineup Edge': st.session_state['portfolio'][flex_mask]['Lineup Edge'].mean(),
1227
  })
1228
  elif sport_var != 'CS2':
1229
  # Original Classic format processing
1230
  for player in player_names:
1231
- player_mask = st.session_state['portfolio'][player_columns].apply(
1232
  lambda row: player in list(row), axis=1
1233
  )
1234
 
@@ -1236,11 +1226,11 @@ with tab2:
1236
  player_stats.append({
1237
  'Player': player,
1238
  'Lineup Count': player_mask.sum(),
1239
- 'Avg Median': st.session_state['portfolio'][player_mask]['median'].mean(),
1240
- 'Avg Own': st.session_state['portfolio'][player_mask]['Own'].mean(),
1241
- 'Avg Dupes': st.session_state['portfolio'][player_mask]['Dupes'].mean(),
1242
- 'Avg Finish %': st.session_state['portfolio'][player_mask]['Finish_percentile'].mean(),
1243
- 'Avg Lineup Edge': st.session_state['portfolio'][player_mask]['Lineup Edge'].mean(),
1244
  })
1245
 
1246
  player_summary = pd.DataFrame(player_stats)
 
187
 
188
  # Update the portfolio in session state
189
  st.session_state['portfolio'] = portfolio
 
190
 
191
  # Store the match dictionary for reference
192
  st.session_state['portfolio_to_projection_matches'] = portfolio_match_dict
 
243
  st.write(f"- {name}")
244
  else:
245
  st.success("All portfolio names were matched to projections!")
246
+ working_frame = st.session_state['portfolio'].copy()
247
  st.session_state['origin_portfolio'] = st.session_state['portfolio'].copy()
248
 
249
  # with tab2:
 
761
  with tab2:
762
  if 'portfolio' in st.session_state and 'projections_df' in st.session_state:
763
 
764
+
765
  excluded_cols = ['salary', 'median', 'Own', 'Finish_percentile', 'Dupes', 'Stack', 'Win%', 'Lineup Edge', 'Weighted Own', 'Geomean']
766
  with st.container():
767
 
 
769
  with col1:
770
  site_var = st.selectbox("Select Site", ['Draftkings', 'Fanduel'])
771
  if st.button('Reset Portfolio', key='reset_port'):
772
+ working_frame = st.session_state['origin_portfolio'].copy()
773
  if st.button('Clear data', key='reset3'):
774
  st.session_state.clear()
775
 
 
781
  Contest_Size = st.number_input("Enter Contest Size", value=25000, min_value=1, step=1)
782
  strength_var = st.selectbox("Select field strength", ['Average', 'Sharp', 'Weak'])
783
 
784
+ if 'working_frame' not in st.session_state:
785
+ if site_var == 'Draftkings':
786
+ if type_var == 'Classic':
787
+ if sport_var == 'CS2':
788
+ map_dict = {
789
+ 'pos_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['position'])),
790
+ 'team_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['team'])),
791
+ 'salary_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'])),
792
+ 'proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'])),
793
+ 'own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'])),
794
+ 'own_percent_rank':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'].rank(pct=True))),
795
+ 'cpt_salary_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'] * 1.5)),
796
+ 'cpt_proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'] * 1.5)),
797
+ 'cpt_own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['captain ownership']))
798
+ }
799
+ elif sport_var != 'CS2':
800
+ map_dict = {
801
+ 'pos_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['position'])),
802
+ 'team_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['team'])),
803
+ 'salary_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'])),
804
+ 'proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'])),
805
+ 'own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'])),
806
+ 'own_percent_rank':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'].rank(pct=True))),
807
+ 'cpt_salary_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'])),
808
+ 'cpt_proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'] * 1.5)),
809
+ 'cpt_own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['captain ownership']))
810
+ }
811
+ elif type_var == 'Showdown':
812
  map_dict = {
813
  'pos_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['position'])),
814
  'team_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['team'])),
 
820
  'cpt_proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'] * 1.5)),
821
  'cpt_own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['captain ownership']))
822
  }
823
+ elif site_var == 'Fanduel':
 
 
 
 
 
 
 
 
 
 
 
 
824
  map_dict = {
825
  'pos_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['position'])),
826
  'team_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['team'])),
 
828
  'proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'])),
829
  'own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'])),
830
  'own_percent_rank':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['ownership'].rank(pct=True))),
831
+ 'cpt_salary_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['salary'])),
832
  'cpt_proj_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['median'] * 1.5)),
833
  'cpt_own_map':dict(zip(st.session_state['projections_df']['player_names'], st.session_state['projections_df']['captain ownership']))
834
  }
835
+ if type_var == 'Classic':
836
+ if sport_var == 'CS2':
837
+ # Calculate salary (CPT uses cpt_salary_map, others use salary_map)
838
+ st.session_state['working_frame']['salary'] = st.session_state['working_frame'].apply(
839
+ lambda row: map_dict['cpt_salary_map'].get(row.iloc[0], 0) +
840
+ sum(map_dict['salary_map'].get(player, 0) for player in row.iloc[1:]),
841
+ axis=1
842
+ )
843
+
844
+ # Calculate median (CPT uses cpt_proj_map, others use proj_map)
845
+ st.session_state['working_frame']['median'] = st.session_state['working_frame'].apply(
846
+ lambda row: map_dict['cpt_proj_map'].get(row.iloc[0], 0) +
847
+ sum(map_dict['proj_map'].get(player, 0) for player in row.iloc[1:]),
848
+ axis=1
849
+ )
850
+
851
+ # Calculate ownership (CPT uses cpt_own_map, others use own_map)
852
+ st.session_state['working_frame']['Own'] = st.session_state['working_frame'].apply(
853
+ lambda row: map_dict['cpt_own_map'].get(row.iloc[0], 0) +
854
+ sum(map_dict['own_map'].get(player, 0) for player in row.iloc[1:]),
855
+ axis=1
856
+ )
857
+
858
+ elif sport_var != 'CS2':
859
+ st.session_state['working_frame']['salary'] = st.session_state['working_frame'].apply(lambda row: sum(map_dict['salary_map'].get(player, 0) for player in row), axis=1)
860
+ st.session_state['working_frame']['median'] = st.session_state['working_frame'].apply(lambda row: sum(map_dict['proj_map'].get(player, 0) for player in row), axis=1)
861
+ st.session_state['working_frame']['Own'] = st.session_state['working_frame'].apply(lambda row: sum(map_dict['own_map'].get(player, 0) for player in row), axis=1)
862
+ if stack_dict is not None:
863
+ st.session_state['working_frame']['Stack'] = st.session_state['working_frame'].index.map(stack_dict)
864
+ elif type_var == 'Showdown':
865
  # Calculate salary (CPT uses cpt_salary_map, others use salary_map)
866
+ st.session_state['working_frame']['salary'] = st.session_state['working_frame'].apply(
867
  lambda row: map_dict['cpt_salary_map'].get(row.iloc[0], 0) +
868
  sum(map_dict['salary_map'].get(player, 0) for player in row.iloc[1:]),
869
  axis=1
870
  )
871
 
872
  # Calculate median (CPT uses cpt_proj_map, others use proj_map)
873
+ st.session_state['working_frame']['median'] = st.session_state['working_frame'].apply(
874
  lambda row: map_dict['cpt_proj_map'].get(row.iloc[0], 0) +
875
  sum(map_dict['proj_map'].get(player, 0) for player in row.iloc[1:]),
876
  axis=1
877
  )
878
 
879
  # Calculate ownership (CPT uses cpt_own_map, others use own_map)
880
+ st.session_state['working_frame']['Own'] = st.session_state['working_frame'].apply(
881
  lambda row: map_dict['cpt_own_map'].get(row.iloc[0], 0) +
882
  sum(map_dict['own_map'].get(player, 0) for player in row.iloc[1:]),
883
  axis=1
884
  )
885
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
886
  col1, col2 = st.columns([2, 8])
887
  with col1:
888
  if 'trimming_dict_maxes' not in st.session_state:
 
906
  stack_remove = st.multiselect("If Specific Stacks, Which to remove?", options=sorted(list(set(stack_dict.values()))), default=[])
907
 
908
  submitted = st.form_submit_button("Submit")
909
+ if submitted:
910
+ st.session_state['working_frame'] = predict_dupes(st.session_state['working_frame'], map_dict, site_var, type_var, Contest_Size, strength_var, sport_var)
911
+ if 'trimming_dict_maxes' not in st.session_state:
912
+ st.session_state['trimming_dict_maxes'] = {
913
+ 'Own': st.session_state['working_frame']['Own'].max(),
914
+ 'Geomean': st.session_state['working_frame']['Geomean'].max(),
915
+ 'Weighted Own': st.session_state['working_frame']['Weighted Own'].max(),
916
+ 'median': st.session_state['working_frame']['median'].max(),
917
+ 'Finish_percentile': st.session_state['working_frame']['Finish_percentile'].max()
918
+ }
919
+ st.session_state['working_frame'] = st.session_state['working_frame'][st.session_state['working_frame']['Dupes'] <= max_dupes]
920
+ st.session_state['working_frame'] = st.session_state['working_frame'][st.session_state['working_frame']['salary'] >= min_salary]
921
+ st.session_state['working_frame'] = st.session_state['working_frame'][st.session_state['working_frame']['salary'] <= max_salary]
922
+ st.session_state['working_frame'] = st.session_state['working_frame'][st.session_state['working_frame']['Finish_percentile'] <= max_finish_percentile]
923
+ st.session_state['working_frame'] = st.session_state['working_frame'][st.session_state['working_frame']['Lineup Edge'] >= min_lineup_edge]
924
+ if stack_dict is not None:
925
+ if stack_toggle == 'All Stacks':
926
+ st.session_state['working_frame'] = st.session_state['working_frame']
927
+ st.session_state['working_frame'] = st.session_state['working_frame'][~st.session_state['working_frame']['Stack'].isin(stack_remove)]
928
+ else:
929
+ st.session_state['working_frame'] = st.session_state['working_frame'][st.session_state['working_frame']['Stack'].isin(stack_selections)]
930
+ st.session_state['working_frame'] = st.session_state['working_frame'][~st.session_state['working_frame']['Stack'].isin(stack_remove)]
931
+ st.session_state['working_frame'] = st.session_state['working_frame'].sort_values(by='median', ascending=False)
932
+ st.session_state['export_merge'] = st.session_state['working_frame'].copy()
933
+
934
  with st.expander('Micro Filter Options'):
935
  with st.form(key='micro_filter_form'):
936
  player_names = set()
937
+ for col in st.session_state['working_frame'].columns:
938
  if col not in excluded_cols:
939
+ player_names.update(st.session_state['working_frame'][col].unique())
940
  player_lock = st.multiselect("Lock players?", options=sorted(list(player_names)), default=[])
941
  player_remove = st.multiselect("Remove players?", options=sorted(list(player_names)), default=[])
942
 
943
  submitted = st.form_submit_button("Submit")
944
+ if submitted:
945
+ st.session_state['working_frame'] = predict_dupes(st.session_state['working_frame'], map_dict, site_var, type_var, Contest_Size, strength_var, sport_var)
946
+ if 'trimming_dict_maxes' not in st.session_state:
947
+ st.session_state['trimming_dict_maxes'] = {
948
+ 'Own': st.session_state['working_frame']['Own'].max(),
949
+ 'Geomean': st.session_state['working_frame']['Geomean'].max(),
950
+ 'Weighted Own': st.session_state['working_frame']['Weighted Own'].max(),
951
+ 'median': st.session_state['working_frame']['median'].max(),
952
+ 'Finish_percentile': st.session_state['working_frame']['Finish_percentile'].max()
953
+ }
954
+ if player_remove:
955
+ # Create mask for lineups that contain any of the removed players
956
+ player_columns = [col for col in st.session_state['working_frame'].columns if col not in excluded_cols]
957
+ remove_mask = st.session_state['working_frame'][player_columns].apply(
958
+ lambda row: not any(player in list(row) for player in player_remove), axis=1
959
+ )
960
+ st.session_state['working_frame'] = st.session_state['working_frame'][remove_mask]
961
+
962
+ if player_lock:
963
+ # Create mask for lineups that contain all locked players
964
+ player_columns = [col for col in st.session_state['working_frame'].columns if col not in excluded_cols]
965
+
966
+ lock_mask = st.session_state['working_frame'][player_columns].apply(
967
+ lambda row: all(player in list(row) for player in player_lock), axis=1
968
+ )
969
+ st.session_state['working_frame'] = st.session_state['working_frame'][lock_mask]
970
+ st.session_state['working_frame'] = st.session_state['working_frame'].sort_values(by='median', ascending=False)
971
+ st.session_state['export_merge'] = st.session_state['working_frame'].copy()
972
+
973
  with st.expander('Trimming Options'):
974
  st.info("Make sure you filter before trimming if you want to filter, trimming before a filter will reset your portfolio")
975
  with st.form(key='trim_form'):
 
999
  submitted = st.form_submit_button("Trim")
1000
  if submitted:
1001
  st.write('initiated')
1002
+ st.session_state['working_frame'] = predict_dupes(st.session_state['working_frame'], map_dict, site_var, type_var, Contest_Size, strength_var, sport_var)
1003
  if 'trimming_dict_maxes' not in st.session_state:
1004
  st.session_state['trimming_dict_maxes'] = {
1005
+ 'Own': st.session_state['working_frame']['Own'].max(),
1006
+ 'Geomean': st.session_state['working_frame']['Geomean'].max(),
1007
+ 'Weighted Own': st.session_state['working_frame']['Weighted Own'].max(),
1008
+ 'median': st.session_state['working_frame']['median'].max(),
1009
+ 'Finish_percentile': st.session_state['working_frame']['Finish_percentile'].max()
1010
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1011
 
1012
+ st.session_state['working_frame'] = trim_portfolio(st.session_state['working_frame'], trim_slack_var, performance_type, own_type, performance_threshold_high, performance_threshold_low, own_threshold_high, own_threshold_low)
1013
+ st.session_state['working_frame'] = st.session_state['working_frame'].sort_values(by='median', ascending=False)
1014
+ st.session_state['export_merge'] = st.session_state['working_frame'].copy()
 
 
 
 
 
 
 
 
 
1015
 
1016
  with col2:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1017
  with st.expander("Download options"):
1018
  if stack_dict is not None:
1019
  download_type = st.selectbox("Simple or Advanced Download?", options=['Simple', 'Advanced'], key='download_choice')
1020
  if download_type == 'Simple':
1021
+ st.session_state['export_file'] = st.session_state['working_frame'].copy()
1022
  for col in st.session_state['export_file'].columns:
1023
  if col not in excluded_cols:
1024
  st.session_state['export_file'][col] = st.session_state['export_file'][col].map(st.session_state['export_dict'])
 
1047
  # Create a unique key for each number input
1048
  key = f"stack_count_{stack}"
1049
  # Get the current count of this stack in the portfolio
1050
+ current_stack_count = len(st.session_state['working_frame'][st.session_state['working_frame']['Stack'] == stack])
1051
  # Create number input with current value and max value based on actual count
1052
  st.session_state.stack_multipliers[stack] = st.number_input(
1053
  f"{stack} count",
 
1058
  key=key
1059
  )
1060
 
1061
+ portfolio_copy = st.session_state['working_frame'].copy()
1062
 
1063
  submitted = st.form_submit_button("Submit")
1064
  if submitted:
 
1085
  st.session_state['export_file'][col] = st.session_state['export_file'][col].map(st.session_state['export_dict'])
1086
  st.write('Export portfolio updated!')
1087
  else:
1088
+ st.session_state['export_file'] = st.session_state['working_frame'].copy()
1089
  if 'export_base' not in st.session_state:
1090
+ st.session_state['export_base'] = pd.DataFrame(columns=st.session_state['working_frame'].columns)
1091
  for col in st.session_state['export_file'].columns:
1092
  if col not in excluded_cols:
1093
  st.session_state['export_file'][col] = st.session_state['export_file'][col].map(st.session_state['export_dict'])
 
1104
 
1105
  # display_frame_source = st.selectbox("Display:", options=['Portfolio', 'Export Base'], key='display_frame_source')
1106
  # if display_frame_source == 'Portfolio':
1107
+ # display_frame = st.session_state['working_frame']
1108
  # elif display_frame_source == 'Export Base':
1109
  # display_frame = st.session_state['export_base']
1110
+ display_frame = st.session_state['working_frame']
1111
  total_rows = len(display_frame)
1112
  rows_per_page = 500
1113
  total_pages = (total_rows + rows_per_page - 1) // rows_per_page # Ceiling division
 
1149
 
1150
  # Create player summary dataframe
1151
  player_stats = []
1152
+ player_columns = [col for col in st.session_state['working_frame'].columns if col not in excluded_cols]
1153
 
1154
  if type_var == 'Showdown':
1155
  for player in player_names:
1156
  # Create mask for lineups where this player is Captain (first column)
1157
+ cpt_mask = st.session_state['working_frame'][player_columns[0]] == player
1158
 
1159
  if cpt_mask.any():
1160
  player_stats.append({
1161
  'Player': f"{player} (CPT)",
1162
  'Lineup Count': cpt_mask.sum(),
1163
+ 'Avg Median': st.session_state['working_frame'][cpt_mask]['median'].mean(),
1164
+ 'Avg Own': st.session_state['working_frame'][cpt_mask]['Own'].mean(),
1165
+ 'Avg Dupes': st.session_state['working_frame'][cpt_mask]['Dupes'].mean(),
1166
+ 'Avg Finish %': st.session_state['working_frame'][cpt_mask]['Finish_percentile'].mean(),
1167
+ 'Avg Lineup Edge': st.session_state['working_frame'][cpt_mask]['Lineup Edge'].mean(),
1168
  })
1169
 
1170
  # Create mask for lineups where this player is FLEX (other columns)
1171
+ flex_mask = st.session_state['working_frame'][player_columns[1:]].apply(
1172
  lambda row: player in list(row), axis=1
1173
  )
1174
 
 
1176
  player_stats.append({
1177
  'Player': f"{player} (FLEX)",
1178
  'Lineup Count': flex_mask.sum(),
1179
+ 'Avg Median': st.session_state['working_frame'][flex_mask]['median'].mean(),
1180
+ 'Avg Own': st.session_state['working_frame'][flex_mask]['Own'].mean(),
1181
+ 'Avg Dupes': st.session_state['working_frame'][flex_mask]['Dupes'].mean(),
1182
+ 'Avg Finish %': st.session_state['working_frame'][flex_mask]['Finish_percentile'].mean(),
1183
+ 'Avg Lineup Edge': st.session_state['working_frame'][flex_mask]['Lineup Edge'].mean(),
1184
  })
1185
  else:
1186
  if sport_var == 'CS2':
1187
  # Handle Captain positions
1188
  for player in player_names:
1189
  # Create mask for lineups where this player is Captain (first column)
1190
+ cpt_mask = st.session_state['working_frame'][player_columns[0]] == player
1191
 
1192
  if cpt_mask.any():
1193
  player_stats.append({
1194
  'Player': f"{player} (CPT)",
1195
  'Lineup Count': cpt_mask.sum(),
1196
+ 'Avg Median': st.session_state['working_frame'][cpt_mask]['median'].mean(),
1197
+ 'Avg Own': st.session_state['working_frame'][cpt_mask]['Own'].mean(),
1198
+ 'Avg Dupes': st.session_state['working_frame'][cpt_mask]['Dupes'].mean(),
1199
+ 'Avg Finish %': st.session_state['working_frame'][cpt_mask]['Finish_percentile'].mean(),
1200
+ 'Avg Lineup Edge': st.session_state['working_frame'][cpt_mask]['Lineup Edge'].mean(),
1201
  })
1202
 
1203
  # Create mask for lineups where this player is FLEX (other columns)
1204
+ flex_mask = st.session_state['working_frame'][player_columns[1:]].apply(
1205
  lambda row: player in list(row), axis=1
1206
  )
1207
 
 
1209
  player_stats.append({
1210
  'Player': f"{player} (FLEX)",
1211
  'Lineup Count': flex_mask.sum(),
1212
+ 'Avg Median': st.session_state['working_frame'][flex_mask]['median'].mean(),
1213
+ 'Avg Own': st.session_state['working_frame'][flex_mask]['Own'].mean(),
1214
+ 'Avg Dupes': st.session_state['working_frame'][flex_mask]['Dupes'].mean(),
1215
+ 'Avg Finish %': st.session_state['working_frame'][flex_mask]['Finish_percentile'].mean(),
1216
+ 'Avg Lineup Edge': st.session_state['working_frame'][flex_mask]['Lineup Edge'].mean(),
1217
  })
1218
  elif sport_var != 'CS2':
1219
  # Original Classic format processing
1220
  for player in player_names:
1221
+ player_mask = st.session_state['working_frame'][player_columns].apply(
1222
  lambda row: player in list(row), axis=1
1223
  )
1224
 
 
1226
  player_stats.append({
1227
  'Player': player,
1228
  'Lineup Count': player_mask.sum(),
1229
+ 'Avg Median': st.session_state['working_frame'][player_mask]['median'].mean(),
1230
+ 'Avg Own': st.session_state['working_frame'][player_mask]['Own'].mean(),
1231
+ 'Avg Dupes': st.session_state['working_frame'][player_mask]['Dupes'].mean(),
1232
+ 'Avg Finish %': st.session_state['working_frame'][player_mask]['Finish_percentile'].mean(),
1233
+ 'Avg Lineup Edge': st.session_state['working_frame'][player_mask]['Lineup Edge'].mean(),
1234
  })
1235
 
1236
  player_summary = pd.DataFrame(player_stats)