James McCool commited on
Commit
1e6dbdc
·
1 Parent(s): fc0f1cd

Add Streamlit app for PGA DFS projections and lineup optimization with MongoDB integration

Browse files
Files changed (3) hide show
  1. app.py +448 -0
  2. app.yaml +10 -0
  3. requirements.txt +9 -0
app.py ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ st.set_page_config(layout="wide")
3
+
4
+ for name in dir():
5
+ if not name.startswith('_'):
6
+ del globals()[name]
7
+
8
+ import numpy as np
9
+ import pandas as pd
10
+ import streamlit as st
11
+ import gc
12
+ import pymongo
13
+
14
+ @st.cache_resource
15
+ def init_conn():
16
+ uri = st.secrets['mongo_uri']
17
+ client = pymongo.MongoClient(uri, retryWrites=True, serverSelectionTimeoutMS=500000)
18
+ db = client["PGA_Database"]
19
+
20
+ return db
21
+
22
+ db = init_conn()
23
+
24
+ dk_player_url = 'https://docs.google.com/spreadsheets/d/1lMLxWdvCnOFBtG9dhM0zv2USuxZbkogI_2jnxFfQVVs/edit#gid=1828092624'
25
+ CSV_URL = 'https://docs.google.com/spreadsheets/d/1lMLxWdvCnOFBtG9dhM0zv2USuxZbkogI_2jnxFfQVVs/edit#gid=1828092624'
26
+
27
+ player_roo_format = {'Top_finish': '{:.2%}','Top_5_finish': '{:.2%}', 'Top_10_finish': '{:.2%}', '100+%': '{:.2%}', '5x%': '{:.2%}', '6x%': '{:.2%}', '7x%': '{:.2%}', '10x%': '{:.2%}', '11x%': '{:.2%}',
28
+ '12x%': '{:.2%}','LevX': '{:.2%}'}
29
+ dk_columns = ['FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'FLEX6', 'salary', 'proj', 'Own']
30
+ fd_columns = ['FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'FLEX6', 'salary', 'proj', 'Own']
31
+
32
+ @st.cache_resource(ttl = 60)
33
+ def init_baselines():
34
+ collection = db["PGA_Range_of_Outcomes"]
35
+ cursor = collection.find()
36
+ player_frame = pd.DataFrame(cursor)
37
+
38
+ timestamp = player_frame['Timestamp'][0]
39
+
40
+ roo_data = player_frame.drop(columns=['_id', 'index', 'Timestamp'])
41
+ roo_data['Salary'] = roo_data['Salary'].astype(int)
42
+
43
+ collection = db["PGA_SD_ROO"]
44
+ cursor = collection.find()
45
+ player_frame = pd.DataFrame(cursor)
46
+
47
+ sd_roo_data = player_frame.drop(columns=['_id', 'index'])
48
+ sd_roo_data['Salary'] = sd_roo_data['Salary'].astype(int)
49
+
50
+ return roo_data, sd_roo_data, timestamp
51
+
52
+ @st.cache_data(ttl = 60)
53
+ def init_DK_lineups(type):
54
+
55
+ if type == 'Regular':
56
+ collection = db['PGA_DK_Seed_Frame_Name_Map']
57
+ elif type == 'Showdown':
58
+ collection = db['PGA_DK_SD_Seed_Frame_Name_Map']
59
+ cursor = collection.find()
60
+ raw_data = pd.DataFrame(list(cursor))
61
+ names_dict = dict(zip(raw_data['key'], raw_data['value']))
62
+
63
+ if type == 'Regular':
64
+ collection = db["PGA_DK_Seed_Frame"]
65
+ elif type == 'Showdown':
66
+ collection = db["PGA_DK_SD_Seed_Frame"]
67
+ cursor = collection.find().limit(10000)
68
+
69
+ raw_display = pd.DataFrame(list(cursor))
70
+ raw_display = raw_display[['FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'FLEX6', 'salary', 'proj', 'Own']]
71
+ dict_columns = ['FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'FLEX6']
72
+ for col in dict_columns:
73
+ raw_display[col] = raw_display[col].map(names_dict)
74
+ DK_seed = raw_display.to_numpy()
75
+
76
+ return DK_seed
77
+
78
+ @st.cache_data(ttl = 60)
79
+ def init_FD_lineups(type):
80
+
81
+ if type == 'Regular':
82
+ collection = db['PGA_FD_Seed_Frame_Name_Map']
83
+ elif type == 'Showdown':
84
+ collection = db['PGA_DK_SD_Seed_Frame_Name_Map']
85
+ cursor = collection.find()
86
+ raw_data = pd.DataFrame(list(cursor))
87
+ names_dict = dict(zip(raw_data['key'], raw_data['value']))
88
+
89
+ if type == 'Regular':
90
+ collection = db["PGA_FD_Seed_Frame"]
91
+ elif type == 'Showdown':
92
+ collection = db["PGA_DK_SD_Seed_Frame"]
93
+ cursor = collection.find().limit(10000)
94
+
95
+ raw_display = pd.DataFrame(list(cursor))
96
+ raw_display = raw_display[['FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'FLEX6', 'salary', 'proj', 'Own']]
97
+ dict_columns = ['FLEX1', 'FLEX2', 'FLEX3', 'FLEX4', 'FLEX5', 'FLEX6']
98
+ for col in dict_columns:
99
+ raw_display[col] = raw_display[col].map(names_dict)
100
+ FD_seed = raw_display.to_numpy()
101
+
102
+ return FD_seed
103
+
104
+ def convert_df_to_csv(df):
105
+ return df.to_csv().encode('utf-8')
106
+
107
+ @st.cache_data
108
+ def convert_df(array):
109
+ array = pd.DataFrame(array, columns=column_names)
110
+ return array.to_csv().encode('utf-8')
111
+
112
+ roo_data, sd_roo_data, timestamp = init_baselines()
113
+ hold_display = roo_data
114
+ lineup_display = []
115
+ check_list = []
116
+ rand_player = 0
117
+ boost_player = 0
118
+ salaryCut = 0
119
+
120
+ tab1, tab2 = st.tabs(["Player Overall Projections", "Optimals and Exposures"])
121
+
122
+ with tab1:
123
+ if st.button("Reset Data", key='reset1'):
124
+ # Clear values from *all* all in-memory and on-disk data caches:
125
+ # i.e. clear values from both square and cube
126
+ st.cache_data.clear()
127
+ roo_data, sd_roo_data, timestamp = init_baselines()
128
+ dk_lineups = init_DK_lineups('Regular')
129
+ fd_lineups = init_FD_lineups('Regular')
130
+ hold_display = roo_data
131
+ for key in st.session_state.keys():
132
+ del st.session_state[key]
133
+
134
+ st.write(timestamp)
135
+ info_container = st.empty()
136
+ options_container = st.empty()
137
+ hold_container = st.empty()
138
+
139
+ with options_container:
140
+ col1, col2, col3 = st.columns(3)
141
+ with col1:
142
+ view_var = st.radio("Select a View", ["Simple", "Advanced"])
143
+ with col2:
144
+ site_var = st.radio("Select a Site", ["Draftkings", "FanDuel"])
145
+ with col3:
146
+ type_var = st.radio("Select a Type", ["Full Slate", "Showdown"])
147
+ with hold_container:
148
+ if type_var == "Full Slate":
149
+ display = hold_display[hold_display['Site'] == site_var]
150
+ display = display.drop_duplicates(subset=['Player'])
151
+ elif type_var == "Showdown":
152
+ display = sd_roo_data
153
+ display = display.drop_duplicates(subset=['Player'])
154
+
155
+ if view_var == "Simple":
156
+ if type_var == "Full Slate":
157
+ display = display[['Player', 'Salary', 'Median', '10x%', 'Own']]
158
+ display = display.set_index('Player')
159
+ elif type_var == "Showdown":
160
+ display = display[['Player', 'Salary', 'Median', '5x%', 'Own']]
161
+ display = display.set_index('Player')
162
+ st.dataframe(display.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(player_roo_format, precision=2), height=750, use_container_width = True)
163
+ elif view_var == "Advanced":
164
+ display = display
165
+ display = display.set_index('Player')
166
+ st.dataframe(display.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(player_roo_format, precision=2), height=750, use_container_width = True)
167
+
168
+ st.download_button(
169
+ label="Export Projections",
170
+ data=convert_df_to_csv(display),
171
+ file_name='PGA_DFS_export.csv',
172
+ mime='text/csv',
173
+ )
174
+
175
+ with tab2:
176
+ col1, col2 = st.columns([1, 7])
177
+ with col1:
178
+ if st.button("Load/Reset Data", key='reset2'):
179
+ st.cache_data.clear()
180
+ roo_data, sd_roo_data, timestamp = init_baselines()
181
+ hold_display = roo_data
182
+ dk_lineups = init_DK_lineups('Regular')
183
+ fd_lineups = init_FD_lineups('Regular')
184
+ t_stamp = f"Last Update: " + str(timestamp) + f" CST"
185
+ for key in st.session_state.keys():
186
+ del st.session_state[key]
187
+
188
+ slate_var1 = st.radio("Which data are you loading?", ('Regular', 'Showdown'))
189
+
190
+ site_var1 = st.radio("What site are you working with?", ('Draftkings', 'Fanduel'))
191
+ if slate_var1 == 'Regular':
192
+ if site_var1 == 'Draftkings':
193
+ dk_lineups = init_DK_lineups('Regular')
194
+ elif site_var1 == 'Fanduel':
195
+ fd_lineups = init_FD_lineups('Regular')
196
+ elif slate_var1 == 'Showdown':
197
+ if site_var1 == 'Draftkings':
198
+ dk_lineups = init_DK_lineups('Showdown')
199
+ elif site_var1 == 'Fanduel':
200
+ fd_lineups = init_FD_lineups('Showdown')
201
+ lineup_num_var = st.number_input("How many lineups do you want to display?", min_value=1, max_value=1000, value=150, step=1)
202
+
203
+ if slate_var1 == 'Regular':
204
+ raw_baselines = roo_data
205
+ elif slate_var1 == 'Showdown':
206
+ raw_baselines = sd_roo_data
207
+
208
+ if site_var1 == 'Draftkings':
209
+ if slate_var1 == 'Regular':
210
+ ROO_slice = raw_baselines[raw_baselines['Site'] == 'Draftkings']
211
+ player_salaries = dict(zip(ROO_slice['Player'], ROO_slice['Salary']))
212
+ elif slate_var1 == 'Showdown':
213
+ player_salaries = dict(zip(raw_baselines['Player'], raw_baselines['Salary']))
214
+ # Get the minimum and maximum ownership values from dk_lineups
215
+ min_own = np.min(dk_lineups[:,8])
216
+ max_own = np.max(dk_lineups[:,8])
217
+ column_names = dk_columns
218
+
219
+ player_var1 = st.radio("Do you want a frame with specific Players?", ('Full Slate', 'Specific Players'), key='player_var1')
220
+ if player_var1 == 'Specific Players':
221
+ player_var2 = st.multiselect('Which players do you want?', options = raw_baselines['Player'].unique())
222
+ elif player_var1 == 'Full Slate':
223
+ player_var2 = raw_baselines.Player.values.tolist()
224
+
225
+ elif site_var1 == 'Fanduel':
226
+ raw_baselines = hold_display
227
+ if slate_var1 == 'Regular':
228
+ ROO_slice = raw_baselines[raw_baselines['Site'] == 'Fanduel']
229
+ player_salaries = dict(zip(ROO_slice['Player'], ROO_slice['Salary']))
230
+ elif slate_var1 == 'Showdown':
231
+ player_salaries = dict(zip(raw_baselines['Player'], raw_baselines['Salary']))
232
+ min_own = np.min(fd_lineups[:,8])
233
+ max_own = np.max(fd_lineups[:,8])
234
+ column_names = fd_columns
235
+
236
+ player_var1 = st.radio("Do you want a frame with specific Players?", ('Full Slate', 'Specific Players'), key='player_var1')
237
+ if player_var1 == 'Specific Players':
238
+ player_var2 = st.multiselect('Which players do you want?', options = raw_baselines['Player'].unique())
239
+ elif player_var1 == 'Full Slate':
240
+ player_var2 = raw_baselines.Player.values.tolist()
241
+
242
+ if st.button("Prepare data export", key='data_export'):
243
+ data_export = st.session_state.working_seed.copy()
244
+ # if site_var1 == 'Draftkings':
245
+ # for col_idx in range(6):
246
+ # data_export[:, col_idx] = np.array([id_dict.get(player, player) for player in data_export[:, col_idx]])
247
+ # elif site_var1 == 'Fanduel':
248
+ # for col_idx in range(6):
249
+ # data_export[:, col_idx] = np.array([id_dict.get(player, player) for player in data_export[:, col_idx]])
250
+ st.download_button(
251
+ label="Export optimals set",
252
+ data=convert_df(data_export),
253
+ file_name='NBA_optimals_export.csv',
254
+ mime='text/csv',
255
+ )
256
+ with col2:
257
+
258
+ if site_var1 == 'Draftkings':
259
+ if 'working_seed' in st.session_state:
260
+ st.session_state.working_seed = st.session_state.working_seed
261
+ if player_var1 == 'Specific Players':
262
+ st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)]
263
+ elif player_var1 == 'Full Slate':
264
+ st.session_state.working_seed = dk_lineups.copy()
265
+ st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names)
266
+ elif 'working_seed' not in st.session_state:
267
+ st.session_state.working_seed = dk_lineups.copy()
268
+ st.session_state.working_seed = st.session_state.working_seed
269
+ if player_var1 == 'Specific Players':
270
+ st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)]
271
+ elif player_var1 == 'Full Slate':
272
+ st.session_state.working_seed = dk_lineups.copy()
273
+ st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names)
274
+
275
+ elif site_var1 == 'Fanduel':
276
+ if 'working_seed' in st.session_state:
277
+ st.session_state.working_seed = st.session_state.working_seed
278
+ if player_var1 == 'Specific Players':
279
+ st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)]
280
+ elif player_var1 == 'Full Slate':
281
+ st.session_state.working_seed = fd_lineups.copy()
282
+ st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names)
283
+ elif 'working_seed' not in st.session_state:
284
+ st.session_state.working_seed = fd_lineups.copy()
285
+ st.session_state.working_seed = st.session_state.working_seed
286
+ if player_var1 == 'Specific Players':
287
+ st.session_state.working_seed = st.session_state.working_seed[np.equal.outer(st.session_state.working_seed, player_var2).any(axis=1).all(axis=1)]
288
+ elif player_var1 == 'Full Slate':
289
+ st.session_state.working_seed = fd_lineups.copy()
290
+ st.session_state.data_export_display = pd.DataFrame(st.session_state.working_seed[0:lineup_num_var], columns=column_names)
291
+
292
+ export_file = st.session_state.data_export_display.copy()
293
+ # if site_var1 == 'Draftkings':
294
+ # for col_idx in range(6):
295
+ # export_file.iloc[:, col_idx] = export_file.iloc[:, col_idx].map(id_dict)
296
+ # elif site_var1 == 'Fanduel':
297
+ # for col_idx in range(6):
298
+ # export_file.iloc[:, col_idx] = export_file.iloc[:, col_idx].map(id_dict)
299
+
300
+ with st.container():
301
+ if st.button("Reset Optimals", key='reset3'):
302
+ for key in st.session_state.keys():
303
+ del st.session_state[key]
304
+ if site_var1 == 'Draftkings':
305
+ st.session_state.working_seed = dk_lineups.copy()
306
+ elif site_var1 == 'Fanduel':
307
+ st.session_state.working_seed = fd_lineups.copy()
308
+ if 'data_export_display' in st.session_state:
309
+ st.dataframe(st.session_state.data_export_display.style.background_gradient(axis=0).background_gradient(cmap='RdYlGn').format(precision=2), height=500, use_container_width = True)
310
+ st.download_button(
311
+ label="Export display optimals",
312
+ data=convert_df(export_file),
313
+ file_name='NBA_display_optimals.csv',
314
+ mime='text/csv',
315
+ )
316
+
317
+ with st.container():
318
+ if 'working_seed' in st.session_state:
319
+ # Create a new dataframe with summary statistics
320
+ if site_var1 == 'Draftkings':
321
+ summary_df = pd.DataFrame({
322
+ 'Metric': ['Min', 'Average', 'Max', 'STDdev'],
323
+ 'Salary': [
324
+ np.min(st.session_state.working_seed[:,6]),
325
+ np.mean(st.session_state.working_seed[:,6]),
326
+ np.max(st.session_state.working_seed[:,6]),
327
+ np.std(st.session_state.working_seed[:,6])
328
+ ],
329
+ 'Proj': [
330
+ np.min(st.session_state.working_seed[:,7]),
331
+ np.mean(st.session_state.working_seed[:,7]),
332
+ np.max(st.session_state.working_seed[:,7]),
333
+ np.std(st.session_state.working_seed[:,7])
334
+ ],
335
+ 'Own': [
336
+ np.min(st.session_state.working_seed[:,8]),
337
+ np.mean(st.session_state.working_seed[:,8]),
338
+ np.max(st.session_state.working_seed[:,8]),
339
+ np.std(st.session_state.working_seed[:,8])
340
+ ]
341
+ })
342
+ elif site_var1 == 'Fanduel':
343
+ summary_df = pd.DataFrame({
344
+ 'Metric': ['Min', 'Average', 'Max', 'STDdev'],
345
+ 'Salary': [
346
+ np.min(st.session_state.working_seed[:,6]),
347
+ np.mean(st.session_state.working_seed[:,6]),
348
+ np.max(st.session_state.working_seed[:,6]),
349
+ np.std(st.session_state.working_seed[:,6])
350
+ ],
351
+ 'Proj': [
352
+ np.min(st.session_state.working_seed[:,7]),
353
+ np.mean(st.session_state.working_seed[:,7]),
354
+ np.max(st.session_state.working_seed[:,7]),
355
+ np.std(st.session_state.working_seed[:,7])
356
+ ],
357
+ 'Own': [
358
+ np.min(st.session_state.working_seed[:,8]),
359
+ np.mean(st.session_state.working_seed[:,8]),
360
+ np.max(st.session_state.working_seed[:,8]),
361
+ np.std(st.session_state.working_seed[:,8])
362
+ ]
363
+ })
364
+
365
+ # Set the index of the summary dataframe as the "Metric" column
366
+ summary_df = summary_df.set_index('Metric')
367
+
368
+ # Display the summary dataframe
369
+ st.subheader("Optimal Statistics")
370
+ st.dataframe(summary_df.style.format({
371
+ 'Salary': '{:.2f}',
372
+ 'Proj': '{:.2f}',
373
+ 'Own': '{:.2f}'
374
+ }).background_gradient(cmap='RdYlGn', axis=0, subset=['Salary', 'Proj', 'Own']), use_container_width=True)
375
+
376
+ with st.container():
377
+ tab1, tab2 = st.tabs(["Display Frequency", "Seed Frame Frequency"])
378
+ with tab1:
379
+ if 'data_export_display' in st.session_state:
380
+ if site_var1 == 'Draftkings':
381
+ player_columns = st.session_state.data_export_display.iloc[:, :6]
382
+ elif site_var1 == 'Fanduel':
383
+ player_columns = st.session_state.data_export_display.iloc[:, :6]
384
+
385
+ # Flatten the DataFrame and count unique values
386
+ value_counts = player_columns.values.flatten().tolist()
387
+ value_counts = pd.Series(value_counts).value_counts()
388
+
389
+ percentages = (value_counts / lineup_num_var * 100).round(2)
390
+
391
+ # Create a DataFrame with the results
392
+ summary_df = pd.DataFrame({
393
+ 'Player': value_counts.index,
394
+ 'Frequency': value_counts.values,
395
+ 'Percentage': percentages.values
396
+ })
397
+
398
+ # Sort by frequency in descending order
399
+ summary_df['Salary'] = summary_df['Player'].map(player_salaries)
400
+ summary_df = summary_df[['Player', 'Salary', 'Frequency', 'Percentage']]
401
+ summary_df = summary_df.sort_values('Frequency', ascending=False)
402
+ summary_df = summary_df.set_index('Player')
403
+
404
+ # Display the table
405
+ st.write("Player Frequency Table:")
406
+ st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True)
407
+
408
+ st.download_button(
409
+ label="Export player frequency",
410
+ data=convert_df_to_csv(summary_df),
411
+ file_name='PGA_player_frequency.csv',
412
+ mime='text/csv',
413
+ )
414
+ with tab2:
415
+ if 'working_seed' in st.session_state:
416
+ if site_var1 == 'Draftkings':
417
+ player_columns = st.session_state.working_seed[:, :6]
418
+ elif site_var1 == 'Fanduel':
419
+ player_columns = st.session_state.working_seed[:, :6]
420
+
421
+ # Flatten the DataFrame and count unique values
422
+ value_counts = player_columns.flatten().tolist()
423
+ value_counts = pd.Series(value_counts).value_counts()
424
+
425
+ percentages = (value_counts / len(st.session_state.working_seed) * 100).round(2)
426
+ # Create a DataFrame with the results
427
+ summary_df = pd.DataFrame({
428
+ 'Player': value_counts.index,
429
+ 'Frequency': value_counts.values,
430
+ 'Percentage': percentages.values
431
+ })
432
+
433
+ # Sort by frequency in descending order
434
+ summary_df['Salary'] = summary_df['Player'].map(player_salaries)
435
+ summary_df = summary_df[['Player', 'Salary', 'Frequency', 'Percentage']]
436
+ summary_df = summary_df.sort_values('Frequency', ascending=False)
437
+ summary_df = summary_df.set_index('Player')
438
+
439
+ # Display the table
440
+ st.write("Seed Frame Frequency Table:")
441
+ st.dataframe(summary_df.style.format({'Percentage': '{:.2f}%'}), height=500, use_container_width=True)
442
+
443
+ st.download_button(
444
+ label="Export seed frame frequency",
445
+ data=convert_df_to_csv(summary_df),
446
+ file_name='PGA_seed_frame_frequency.csv',
447
+ mime='text/csv',
448
+ )
app.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ runtime: python
2
+ env: flex
3
+
4
+ runtime_config:
5
+ python_version: 3
6
+
7
+ entrypoint: streamlit run streamlit-app.py --server.port $PORT
8
+
9
+ automatic_scaling:
10
+ max_num_instances: 100
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ gspread
3
+ openpyxl
4
+ matplotlib
5
+ pymongo
6
+ pulp
7
+ docker
8
+ plotly
9
+ scipy