James McCool commited on
Commit
42c2829
·
1 Parent(s): 63139f0

Enhance predict_dupes function by adding max_salary parameter for improved salary handling and adjusting sample range slider step in app.py for better user experience.

Browse files
Files changed (2) hide show
  1. app.py +2 -2
  2. global_func/predict_dupes.py +106 -247
app.py CHANGED
@@ -1093,7 +1093,7 @@ with tab2:
1093
  st.session_state['working_frame']['median'] = st.session_state['working_frame']['median'].astype('float32')
1094
  st.session_state['working_frame']['salary'] = st.session_state['working_frame']['salary'].astype('uint16')
1095
 
1096
- st.session_state['base_frame'] = predict_dupes(st.session_state['working_frame'], st.session_state['map_dict'], site_var, type_var, Contest_Size, strength_var, sport_var)
1097
  st.session_state['working_frame'] = st.session_state['base_frame'].copy()
1098
  # st.session_state['highest_owned_teams'] = st.session_state['projections_df'][~st.session_state['projections_df']['position'].isin(['P', 'SP'])].groupby('team')['ownership'].sum().sort_values(ascending=False).head(3).index.tolist()
1099
  # st.session_state['highest_owned_pitchers'] = st.session_state['projections_df'][st.session_state['projections_df']['position'].isin(['P', 'SP'])]['player_names'].sort_values(by='ownership', ascending=False).head(3).tolist()
@@ -1441,7 +1441,7 @@ with tab2:
1441
  with st.form(key='Stratification'):
1442
  sorting_choice = st.selectbox("Stat Choice", options=['median', 'Own', 'Weighted Own', 'Geomean', 'Lineup Edge', 'Finish_percentile', 'Diversity'], index=0)
1443
  lineup_target = st.number_input("Lineups to produce", value=150, min_value=1, step=1)
1444
- strat_sample = st.slider("Sample range", value=[0.0, 100.0], min_value=0.0, max_value=100.0, step=0.05)
1445
  submitted_col, export_col = st.columns(2)
1446
  st.info("Portfolio Button applies to your overall Portfolio, Export button applies to your Custom Export")
1447
  with submitted_col:
 
1093
  st.session_state['working_frame']['median'] = st.session_state['working_frame']['median'].astype('float32')
1094
  st.session_state['working_frame']['salary'] = st.session_state['working_frame']['salary'].astype('uint16')
1095
 
1096
+ st.session_state['base_frame'] = predict_dupes(st.session_state['working_frame'], st.session_state['map_dict'], site_var, type_var, Contest_Size, strength_var, sport_var, salary_max)
1097
  st.session_state['working_frame'] = st.session_state['base_frame'].copy()
1098
  # st.session_state['highest_owned_teams'] = st.session_state['projections_df'][~st.session_state['projections_df']['position'].isin(['P', 'SP'])].groupby('team')['ownership'].sum().sort_values(ascending=False).head(3).index.tolist()
1099
  # st.session_state['highest_owned_pitchers'] = st.session_state['projections_df'][st.session_state['projections_df']['position'].isin(['P', 'SP'])]['player_names'].sort_values(by='ownership', ascending=False).head(3).tolist()
 
1441
  with st.form(key='Stratification'):
1442
  sorting_choice = st.selectbox("Stat Choice", options=['median', 'Own', 'Weighted Own', 'Geomean', 'Lineup Edge', 'Finish_percentile', 'Diversity'], index=0)
1443
  lineup_target = st.number_input("Lineups to produce", value=150, min_value=1, step=1)
1444
+ strat_sample = st.slider("Sample range", value=[0.0, 100.0], min_value=0.0, max_value=100.0, step=1.0)
1445
  submitted_col, export_col = st.columns(2)
1446
  st.info("Portfolio Button applies to your overall Portfolio, Export button applies to your Custom Export")
1447
  with submitted_col:
global_func/predict_dupes.py CHANGED
@@ -4,7 +4,6 @@ import pandas as pd
4
  import time
5
  import math
6
  from difflib import SequenceMatcher
7
- import heapq
8
 
9
  def calculate_weighted_ownership_vectorized(ownership_array):
10
  """
@@ -106,182 +105,8 @@ def calculate_player_similarity_score_vectorized(portfolio, player_columns):
106
 
107
  return similarity_scores
108
 
109
- def predict_dupes_vectorized(portfolio, maps_dict, site_var, type_var, Contest_Size, strength_var, sport_var):
110
- """
111
- Vectorized version of predict_dupes using NumPy arrays for better performance.
112
- """
113
- # Set multipliers based on strength
114
- if strength_var == 'Weak':
115
- dupes_multiplier = 0.75
116
- percentile_multiplier = 0.90
117
- elif strength_var == 'Average':
118
- dupes_multiplier = 1.00
119
- percentile_multiplier = 1.00
120
- elif strength_var == 'Sharp':
121
- dupes_multiplier = 1.25
122
- percentile_multiplier = 1.10
123
-
124
- max_ownership = max(maps_dict['own_map'].values()) / 100
125
- average_ownership = np.mean(list(maps_dict['own_map'].values())) / 100
126
-
127
- # Convert portfolio to NumPy arrays for faster operations
128
- portfolio_values = portfolio.values
129
- n_rows = len(portfolio)
130
-
131
- # Pre-allocate arrays for ownership data
132
- if site_var == 'Fanduel':
133
- if type_var == 'Showdown':
134
- num_players = 5
135
- salary_cap = 60000
136
- player_cols = list(range(5)) # First 5 columns are players
137
- elif type_var == 'Classic':
138
- if sport_var == 'WNBA':
139
- num_players = len([col for col in portfolio.columns if col not in ['salary', 'median', 'Own']])
140
- salary_cap = 40000
141
- player_cols = list(range(num_players))
142
- else:
143
- num_players = len([col for col in portfolio.columns if col not in ['salary', 'median', 'Own']])
144
- salary_cap = 60000
145
- player_cols = list(range(num_players))
146
- elif site_var == 'Draftkings':
147
- if type_var == 'Showdown':
148
- num_players = 6
149
- salary_cap = 50000
150
- player_cols = list(range(6))
151
- elif type_var == 'Classic':
152
- if sport_var == 'CS2':
153
- num_players = 6
154
- salary_cap = 50000
155
- player_cols = list(range(6))
156
- elif sport_var == 'LOL':
157
- num_players = 7
158
- salary_cap = 50000
159
- player_cols = list(range(7))
160
- else:
161
- num_players = len([col for col in portfolio.columns if col not in ['salary', 'median', 'Own']])
162
- salary_cap = 50000
163
- player_cols = list(range(num_players))
164
-
165
- # Pre-allocate ownership arrays
166
- ownership_array = np.zeros((n_rows, num_players), dtype=np.float32)
167
- ownership_rank_array = np.zeros((n_rows, num_players), dtype=np.float32)
168
-
169
- # Vectorized ownership mapping
170
- for i, col_idx in enumerate(player_cols):
171
- if i == 0 and type_var == 'Showdown': # Captain
172
- ownership_array[:, i] = np.vectorize(lambda x: maps_dict['cpt_own_map'].get(x, 0))(portfolio_values[:, col_idx]) / 100
173
- ownership_rank_array[:, i] = np.vectorize(lambda x: maps_dict['cpt_own_map'].get(x, 0))(portfolio_values[:, col_idx])
174
- else: # Flex players
175
- ownership_array[:, i] = np.vectorize(lambda x: maps_dict['own_map'].get(x, 0))(portfolio_values[:, col_idx]) / 100
176
- ownership_rank_array[:, i] = np.vectorize(lambda x: maps_dict['own_map'].get(x, 0))(portfolio_values[:, col_idx])
177
-
178
- # Calculate ranks for flex players (excluding captain)
179
- if type_var == 'Showdown':
180
- flex_ownerships = ownership_rank_array[:, 1:].flatten()
181
- flex_rank = pd.Series(flex_ownerships).rank(pct=True).values.reshape(n_rows, -1)
182
- ownership_rank_array[:, 1:] = flex_rank
183
-
184
- # Convert to percentile ranks
185
- ownership_rank_array = ownership_rank_array / 100
186
-
187
- # Vectorized calculations
188
- own_product = np.prod(ownership_array, axis=1)
189
- own_average = (portfolio_values[:, portfolio.columns.get_loc('Own')].max() * 0.33) / 100
190
- own_sum = np.sum(ownership_array, axis=1)
191
- avg_own_rank = np.mean(ownership_rank_array, axis=1)
192
-
193
- # Calculate dupes formula vectorized
194
- salary_col = portfolio.columns.get_loc('salary')
195
- own_col = portfolio.columns.get_loc('Own')
196
-
197
- dupes_calc = (own_product * avg_own_rank) * Contest_Size + \
198
- ((portfolio_values[:, salary_col] - (salary_cap - portfolio_values[:, own_col])) / 100) - \
199
- ((salary_cap - portfolio_values[:, salary_col]) / 100)
200
-
201
- dupes_calc *= dupes_multiplier
202
-
203
- # Round and handle negative values
204
- dupes = np.where(np.round(dupes_calc, 0) <= 0, 0, np.round(dupes_calc, 0) - 1)
205
-
206
- # Calculate own_ratio vectorized
207
- max_own_mask = np.any(ownership_array == max_ownership, axis=1)
208
- own_ratio = np.where(max_own_mask,
209
- own_sum / own_average,
210
- (own_sum - max_ownership) / own_average)
211
-
212
- # Calculate Finish_percentile vectorized
213
- percentile_cut_scalar = portfolio_values[:, portfolio.columns.get_loc('median')].max()
214
-
215
- if type_var == 'Classic':
216
- own_ratio_nerf = 2 if sport_var == 'CS2' or sport_var == 'LOL' else 1.5
217
- elif type_var == 'Showdown':
218
- own_ratio_nerf = 1.5
219
-
220
- median_col = portfolio.columns.get_loc('median')
221
- finish_percentile = (own_ratio - own_ratio_nerf) / ((5 * (portfolio_values[:, median_col] / percentile_cut_scalar)) / 3)
222
- finish_percentile = np.where(finish_percentile < 0.0005, 0.0005, finish_percentile / 2)
223
-
224
- # Calculate other metrics vectorized
225
- ref_proj = portfolio_values[:, median_col].max()
226
- max_proj = ref_proj + 10
227
- min_proj = ref_proj - 10
228
- avg_ref = (max_proj + min_proj) / 2
229
-
230
- win_percent = (((portfolio_values[:, median_col] / avg_ref) - (0.1 + ((ref_proj - portfolio_values[:, median_col])/100))) / (Contest_Size / 1000)) / 10
231
- max_allowed_win = (1 / Contest_Size) * 5
232
- win_percent = win_percent / win_percent.max() * max_allowed_win
233
-
234
- finish_percentile = finish_percentile + 0.005 + (0.005 * (Contest_Size / 10000))
235
- finish_percentile *= percentile_multiplier
236
- win_percent *= (1 - finish_percentile)
237
-
238
- # Calculate low ownership count vectorized
239
- low_own_count = np.sum(ownership_array < 0.10, axis=1)
240
- finish_percentile = np.where(low_own_count <= 0,
241
- finish_percentile,
242
- finish_percentile / low_own_count)
243
-
244
- # Calculate Lineup Edge vectorized
245
- lineup_edge = win_percent * ((0.5 - finish_percentile) * (Contest_Size / 2.5))
246
- lineup_edge = np.where(dupes > 0, lineup_edge / (dupes + 1), lineup_edge)
247
- lineup_edge = lineup_edge - lineup_edge.mean()
248
-
249
- # Calculate Weighted Own vectorized
250
- weighted_own = calculate_weighted_ownership_vectorized(ownership_array)
251
-
252
- # Calculate Geomean vectorized
253
- geomean = np.power(np.prod(ownership_array * 100, axis=1), 1 / num_players)
254
-
255
- # Calculate Diversity vectorized
256
- diversity = calculate_player_similarity_score_vectorized(portfolio, player_cols)
257
-
258
- # Create result DataFrame with optimized data types
259
- result_data = {
260
- 'Dupes': dupes.astype('uint16'),
261
- 'median': portfolio_values[:, portfolio.columns.get_loc('median')].astype('float32'),
262
- 'Own': portfolio_values[:, portfolio.columns.get_loc('Own')].astype('float32'),
263
- 'salary': portfolio_values[:, portfolio.columns.get_loc('salary')].astype('uint16'),
264
- 'Finish_percentile': finish_percentile.astype('float32'),
265
- 'Win%': win_percent.astype('float32'),
266
- 'Lineup Edge': lineup_edge.astype('float32'),
267
- 'Weighted Own': weighted_own.astype('float32'),
268
- 'Geomean': geomean.astype('float32'),
269
- 'Diversity': diversity.astype('float32')
270
- }
271
-
272
- # Add Size column if it exists
273
- if 'Size' in portfolio.columns:
274
- result_data['Size'] = portfolio_values[:, portfolio.columns.get_loc('Size')].astype('uint16')
275
-
276
- # Add player columns back
277
- for i, col_name in enumerate(portfolio.columns[:num_players]):
278
- result_data[col_name] = portfolio_values[:, i]
279
-
280
- return pd.DataFrame(result_data)
281
-
282
  # Keep the original function for backward compatibility
283
- def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, strength_var, sport_var):
284
- player_columns = [col for col in portfolio.columns[:5] if col not in ['salary', 'median', 'Own']]
285
  if strength_var == 'Weak':
286
  dupes_multiplier = .75
287
  percentile_multiplier = .90
@@ -292,22 +117,22 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
292
  dupes_multiplier = 1.25
293
  percentile_multiplier = 1.10
294
  max_ownership = max(maps_dict['own_map'].values()) / 100
295
- top_x_ownership_keys = heapq.nlargest(len(player_columns), maps_dict['own_map'], key=maps_dict['own_map'].get)
296
  average_ownership = np.mean(list(maps_dict['own_map'].values())) / 100
297
  if site_var == 'Fanduel':
298
  if type_var == 'Showdown':
299
  dup_count_columns = ['CPT_Own_percent_rank', 'FLEX1_Own_percent_rank', 'FLEX2_Own_percent_rank', 'FLEX3_Own_percent_rank', 'FLEX4_Own_percent_rank']
300
  own_columns = ['CPT_Own', 'FLEX1_Own', 'FLEX2_Own', 'FLEX3_Own', 'FLEX4_Own']
301
- calc_columns = ['own_product', 'own_average', 'own_sum', 'avg_own_rank', 'own_rank_percentile', 'dupes_calc', 'low_own_count', 'own_ratio', 'Ref_Proj', 'Max_Proj', 'Min_Proj', 'Avg_Ref', 'own_ratio', 'top_x_presence']
302
  # Get the original player columns (first 5 columns excluding salary, median, Own)
303
  player_columns = [col for col in portfolio.columns[:5] if col not in ['salary', 'median', 'Own']]
304
 
305
  flex_ownerships = pd.concat([
306
- portfolio.iloc[:,1].map(maps_dict['own_map']),
307
- portfolio.iloc[:,2].map(maps_dict['own_map']),
308
- portfolio.iloc[:,3].map(maps_dict['own_map']),
309
- portfolio.iloc[:,4].map(maps_dict['own_map'])
310
- ])
 
311
  flex_rank = flex_ownerships.rank(pct=True)
312
 
313
  # Assign ranks back to individual columns using the same rank scale
@@ -325,31 +150,31 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
325
  portfolio['FLEX4_Own'] = portfolio.iloc[:,4].map(maps_dict['own_map']).astype('float32') / 100
326
  portfolio['FLEX5_Own'] = portfolio.iloc[:,5].map(maps_dict['own_map']).astype('float32') / 100
327
 
328
- portfolio['own_product'] = (portfolio[own_columns].product(axis=1))
329
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
330
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
331
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
332
- portfolio['own_rank_percentile'] = portfolio['Own'].rank(pct=True)
333
- portfolio['top_x_presence'] = portfolio[player_columns].apply(
334
- lambda row: sum(1 for player in row if player in top_x_ownership_keys), axis=1
335
- )
336
 
337
  # Calculate dupes formula
338
- portfolio['dupes_calc'] = (portfolio['avg_own_rank'] / 1000) * (Contest_Size / 100) + ((portfolio['salary'] - (60000 - portfolio['Own'])) / 100) - ((59700 - portfolio['salary']) / 100)
339
- portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (100 + (Contest_Size / 1000))) * portfolio['top_x_presence']
340
 
341
  # Round and handle negative values
342
  portfolio['Dupes'] = np.where(
343
- np.round(portfolio['dupes_calc'], 0) <= 0,
 
 
 
 
 
344
  0,
345
- np.round((portfolio['dupes_calc'] * (1 + portfolio['own_rank_percentile'])), 0) - 1
346
  )
347
-
348
  elif type_var == 'Classic':
349
  num_players = len([col for col in portfolio.columns if col not in ['salary', 'median', 'Own']])
350
  dup_count_columns = [f'player_{i}_percent_rank' for i in range(1, num_players + 1)]
351
  own_columns = [f'player_{i}_own' for i in range(1, num_players + 1)]
352
- calc_columns = ['own_product', 'own_average', 'own_sum', 'avg_own_rank', 'own_rank_percentile', 'dupes_calc', 'low_own_count', 'own_ratio', 'Ref_Proj', 'Max_Proj', 'Min_Proj', 'Avg_Ref', 'own_ratio', 'top_x_presence']
353
  # Get the original player columns (first num_players columns excluding salary, median, Own)
354
  player_columns = [col for col in portfolio.columns[:num_players] if col not in ['salary', 'median', 'Own']]
355
 
@@ -357,22 +182,23 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
357
  portfolio[f'player_{i}_percent_rank'] = portfolio.iloc[:,i-1].map(maps_dict['own_percent_rank'])
358
  portfolio[f'player_{i}_own'] = portfolio.iloc[:,i-1].map(maps_dict['own_map']).astype('float32') / 100
359
 
360
- portfolio['own_product'] = (portfolio[own_columns].product(axis=1))
361
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
362
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
363
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
364
- portfolio['own_rank_percentile'] = portfolio['Own'].rank(pct=True)
365
- portfolio['top_x_presence'] = portfolio[player_columns].apply(
366
- lambda row: sum(1 for player in row if player in top_x_ownership_keys), axis=1
367
- )
368
 
369
- portfolio['dupes_calc'] = (portfolio['avg_own_rank'] / 1000) * (Contest_Size / 100) + ((portfolio['salary'] - (60000 - portfolio['Own'])) / 100) - ((59700 - portfolio['salary']) / 100)
370
- portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (100 + (Contest_Size / 1000))) * portfolio['top_x_presence']
371
  # Round and handle negative values
372
  portfolio['Dupes'] = np.where(
373
- np.round(portfolio['dupes_calc'], 0) <= 0,
 
 
 
 
 
374
  0,
375
- np.round((portfolio['dupes_calc'] * (1 + portfolio['own_rank_percentile'])), 0) - 1
376
  )
377
 
378
  elif site_var == 'Draftkings':
@@ -383,7 +209,7 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
383
  else:
384
  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']
385
  own_columns = ['CPT_Own', 'FLEX1_Own', 'FLEX2_Own', 'FLEX3_Own', 'FLEX4_Own', 'FLEX5_Own']
386
- calc_columns = ['own_product', 'own_average', 'own_sum', 'avg_own_rank', 'own_rank_percentile', 'dupes_calc', 'low_own_count', 'Ref_Proj', 'Max_Proj', 'Min_Proj', 'Avg_Ref', 'own_ratio', 'top_x_presence']
387
  # Get the original player columns (first 6 columns excluding salary, median, Own)
388
  player_columns = [col for col in portfolio.columns[:6] if col not in ['salary', 'median', 'Own']]
389
  if sport_var == 'GOLF':
@@ -435,30 +261,31 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
435
  portfolio['FLEX4_Own'] = portfolio.iloc[:,4].map(maps_dict['own_map']).astype('float32') / 100
436
  portfolio['FLEX5_Own'] = portfolio.iloc[:,5].map(maps_dict['own_map']).astype('float32') / 100
437
 
438
- portfolio['own_product'] = (portfolio[own_columns].product(axis=1))
439
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
440
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
441
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
442
- portfolio['own_rank_percentile'] = portfolio['Own'].rank(pct=True)
443
- portfolio['top_x_presence'] = portfolio[player_columns].apply(
444
- lambda row: sum(1 for player in row if player in top_x_ownership_keys), axis=1
445
- )
446
 
447
  # Calculate dupes formula
448
- portfolio['dupes_calc'] = (portfolio['avg_own_rank'] / 1000) * (Contest_Size / 100) + ((portfolio['salary'] - (50000 - portfolio['Own'])) / 100) - ((49700 - portfolio['salary']) / 100)
449
- portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (100 + (Contest_Size / 1000))) * portfolio['top_x_presence']
450
 
451
  # Round and handle negative values
452
  portfolio['Dupes'] = np.where(
453
- np.round(portfolio['dupes_calc'], 0) <= 0,
 
 
 
 
 
454
  0,
455
- np.round((portfolio['dupes_calc'] * (1 + portfolio['own_rank_percentile'])), 0) - 1
456
  )
457
  elif type_var == 'Classic':
458
  if sport_var == 'CS2':
459
  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']
460
  own_columns = ['CPT_Own', 'FLEX1_Own', 'FLEX2_Own', 'FLEX3_Own', 'FLEX4_Own', 'FLEX5_Own']
461
- calc_columns = ['own_product', 'own_average', 'own_sum', 'avg_own_rank', 'own_rank_percentile', 'dupes_calc', 'low_own_count', 'Ref_Proj', 'Max_Proj', 'Min_Proj', 'Avg_Ref', 'own_ratio', 'top_x_presence']
462
  # Get the original player columns (first 6 columns excluding salary, median, Own)
463
  player_columns = [col for col in portfolio.columns[:6] if col not in ['salary', 'median', 'Own']]
464
 
@@ -486,29 +313,30 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
486
  portfolio['FLEX4_Own'] = portfolio.iloc[:,4].map(maps_dict['own_map']).astype('float32') / 100
487
  portfolio['FLEX5_Own'] = portfolio.iloc[:,5].map(maps_dict['own_map']).astype('float32') / 100
488
 
489
- portfolio['own_product'] = (portfolio[own_columns].product(axis=1))
490
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
491
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
492
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
493
- portfolio['own_rank_percentile'] = portfolio['Own'].rank(pct=True)
494
- portfolio['top_x_presence'] = portfolio[player_columns].apply(
495
- lambda row: sum(1 for player in row if player in top_x_ownership_keys), axis=1
496
- )
497
 
498
  # Calculate dupes formula
499
- portfolio['dupes_calc'] = (portfolio['avg_own_rank'] / 1000) * (Contest_Size / 100) + ((portfolio['salary'] - (50000 - portfolio['Own'])) / 100) - ((49700 - portfolio['salary']) / 100)
500
- portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (100 + (Contest_Size / 1000))) * portfolio['top_x_presence']
501
 
502
  # Round and handle negative values
503
  portfolio['Dupes'] = np.where(
504
- np.round(portfolio['dupes_calc'], 0) <= 0,
 
 
 
 
 
505
  0,
506
- np.round((portfolio['dupes_calc'] * (1 + portfolio['own_rank_percentile'])), 0) - 1
507
  )
508
  if sport_var == 'LOL':
509
  dup_count_columns = ['CPT_Own_percent_rank', 'TOP_Own_percent_rank', 'JNG_Own_percent_rank', 'MID_Own_percent_rank', 'ADC_Own_percent_rank', 'SUP_Own_percent_rank', 'Team_Own_percent_rank']
510
  own_columns = ['CPT_Own', 'TOP_Own', 'JNG_Own', 'MID_Own', 'ADC_Own', 'SUP_Own', 'Team_Own']
511
- calc_columns = ['own_product', 'own_average', 'own_sum', 'avg_own_rank', 'own_rank_percentile', 'dupes_calc', 'low_own_count', 'Ref_Proj', 'Max_Proj', 'Min_Proj', 'Avg_Ref', 'own_ratio', 'top_x_presence']
512
  # Get the original player columns (first 6 columns excluding salary, median, Own)
513
  player_columns = [col for col in portfolio.columns[:7] if col not in ['salary', 'median', 'Own']]
514
 
@@ -539,30 +367,31 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
539
  portfolio['SUP_Own'] = portfolio.iloc[:,5].map(maps_dict['own_map']).astype('float32') / 100
540
  portfolio['Team_Own'] = portfolio.iloc[:,6].map(maps_dict['own_map']).astype('float32') / 100
541
 
542
- portfolio['own_product'] = (portfolio[own_columns].product(axis=1))
543
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
544
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
545
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
546
- portfolio['own_rank_percentile'] = portfolio['Own'].rank(pct=True)
547
- portfolio['top_x_presence'] = portfolio[player_columns].apply(
548
- lambda row: sum(1 for player in row if player in top_x_ownership_keys), axis=1
549
- )
550
-
551
  # Calculate dupes formula
552
- portfolio['dupes_calc'] = (portfolio['avg_own_rank'] / 1000) * (Contest_Size / 100) + ((portfolio['salary'] - (50000 - portfolio['Own'])) / 100) - ((49700 - portfolio['salary']) / 100)
553
- portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (100 + (Contest_Size / 1000))) * portfolio['top_x_presence']
554
 
555
  # Round and handle negative values
556
  portfolio['Dupes'] = np.where(
557
- np.round(portfolio['dupes_calc'], 0) <= 0,
 
 
 
 
 
558
  0,
559
- np.round((portfolio['dupes_calc'] * (1 + portfolio['own_rank_percentile'])), 0) - 1
560
  )
561
- elif sport_var != 'CS2' and sport_var != 'LOL':
562
  num_players = len([col for col in portfolio.columns if col not in ['salary', 'median', 'Own']])
563
  dup_count_columns = [f'player_{i}_percent_rank' for i in range(1, num_players + 1)]
564
  own_columns = [f'player_{i}_own' for i in range(1, num_players + 1)]
565
- calc_columns = ['own_product', 'own_average', 'own_sum', 'avg_own_rank', 'own_rank_percentile', 'dupes_calc', 'low_own_count', 'Ref_Proj', 'Max_Proj', 'Min_Proj', 'Avg_Ref', 'own_ratio', 'top_x_presence']
566
  # Get the original player columns (first num_players columns excluding salary, median, Own)
567
  player_columns = [col for col in portfolio.columns[:num_players] if col not in ['salary', 'median', 'Own']]
568
 
@@ -570,23 +399,55 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
570
  portfolio[f'player_{i}_percent_rank'] = portfolio.iloc[:,i-1].map(maps_dict['own_percent_rank'])
571
  portfolio[f'player_{i}_own'] = portfolio.iloc[:,i-1].map(maps_dict['own_map']).astype('float32') / 100
572
 
573
- portfolio['own_product'] = (portfolio[own_columns].product(axis=1))
574
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
575
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
576
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
577
- portfolio['own_rank_percentile'] = portfolio['Own'].rank(pct=True)
578
- portfolio['top_x_presence'] = portfolio[player_columns].apply(
579
- lambda row: sum(1 for player in row if player in top_x_ownership_keys), axis=1
 
 
 
 
 
580
  )
 
 
 
 
 
 
 
 
 
 
 
 
581
 
582
- portfolio['dupes_calc'] = (portfolio['avg_own_rank'] / 1000) * (Contest_Size / 100) + ((portfolio['salary'] - (50000 - (portfolio['Own'] * 100))) / 100) - ((49700 - portfolio['salary']) / 100)
583
- portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * ((portfolio['Own'] * 100) / (100 + (Contest_Size / 1000))) * portfolio['top_x_presence']
 
 
 
 
 
 
 
 
 
584
  # Round and handle negative values
585
  portfolio['Dupes'] = np.where(
586
- np.round(portfolio['dupes_calc'], 0) <= 0,
 
 
 
 
 
587
  0,
588
- np.round((portfolio['dupes_calc'] * (1 + portfolio['own_rank_percentile'])), 0) - 1
589
  )
 
590
 
591
  portfolio['Dupes'] = np.round(portfolio['Dupes'], 0)
592
  portfolio['own_ratio'] = np.where(
@@ -594,7 +455,7 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
594
  portfolio['own_sum'] / portfolio['own_average'],
595
  (portfolio['own_sum'] - max_ownership) / portfolio['own_average']
596
  )
597
- percentile_cut_scalar = portfolio['median'].max()
598
  if type_var == 'Classic':
599
  if sport_var == 'CS2':
600
  own_ratio_nerf = 2
@@ -633,8 +494,6 @@ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, streng
633
 
634
  # Calculate similarity score based on actual player selection
635
  portfolio['Diversity'] = calculate_player_similarity_score_vectorized(portfolio, player_columns)
636
-
637
- st.table(portfolio.sort_values(by='Dupes', ascending=False).head(10))
638
 
639
  portfolio = portfolio.drop(columns=dup_count_columns)
640
  portfolio = portfolio.drop(columns=own_columns)
 
4
  import time
5
  import math
6
  from difflib import SequenceMatcher
 
7
 
8
  def calculate_weighted_ownership_vectorized(ownership_array):
9
  """
 
105
 
106
  return similarity_scores
107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  # Keep the original function for backward compatibility
109
+ def predict_dupes(portfolio, maps_dict, site_var, type_var, Contest_Size, strength_var, sport_var, max_salary):
 
110
  if strength_var == 'Weak':
111
  dupes_multiplier = .75
112
  percentile_multiplier = .90
 
117
  dupes_multiplier = 1.25
118
  percentile_multiplier = 1.10
119
  max_ownership = max(maps_dict['own_map'].values()) / 100
 
120
  average_ownership = np.mean(list(maps_dict['own_map'].values())) / 100
121
  if site_var == 'Fanduel':
122
  if type_var == 'Showdown':
123
  dup_count_columns = ['CPT_Own_percent_rank', 'FLEX1_Own_percent_rank', 'FLEX2_Own_percent_rank', 'FLEX3_Own_percent_rank', 'FLEX4_Own_percent_rank']
124
  own_columns = ['CPT_Own', 'FLEX1_Own', 'FLEX2_Own', 'FLEX3_Own', 'FLEX4_Own']
125
+ 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']
126
  # Get the original player columns (first 5 columns excluding salary, median, Own)
127
  player_columns = [col for col in portfolio.columns[:5] if col not in ['salary', 'median', 'Own']]
128
 
129
  flex_ownerships = pd.concat([
130
+ portfolio.iloc[:,1].map(maps_dict['own_map']),
131
+ portfolio.iloc[:,2].map(maps_dict['own_map']),
132
+ portfolio.iloc[:,3].map(maps_dict['own_map']),
133
+ portfolio.iloc[:,4].map(maps_dict['own_map']),
134
+ portfolio.iloc[:,5].map(maps_dict['own_map'])
135
+ ])
136
  flex_rank = flex_ownerships.rank(pct=True)
137
 
138
  # Assign ranks back to individual columns using the same rank scale
 
150
  portfolio['FLEX4_Own'] = portfolio.iloc[:,4].map(maps_dict['own_map']).astype('float32') / 100
151
  portfolio['FLEX5_Own'] = portfolio.iloc[:,5].map(maps_dict['own_map']).astype('float32') / 100
152
 
153
+ portfolio['own_product'] = (portfolio[own_columns].product(axis=1)) * max(Contest_Size / 10000, 1)
154
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
155
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
156
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
 
 
 
 
157
 
158
  # Calculate dupes formula
159
+ portfolio['dupes_calc'] = (portfolio['own_product'] * portfolio['avg_own_rank']) * (portfolio['Own'] / 100) * Contest_Size + ((portfolio['salary'] - (max_salary - portfolio['Own'])) / 100) - ((max_salary - portfolio['salary']) / 100)
160
+ portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (90 + (Contest_Size / 1000)))
161
 
162
  # Round and handle negative values
163
  portfolio['Dupes'] = np.where(
164
+ portfolio['salary'] == max_salary,
165
+ portfolio['dupes_calc'] + (portfolio['dupes_calc'] * .10),
166
+ portfolio['dupes_calc']
167
+ )
168
+ portfolio['Dupes'] = np.where(
169
+ np.round(portfolio['Dupes'], 0) <= 0,
170
  0,
171
+ np.round(portfolio['Dupes'], 0) - 1
172
  )
 
173
  elif type_var == 'Classic':
174
  num_players = len([col for col in portfolio.columns if col not in ['salary', 'median', 'Own']])
175
  dup_count_columns = [f'player_{i}_percent_rank' for i in range(1, num_players + 1)]
176
  own_columns = [f'player_{i}_own' for i in range(1, num_players + 1)]
177
+ 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']
178
  # Get the original player columns (first num_players columns excluding salary, median, Own)
179
  player_columns = [col for col in portfolio.columns[:num_players] if col not in ['salary', 'median', 'Own']]
180
 
 
182
  portfolio[f'player_{i}_percent_rank'] = portfolio.iloc[:,i-1].map(maps_dict['own_percent_rank'])
183
  portfolio[f'player_{i}_own'] = portfolio.iloc[:,i-1].map(maps_dict['own_map']).astype('float32') / 100
184
 
185
+ portfolio['own_product'] = (portfolio[own_columns].product(axis=1)) * max(Contest_Size / 10000, 1)
186
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
187
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
188
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
 
 
 
 
189
 
190
+ portfolio['dupes_calc'] = (portfolio['own_product'] * portfolio['avg_own_rank']) * Contest_Size + ((portfolio['salary'] - (max_salary - portfolio['Own'])) / 100) - ((max_salary - portfolio['salary']) / 100)
191
+ portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (90 + (Contest_Size / 1000)))
192
  # Round and handle negative values
193
  portfolio['Dupes'] = np.where(
194
+ portfolio['salary'] == max_salary,
195
+ portfolio['dupes_calc'] + (portfolio['dupes_calc'] * .10),
196
+ portfolio['dupes_calc']
197
+ )
198
+ portfolio['Dupes'] = np.where(
199
+ np.round(portfolio['Dupes'], 0) <= 0,
200
  0,
201
+ np.round(portfolio['Dupes'], 0) - 1
202
  )
203
 
204
  elif site_var == 'Draftkings':
 
209
  else:
210
  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']
211
  own_columns = ['CPT_Own', 'FLEX1_Own', 'FLEX2_Own', 'FLEX3_Own', 'FLEX4_Own', 'FLEX5_Own']
212
+ 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']
213
  # Get the original player columns (first 6 columns excluding salary, median, Own)
214
  player_columns = [col for col in portfolio.columns[:6] if col not in ['salary', 'median', 'Own']]
215
  if sport_var == 'GOLF':
 
261
  portfolio['FLEX4_Own'] = portfolio.iloc[:,4].map(maps_dict['own_map']).astype('float32') / 100
262
  portfolio['FLEX5_Own'] = portfolio.iloc[:,5].map(maps_dict['own_map']).astype('float32') / 100
263
 
264
+ portfolio['own_product'] = (portfolio[own_columns].product(axis=1)) * max(Contest_Size / 10000, 1)
265
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
266
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
267
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
 
 
 
 
268
 
269
  # Calculate dupes formula
270
+ portfolio['dupes_calc'] = (portfolio['own_product'] * portfolio['avg_own_rank']) * Contest_Size + ((portfolio['salary'] - (max_salary - portfolio['Own'])) / 100) - ((max_salary - portfolio['salary']) / 100)
271
+ portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (90 + (Contest_Size / 1000)))
272
 
273
  # Round and handle negative values
274
  portfolio['Dupes'] = np.where(
275
+ portfolio['salary'] == max_salary,
276
+ portfolio['dupes_calc'] + (portfolio['dupes_calc'] * .10),
277
+ portfolio['dupes_calc']
278
+ )
279
+ portfolio['Dupes'] = np.where(
280
+ np.round(portfolio['Dupes'], 0) <= 0,
281
  0,
282
+ np.round(portfolio['Dupes'], 0) - 1
283
  )
284
  elif type_var == 'Classic':
285
  if sport_var == 'CS2':
286
  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']
287
  own_columns = ['CPT_Own', 'FLEX1_Own', 'FLEX2_Own', 'FLEX3_Own', 'FLEX4_Own', 'FLEX5_Own']
288
+ 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']
289
  # Get the original player columns (first 6 columns excluding salary, median, Own)
290
  player_columns = [col for col in portfolio.columns[:6] if col not in ['salary', 'median', 'Own']]
291
 
 
313
  portfolio['FLEX4_Own'] = portfolio.iloc[:,4].map(maps_dict['own_map']).astype('float32') / 100
314
  portfolio['FLEX5_Own'] = portfolio.iloc[:,5].map(maps_dict['own_map']).astype('float32') / 100
315
 
316
+ portfolio['own_product'] = (portfolio[own_columns].product(axis=1)) * max(Contest_Size / 10000, 1)
317
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
318
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
319
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
 
 
 
 
320
 
321
  # Calculate dupes formula
322
+ portfolio['dupes_calc'] = ((portfolio['own_product'] * 10) * portfolio['avg_own_rank']) * Contest_Size + ((portfolio['salary'] - (max_salary - portfolio['Own'])) / 50) - ((max_salary - portfolio['salary']) / 50)
323
+ portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (90 + (Contest_Size / 1000)))
324
 
325
  # Round and handle negative values
326
  portfolio['Dupes'] = np.where(
327
+ portfolio['salary'] == max_salary,
328
+ portfolio['dupes_calc'] + (portfolio['dupes_calc'] * .10),
329
+ portfolio['dupes_calc']
330
+ )
331
+ portfolio['Dupes'] = np.where(
332
+ np.round(portfolio['Dupes'], 0) <= 0,
333
  0,
334
+ np.round(portfolio['Dupes'], 0) - 1
335
  )
336
  if sport_var == 'LOL':
337
  dup_count_columns = ['CPT_Own_percent_rank', 'TOP_Own_percent_rank', 'JNG_Own_percent_rank', 'MID_Own_percent_rank', 'ADC_Own_percent_rank', 'SUP_Own_percent_rank', 'Team_Own_percent_rank']
338
  own_columns = ['CPT_Own', 'TOP_Own', 'JNG_Own', 'MID_Own', 'ADC_Own', 'SUP_Own', 'Team_Own']
339
+ 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']
340
  # Get the original player columns (first 6 columns excluding salary, median, Own)
341
  player_columns = [col for col in portfolio.columns[:7] if col not in ['salary', 'median', 'Own']]
342
 
 
367
  portfolio['SUP_Own'] = portfolio.iloc[:,5].map(maps_dict['own_map']).astype('float32') / 100
368
  portfolio['Team_Own'] = portfolio.iloc[:,6].map(maps_dict['own_map']).astype('float32') / 100
369
 
370
+ portfolio['own_product'] = (portfolio[own_columns].product(axis=1)) * max(Contest_Size / 10000, 1)
371
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
372
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
373
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
374
+
 
 
 
 
375
  # Calculate dupes formula
376
+ portfolio['dupes_calc'] = ((portfolio['own_product'] * 10) * portfolio['avg_own_rank']) * Contest_Size + ((portfolio['salary'] - (max_salary - portfolio['Own'])) / 50) - ((max_salary - portfolio['salary']) / 50)
377
+ portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (90 + (Contest_Size / 1000)))
378
 
379
  # Round and handle negative values
380
  portfolio['Dupes'] = np.where(
381
+ portfolio['salary'] == max_salary,
382
+ portfolio['dupes_calc'] + (portfolio['dupes_calc'] * .10),
383
+ portfolio['dupes_calc']
384
+ )
385
+ portfolio['Dupes'] = np.where(
386
+ np.round(portfolio['Dupes'], 0) <= 0,
387
  0,
388
+ np.round(portfolio['Dupes'], 0) - 1
389
  )
390
+ elif sport_var == 'GOLF':
391
  num_players = len([col for col in portfolio.columns if col not in ['salary', 'median', 'Own']])
392
  dup_count_columns = [f'player_{i}_percent_rank' for i in range(1, num_players + 1)]
393
  own_columns = [f'player_{i}_own' for i in range(1, num_players + 1)]
394
+ 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']
395
  # Get the original player columns (first num_players columns excluding salary, median, Own)
396
  player_columns = [col for col in portfolio.columns[:num_players] if col not in ['salary', 'median', 'Own']]
397
 
 
399
  portfolio[f'player_{i}_percent_rank'] = portfolio.iloc[:,i-1].map(maps_dict['own_percent_rank'])
400
  portfolio[f'player_{i}_own'] = portfolio.iloc[:,i-1].map(maps_dict['own_map']).astype('float32') / 100
401
 
402
+ portfolio['own_product'] = (portfolio[own_columns].product(axis=1)) * max(Contest_Size / 10000, 1)
403
  portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
404
  portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
405
  portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
406
+
407
+ portfolio['dupes_calc'] = (portfolio['own_product'] * portfolio['avg_own_rank']) * Contest_Size + ((portfolio['salary'] - (max_salary - portfolio['Own'])) / 100) - ((max_salary - portfolio['salary']) / 100)
408
+ portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (90 + (Contest_Size / 1000)))
409
+ # Round and handle negative values
410
+ portfolio['Dupes'] = np.where(
411
+ portfolio['salary'] == max_salary,
412
+ portfolio['dupes_calc'] + (portfolio['dupes_calc'] * .10),
413
+ portfolio['dupes_calc']
414
  )
415
+ portfolio['Dupes'] = np.where(
416
+ np.round(portfolio['Dupes'], 0) <= 0,
417
+ 0,
418
+ np.round(portfolio['Dupes'], 0) - 1
419
+ )
420
+ else:
421
+ num_players = len([col for col in portfolio.columns if col not in ['salary', 'median', 'Own']])
422
+ dup_count_columns = [f'player_{i}_percent_rank' for i in range(1, num_players + 1)]
423
+ own_columns = [f'player_{i}_own' for i in range(1, num_players + 1)]
424
+ 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']
425
+ # Get the original player columns (first num_players columns excluding salary, median, Own)
426
+ player_columns = [col for col in portfolio.columns[:num_players] if col not in ['salary', 'median', 'Own']]
427
 
428
+ for i in range(1, num_players + 1):
429
+ portfolio[f'player_{i}_percent_rank'] = portfolio.iloc[:,i-1].map(maps_dict['own_percent_rank'])
430
+ portfolio[f'player_{i}_own'] = portfolio.iloc[:,i-1].map(maps_dict['own_map']).astype('float32') / 100
431
+
432
+ portfolio['own_product'] = (portfolio[own_columns].product(axis=1)) * max(Contest_Size / 10000, 1)
433
+ portfolio['own_average'] = (portfolio['Own'].max() * .33) / 100
434
+ portfolio['own_sum'] = portfolio[own_columns].sum(axis=1)
435
+ portfolio['avg_own_rank'] = portfolio[dup_count_columns].mean(axis=1)
436
+
437
+ portfolio['dupes_calc'] = (portfolio['own_product'] * portfolio['avg_own_rank']) * Contest_Size + ((portfolio['salary'] - (max_salary - portfolio['Own'])) / 100) - ((max_salary - portfolio['salary']) / 100)
438
+ portfolio['dupes_calc'] = portfolio['dupes_calc'] * dupes_multiplier * (portfolio['Own'] / (90 + (Contest_Size / 1000)))
439
  # Round and handle negative values
440
  portfolio['Dupes'] = np.where(
441
+ portfolio['salary'] == max_salary,
442
+ portfolio['dupes_calc'] + (portfolio['dupes_calc'] * .10),
443
+ portfolio['dupes_calc']
444
+ )
445
+ portfolio['Dupes'] = np.where(
446
+ np.round(portfolio['Dupes'], 0) <= 0,
447
  0,
448
+ np.round(portfolio['Dupes'], 0) - 1
449
  )
450
+
451
 
452
  portfolio['Dupes'] = np.round(portfolio['Dupes'], 0)
453
  portfolio['own_ratio'] = np.where(
 
455
  portfolio['own_sum'] / portfolio['own_average'],
456
  (portfolio['own_sum'] - max_ownership) / portfolio['own_average']
457
  )
458
+ percentile_cut_scalar = portfolio['median'].max() # Get scalar value
459
  if type_var == 'Classic':
460
  if sport_var == 'CS2':
461
  own_ratio_nerf = 2
 
494
 
495
  # Calculate similarity score based on actual player selection
496
  portfolio['Diversity'] = calculate_player_similarity_score_vectorized(portfolio, player_columns)
 
 
497
 
498
  portfolio = portfolio.drop(columns=dup_count_columns)
499
  portfolio = portfolio.drop(columns=own_columns)