nesticot commited on
Commit
1f444d1
·
verified ·
1 Parent(s): be5e082

Upload 6 files

Browse files
app.py CHANGED
@@ -19,6 +19,7 @@ import seaborn as sns
19
  from functions.pitch_summary_functions import *
20
  from shiny import App, reactive, ui, render
21
  from shiny.ui import h2, tags
 
22
 
23
  colour_palette = ['#FFB000','#648FFF','#785EF0',
24
  '#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']
@@ -52,6 +53,11 @@ split_dict = {'all':'All',
52
  'left':'LHH',
53
  'right':'RHH'}
54
 
 
 
 
 
 
55
  split_dict_hand = {'all':['L','R'],
56
  'left':['L'],
57
  'right':['R']}
@@ -110,33 +116,55 @@ from shiny.ui import h2, tags
110
  app_ui = ui.page_sidebar(
111
  ui.sidebar(
112
  # Row for selecting season and level
 
113
  ui.row(
114
- ui.column(4, ui.input_select('year_input', 'Select Season', year_list, selected=2024)),
115
- ui.column(4, ui.input_select('level_input', 'Select Level', level_dict)),
116
- ui.column(4, ui.input_select('type_input', 'Select Type', type_dict,selected='R'))
117
  ),
118
  # Row for the action button to get player list
119
- ui.row(ui.input_action_button("player_button", "Get Player List", class_="btn-primary")),
 
 
120
  # Row for selecting the player
121
- ui.row(ui.column(12, ui.output_ui('player_select_ui', 'Select Player'))),
 
 
 
 
122
  # Row for selecting the date range
123
- ui.row(ui.column(12, ui.output_ui('date_id', 'Select Date'))),
124
-
125
- # Rows for selecting plots and split options
126
  ui.row(
127
- ui.column(4, ui.input_select('plot_id_1', 'Plot Left', function_dict, multiple=False, selected='velocity_kdes')),
128
- ui.column(4, ui.input_select('plot_id_2', 'Plot Middle', function_dict, multiple=False, selected='tj_stuff_roling')),
129
- ui.column(4, ui.input_select('plot_id_3', 'Plot Right', function_dict, multiple=False, selected='break_plot'))
130
  ),
 
 
 
 
131
  ui.row(
132
- ui.column(6, ui.input_select('split_id', 'Select Split', split_dict, multiple=False)),
133
- ui.column(6, ui.input_numeric('rolling_window', 'Rolling Window (for tjStuff+ Plot)', min=1, value=50))
 
134
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  ui.row(
136
- ui.column(6, ui.input_switch("switch", "Custom Team?", False)),
137
- ui.column(6, ui.input_select('logo_select', 'Select Custom Logo', image_dict_flip, multiple=False))
138
  ),
139
-
140
  # Row for the action button to generate plot
141
  ui.row(ui.input_action_button("generate_plot", "Generate Plot", class_="btn-primary")),
142
  width="400px" # Added this parameter to control sidebar width
@@ -144,38 +172,74 @@ app_ui = ui.page_sidebar(
144
 
145
  # Main content area with tabs (placed directly in page_sidebar)
146
  ui.navset_tab(
147
- ui.nav_panel("Pitching Summary",
148
  ui.output_text("status"),
149
- ui.output_plot('plot', width='2100px', height='2100px')
150
- ),
 
 
 
 
 
 
 
 
 
 
151
  )
152
- )
153
 
154
 
155
  def server(input, output, session):
156
 
157
  @reactive.calc
158
- @reactive.event(input.pitcher_id, input.date_id,input.split_id)
159
- def cached_data():
160
-
161
- year_input = int(input.year_input())
162
- sport_id = int(input.level_input())
163
- player_input = int(input.pitcher_id())
164
- start_date = str(input.date_id()[0])
165
- end_date = str(input.date_id()[1])
166
  # Simulate an expensive data operation
167
  game_list = scrape.get_player_games_list(sport_id = sport_id,
168
  season = year_input,
169
  player_id = player_input,
170
  start_date = start_date,
171
  end_date = end_date,
172
- game_type = [input.type_input()])
173
 
174
  data_list = scrape.get_data(game_list_input = game_list[:])
175
  df = (stuff_apply.stuff_apply(fe.feature_engineering(update.update(scrape.get_data_df(data_list = data_list).filter(
176
  (pl.col("pitcher_id") == player_input)&
177
  (pl.col("is_pitch") == True)&
178
- (pl.col('batter_hand').is_in(split_dict_hand[input.split_id()]))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
  )))).with_columns(
181
  pl.col('pitch_type').count().over('pitch_type').alias('pitch_count')
@@ -183,27 +247,54 @@ def server(input, output, session):
183
  return df
184
 
185
  @render.ui
186
- @reactive.event(input.player_button, input.year_input, input.level_input, input.type_input,ignore_none=False)
187
- def player_select_ui():
 
188
  # Get the list of pitchers for the selected level and season
189
- df_pitcher_info = scrape.get_players(sport_id=int(input.level_input()), season=int(input.year_input()), game_type = [input.type_input()]).filter(
190
  pl.col("position").is_in(['P','TWP'])).sort("name")
191
 
192
  # Create a dictionary of pitcher IDs and names
193
  pitcher_dict = dict(zip(df_pitcher_info['player_id'], df_pitcher_info['name']))
194
 
195
  # Return a select input for choosing a pitcher
196
- return ui.input_select("pitcher_id", "Select Pitcher", pitcher_dict, selectize=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
 
198
  @render.ui
199
- @reactive.event(input.player_button, ignore_none=False)
200
- def date_id():
201
  # Create a date range input for selecting the date range within the selected year
202
- return ui.input_date_range("date_id", "Select Date Range",
203
- start=f"{int(input.year_input())}-01-01",
204
- end=f"{int(input.year_input())}-12-31",
205
- min=f"{int(input.year_input())}-01-01",
206
- max=f"{int(input.year_input())}-12-31")
 
 
207
  @output
208
  @render.text
209
  def status():
@@ -211,27 +302,59 @@ def server(input, output, session):
211
  if input.generate == 0:
212
  return ""
213
  return ""
214
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  @output
216
  @render.plot
217
  @reactive.event(input.generate_plot, ignore_none=False)
218
- def plot():
 
219
  # Show progress/loading notification
220
  with ui.Progress(min=0, max=1) as p:
 
 
221
  p.set(message="Generating plot", detail="This may take a while...")
222
 
223
 
224
  p.set(0.3, "Gathering data...")
225
- year_input = int(input.year_input())
226
- sport_id = int(input.level_input())
227
- player_input = int(input.pitcher_id())
228
- start_date = str(input.date_id()[0])
229
- end_date = str(input.date_id()[1])
230
 
231
  print(year_input, sport_id, player_input, start_date, end_date)
232
 
233
- df = cached_data()
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  df = df.clone()
 
235
 
236
  p.set(0.6, "Creating plot...")
237
 
@@ -243,28 +366,30 @@ def server(input, output, session):
243
  sns.set_theme(style="whitegrid", palette=colour_palette)
244
  print('this is the one plot')
245
 
246
- gs = gridspec.GridSpec(6, 8,
247
- height_ratios=[5,20,12,36,36,7],
248
- width_ratios=[4,18,18,18,18,18,18,4])
 
249
 
 
250
 
251
- gs.update(hspace=0.2, wspace=0.5)
 
 
 
 
252
 
253
- # Define the positions of each subplot in the grid
254
- ax_headshot = fig.add_subplot(gs[1,1:3])
255
- ax_bio = fig.add_subplot(gs[1,3:5])
256
- ax_logo = fig.add_subplot(gs[1,5:7])
257
 
258
- ax_season_table = fig.add_subplot(gs[2,1:7])
 
 
 
 
259
 
260
- ax_plot_1 = fig.add_subplot(gs[3,1:3])
261
- ax_plot_2 = fig.add_subplot(gs[3,3:5])
262
- ax_plot_3 = fig.add_subplot(gs[3,5:7])
263
 
264
- ax_table = fig.add_subplot(gs[4,1:7])
265
 
266
- ax_footer = fig.add_subplot(gs[-1,1:7])
267
- ax_header = fig.add_subplot(gs[0,1:7])
268
  ax_left = fig.add_subplot(gs[:,0])
269
  ax_right = fig.add_subplot(gs[:,-1])
270
 
@@ -277,15 +402,55 @@ def server(input, output, session):
277
  sns.set_theme(style="whitegrid", palette=colour_palette)
278
  fig.set_facecolor('white')
279
 
 
 
 
 
 
 
280
  df_teams = scrape.get_teams()
 
281
 
282
- player_headshot(player_input=player_input, ax=ax_headshot,sport_id=sport_id,season=year_input)
283
- player_bio(pitcher_id=player_input, ax=ax_bio,sport_id=sport_id,year_input=year_input)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
 
285
- if input.switch():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
  # Get the logo URL from the image dictionary using the team abbreviation
288
- logo_url = input.logo_select()
289
 
290
  # Send a GET request to the logo URL
291
  response = requests.get(logo_url)
@@ -294,63 +459,378 @@ def server(input, output, session):
294
  img = Image.open(BytesIO(response.content))
295
 
296
  # Display the image on the axis
297
- ax_logo.set_xlim(0, 1.3)
298
- ax_logo.set_ylim(0, 1)
299
- ax_logo.imshow(img, extent=[0.3, 1.3, 0, 1], origin='upper')
300
 
301
  # Turn off the axis
302
- ax_logo.axis('off')
303
 
304
  else:
305
- plot_logo(pitcher_id=player_input, ax=ax_logo, df_team=df_teams,df_players=scrape.get_players(sport_id,year_input))
306
-
307
- stat_summary_table(df=df,
308
- ax=ax_season_table,
309
- player_input=player_input,
310
- split=input.split_id(),
311
- sport_id=sport_id,
312
- game_type=[input.type_input()])
313
-
314
- # break_plot(df=df_plot,ax=ax2)
315
- for x,y,z in zip([input.plot_id_1(),input.plot_id_2(),input.plot_id_3()],[ax_plot_1,ax_plot_2,ax_plot_3],[1,3,5]):
316
- if x == 'velocity_kdes':
317
- velocity_kdes(df,
318
- ax=y,
319
- gs=gs,
320
- gs_x=[3,4],
321
- gs_y=[z,z+2],
322
- fig=fig)
323
- if x == 'tj_stuff_roling':
324
- tj_stuff_roling(df=df,
325
- window=int(input.rolling_window()),
326
- ax=y)
327
-
328
- if x == 'tj_stuff_roling_game':
329
- tj_stuff_roling_game(df=df,
330
- window=int(input.rolling_window()),
331
- ax=y)
332
-
333
- if x == 'break_plot':
334
- break_plot(df = df,ax=y)
335
-
336
- if x == 'location_plot_lhb':
337
- location_plot(df = df,ax=y,hand='L')
338
-
339
- if x == 'location_plot_rhb':
340
- location_plot(df = df,ax=y,hand='R')
341
 
342
- summary_table(df=df,
343
- ax=ax_table)
344
 
 
 
 
 
 
 
 
345
  plot_footer(ax_footer)
346
 
347
  fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
 
351
 
352
- app = App(app_ui, server)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
 
356
- app = App(app_ui, server)
 
19
  from functions.pitch_summary_functions import *
20
  from shiny import App, reactive, ui, render
21
  from shiny.ui import h2, tags
22
+ from shiny import req
23
 
24
  colour_palette = ['#FFB000','#648FFF','#785EF0',
25
  '#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']
 
53
  'left':'LHH',
54
  'right':'RHH'}
55
 
56
+ split_dict_title = {'all':'',
57
+ 'left':' - vs LHH',
58
+ 'right':' - vs RHH'}
59
+
60
+
61
  split_dict_hand = {'all':['L','R'],
62
  'left':['L'],
63
  'right':['R']}
 
116
  app_ui = ui.page_sidebar(
117
  ui.sidebar(
118
  # Row for selecting season and level
119
+ ui.row(ui.markdown("### Select Pitcher 1")),
120
  ui.row(
121
+ ui.column(4, ui.input_select('year_input_1', 'Season', year_list, selected=2024)),
122
+ ui.column(4, ui.input_select('level_input_1', 'Level', level_dict)),
123
+ ui.column(4, ui.input_select('type_input_1', 'Type', type_dict,selected='R'))
124
  ),
125
  # Row for the action button to get player list
126
+ ui.row(ui.input_action_button("player_button_1", "Get Player List", class_="btn-primary"),
127
+ ),
128
+
129
  # Row for selecting the player
130
+ ui.row(ui.column(8, ui.output_ui('player_select_ui_1', 'Select Player')),
131
+ ui.column(4, ui.input_select('split_id_1', 'Select Split', split_dict, multiple=False))
132
+
133
+
134
+ ),
135
  # Row for selecting the date range
136
+ ui.row(ui.column(12, ui.output_ui('date_id_1', 'Select Date'))),
 
 
137
  ui.row(
138
+ ui.column(6, ui.input_switch("switch_1", "Custom Team?", False)),
139
+ ui.column(6, ui.input_select('logo_select_1', 'Select Custom Logo', image_dict_flip, multiple=False))
 
140
  ),
141
+
142
+
143
+ # Row for selecting season and level
144
+ ui.row(ui.markdown("### Select Pitcher 2")),
145
  ui.row(
146
+ ui.column(4, ui.input_select('year_input_2', 'Season', year_list, selected=2024)),
147
+ ui.column(4, ui.input_select('level_input_2', 'Level', level_dict)),
148
+ ui.column(4, ui.input_select('type_input_2', 'Type', type_dict,selected='R'))
149
  ),
150
+ # Row for the action button to get player list
151
+ ui.row(ui.input_action_button("player_button_2", "Get Player List", class_="btn-primary"),
152
+ ),
153
+
154
+ # Row for selecting the player
155
+ ui.row(ui.column(8, ui.output_ui('player_select_ui_2', 'Select Player')),
156
+ ui.column(4, ui.input_select('split_id_2', 'Select Split', split_dict, multiple=False))
157
+
158
+
159
+ ),
160
+ # Row for selecting the date range
161
+ ui.row(ui.column(12, ui.output_ui('date_id_2', 'Select Date'))),
162
+
163
  ui.row(
164
+ ui.column(6, ui.input_switch("switch_2", "Custom Team?", False)),
165
+ ui.column(6, ui.input_select('logo_select_2', 'Select Custom Logo', image_dict_flip, multiple=False))
166
  ),
167
+
168
  # Row for the action button to generate plot
169
  ui.row(ui.input_action_button("generate_plot", "Generate Plot", class_="btn-primary")),
170
  width="400px" # Added this parameter to control sidebar width
 
172
 
173
  # Main content area with tabs (placed directly in page_sidebar)
174
  ui.navset_tab(
175
+ ui.nav_panel("Full Compare",
176
  ui.output_text("status"),
177
+ ui.output_plot('plot', width='2100px', height='2100px')),
178
+ ui.nav_panel("Break Plots",
179
+ ui.output_text("status_break"),
180
+ ui.output_plot('plot_break', width='2100px', height='1300px')),
181
+ ui.nav_panel("Tables",
182
+ ui.output_text("status_table"),
183
+ ui.output_plot('plot_table', width='2100px', height='1300px')),
184
+ id="tabset_id",
185
+
186
+
187
+
188
+ ),
189
  )
190
+
191
 
192
 
193
  def server(input, output, session):
194
 
195
  @reactive.calc
196
+ @reactive.event(input.pitcher_id_1, input.date_id_1,input.split_id_1)
197
+ def cached_data_1():
198
+
199
+ year_input = int(input.year_input_1())
200
+ sport_id = int(input.level_input_1())
201
+ player_input = int(input.pitcher_id_1())
202
+ start_date = str(input.date_id_1()[0])
203
+ end_date = str(input.date_id_1()[1])
204
  # Simulate an expensive data operation
205
  game_list = scrape.get_player_games_list(sport_id = sport_id,
206
  season = year_input,
207
  player_id = player_input,
208
  start_date = start_date,
209
  end_date = end_date,
210
+ game_type = [input.type_input_1()])
211
 
212
  data_list = scrape.get_data(game_list_input = game_list[:])
213
  df = (stuff_apply.stuff_apply(fe.feature_engineering(update.update(scrape.get_data_df(data_list = data_list).filter(
214
  (pl.col("pitcher_id") == player_input)&
215
  (pl.col("is_pitch") == True)&
216
+ (pl.col('batter_hand').is_in(split_dict_hand[input.split_id_1()]))
217
+
218
+ )))).with_columns(
219
+ pl.col('pitch_type').count().over('pitch_type').alias('pitch_count')
220
+ ))
221
+ return df
222
+
223
+ def cached_data_2():
224
+
225
+ year_input = int(input.year_input_2())
226
+ sport_id = int(input.level_input_2())
227
+ player_input = int(input.pitcher_id_2())
228
+ start_date = str(input.date_id_2()[0])
229
+ end_date = str(input.date_id_2()[1])
230
+ # Simulate an expensive data operation
231
+ game_list = scrape.get_player_games_list(sport_id = sport_id,
232
+ season = year_input,
233
+ player_id = player_input,
234
+ start_date = start_date,
235
+ end_date = end_date,
236
+ game_type = [input.type_input_2()])
237
+
238
+ data_list = scrape.get_data(game_list_input = game_list[:])
239
+ df = (stuff_apply.stuff_apply(fe.feature_engineering(update.update(scrape.get_data_df(data_list = data_list).filter(
240
+ (pl.col("pitcher_id") == player_input)&
241
+ (pl.col("is_pitch") == True)&
242
+ (pl.col('batter_hand').is_in(split_dict_hand[input.split_id_2()]))
243
 
244
  )))).with_columns(
245
  pl.col('pitch_type').count().over('pitch_type').alias('pitch_count')
 
247
  return df
248
 
249
  @render.ui
250
+ @reactive.event(input.player_button_1, input.year_input_1, input.level_input_1, input.type_input_1,ignore_none=False)
251
+ def player_select_ui_1():
252
+
253
  # Get the list of pitchers for the selected level and season
254
+ df_pitcher_info = scrape.get_players(sport_id=int(input.level_input_1()), season=int(input.year_input_1()), game_type = [input.type_input_1()]).filter(
255
  pl.col("position").is_in(['P','TWP'])).sort("name")
256
 
257
  # Create a dictionary of pitcher IDs and names
258
  pitcher_dict = dict(zip(df_pitcher_info['player_id'], df_pitcher_info['name']))
259
 
260
  # Return a select input for choosing a pitcher
261
+ return ui.input_select("pitcher_id_1", "Select Pitcher", pitcher_dict, selectize=True)
262
+
263
+
264
+ @render.ui
265
+ @reactive.event(input.player_button_2, input.year_input_2, input.level_input_2, input.type_input_2,ignore_none=False)
266
+ def player_select_ui_2():
267
+ # Get the list of pitchers for the selected level and season
268
+ df_pitcher_info = scrape.get_players(sport_id=int(input.level_input_2()), season=int(input.year_input_2()), game_type = [input.type_input_2()]).filter(
269
+ pl.col("position").is_in(['P','TWP'])).sort("name")
270
+
271
+ # Create a dictionary of pitcher IDs and names
272
+ pitcher_dict = dict(zip(df_pitcher_info['player_id'], df_pitcher_info['name']))
273
+
274
+ # Return a select input for choosing a pitcher
275
+ return ui.input_select("pitcher_id_2", "Select Pitcher", pitcher_dict, selectize=True)
276
+
277
+ @render.ui
278
+ @reactive.event(input.player_button_1, input.year_input_1, ignore_none=False)
279
+ def date_id_1():
280
+ # Create a date range input for selecting the date range within the selected year
281
+ return ui.input_date_range("date_id_1", "Select Date Range",
282
+ start=f"{int(input.year_input_1())}-01-01",
283
+ end=f"{int(input.year_input_1())}-12-31",
284
+ min=f"{int(input.year_input_1())}-01-01",
285
+ max=f"{int(input.year_input_1())}-12-31")
286
 
287
  @render.ui
288
+ @reactive.event(input.player_button_1, input.year_input_1, ignore_none=False)
289
+ def date_id_2():
290
  # Create a date range input for selecting the date range within the selected year
291
+ return ui.input_date_range("date_id_2", "Select Date Range",
292
+ start=f"{int(input.year_input_2())}-01-01",
293
+ end=f"{int(input.year_input_2())}-12-31",
294
+ min=f"{int(input.year_input_2())}-01-01",
295
+ max=f"{int(input.year_input_2())}-12-31")
296
+
297
+
298
  @output
299
  @render.text
300
  def status():
 
302
  if input.generate == 0:
303
  return ""
304
  return ""
305
+
306
+ @output
307
+ @render.text
308
+ def status_break():
309
+ # Only show status when generating
310
+ if input.generate == 0:
311
+ return ""
312
+ return ""
313
+ @output
314
+ @render.text
315
+ def status_table():
316
+ # Only show status when generating
317
+ if input.generate == 0:
318
+ return ""
319
+ return ""
320
+
321
  @output
322
  @render.plot
323
  @reactive.event(input.generate_plot, ignore_none=False)
324
+ def plot():
325
+ req(input.generate_plot())
326
  # Show progress/loading notification
327
  with ui.Progress(min=0, max=1) as p:
328
+
329
+
330
  p.set(message="Generating plot", detail="This may take a while...")
331
 
332
 
333
  p.set(0.3, "Gathering data...")
334
+ year_input = int(input.year_input_1())
335
+ sport_id = int(input.level_input_1())
336
+ player_input = int(input.pitcher_id_1())
337
+ start_date = str(input.date_id_1()[0])
338
+ end_date = str(input.date_id_1()[1])
339
 
340
  print(year_input, sport_id, player_input, start_date, end_date)
341
 
342
+ year_input_2 = int(input.year_input_2())
343
+ sport_id_2 = int(input.level_input_2())
344
+ player_input_2 = int(input.pitcher_id_2())
345
+ start_date_2 = str(input.date_id_2()[0])
346
+ end_date_2 = str(input.date_id_2()[1])
347
+
348
+ print(year_input_2, sport_id_2, player_input_2, start_date_2, end_date_2)
349
+
350
+
351
+
352
+ df = cached_data_1()
353
+ print(df)
354
+ df_2 = cached_data_2()
355
+ print(df_2)
356
  df = df.clone()
357
+ df_2 = df_2.clone()
358
 
359
  p.set(0.6, "Creating plot...")
360
 
 
366
  sns.set_theme(style="whitegrid", palette=colour_palette)
367
  print('this is the one plot')
368
 
369
+ gs = gridspec.GridSpec(6, 12,
370
+ height_ratios=[1,10,40,25,25,5],
371
+ width_ratios=[3,3,10,24,10,3,3,10,24,10,3,3])
372
+
373
 
374
+ gs.update(hspace=0.15, wspace=0.15)
375
 
376
+ ax_headshot_1 = fig.add_subplot(gs[1,2])
377
+ ax_bio_1 = fig.add_subplot(gs[1,3])
378
+ ax_logo_1 = fig.add_subplot(gs[1,4])
379
+ ax_break_1 = fig.add_subplot(gs[2,1:6])
380
+ ax_table_1 = fig.add_subplot(gs[3,1:11])
381
 
 
 
 
 
382
 
383
+ ax_headshot_2 = fig.add_subplot(gs[1,7])
384
+ ax_bio_2 = fig.add_subplot(gs[1,8])
385
+ ax_logo_2 = fig.add_subplot(gs[1,9])
386
+ ax_break_2 = fig.add_subplot(gs[2,6:11])
387
+ ax_table_2 = fig.add_subplot(gs[4,1:11])
388
 
 
 
 
389
 
 
390
 
391
+ ax_footer = fig.add_subplot(gs[-1,1:11])
392
+ ax_header = fig.add_subplot(gs[0,1:11])
393
  ax_left = fig.add_subplot(gs[:,0])
394
  ax_right = fig.add_subplot(gs[:,-1])
395
 
 
402
  sns.set_theme(style="whitegrid", palette=colour_palette)
403
  fig.set_facecolor('white')
404
 
405
+ # df_teams = scrape.get_teams()
406
+
407
+ player_headshot(player_input=player_input, ax=ax_headshot_1,sport_id=sport_id,season=year_input)
408
+ player_bio(pitcher_id=player_input, ax=ax_bio_1,sport_id=sport_id,year_input=year_input,
409
+ df=df,split=input.split_id_1(),game_type=input.type_input_1())
410
+
411
  df_teams = scrape.get_teams()
412
+ if input.switch_1():
413
 
414
+ # Get the logo URL from the image dictionary using the team abbreviation
415
+ logo_url = input.logo_select_1()
416
+
417
+ # Send a GET request to the logo URL
418
+ response = requests.get(logo_url)
419
+
420
+ # Open the image from the response content
421
+ img = Image.open(BytesIO(response.content))
422
+
423
+ # Display the image on the axis
424
+ ax_logo_1.set_xlim(0, 1)
425
+ ax_logo_1.set_ylim(0, 1)
426
+ ax_logo_1.imshow(img, extent=[0, 1, 0, 1], origin='upper')
427
+
428
+ # Turn off the axis
429
+ ax_logo_1.axis('off')
430
+
431
+ else:
432
+ plot_logo(pitcher_id=player_input, ax=ax_logo_1, df_team=df_teams,df_players=scrape.get_players(sport_id,year_input))
433
 
434
+ break_plot(df = df,ax=ax_break_1)
435
+ summary_table(df=df,
436
+ ax=ax_table_1)
437
+ ax_table_1.text(x=0.5,y=0.83,s=f'{df["pitcher_name"][0]} - {year_input} - {level_dict[str(sport_id)]} - {type_dict[input.type_input_1()]}{split_dict_title[input.split_id_1()]}',
438
+ fontsize=30, ha='center', va='bottom',fontstyle='italic')
439
+ # level_dict
440
+ # split_dict
441
+ # type_dict
442
+
443
+ #####################
444
+
445
+ player_headshot(player_input=player_input_2, ax=ax_headshot_2,sport_id=sport_id_2,season=year_input_2)
446
+ player_bio(pitcher_id=player_input_2, ax=ax_bio_2,sport_id=sport_id_2,year_input=year_input_2,
447
+ df=df_2,split=input.split_id_2(),game_type=input.type_input_2())
448
+
449
+ # df_teams = scrape.get_teams()
450
+ if input.switch_2():
451
 
452
  # Get the logo URL from the image dictionary using the team abbreviation
453
+ logo_url = input.logo_select_2()
454
 
455
  # Send a GET request to the logo URL
456
  response = requests.get(logo_url)
 
459
  img = Image.open(BytesIO(response.content))
460
 
461
  # Display the image on the axis
462
+ ax_logo_2.set_xlim(0, 1)
463
+ ax_logo_2.set_ylim(0, 1)
464
+ ax_logo_2.imshow(img, extent=[0, 1, 0, 1], origin='upper')
465
 
466
  # Turn off the axis
467
+ ax_logo_2.axis('off')
468
 
469
  else:
470
+ plot_logo(pitcher_id=player_input_2, ax=ax_logo_2, df_team=df_teams,df_players=scrape.get_players(sport_id_2,year_input_2))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
 
 
 
472
 
473
+ break_plot(df = df_2,ax=ax_break_2)
474
+ summary_table(df=df_2,
475
+ ax=ax_table_2)
476
+ ax_table_2.text(x=0.5,y=0.83,s=f'{df_2["pitcher_name"][0]} - {year_input_2} - {level_dict[str(sport_id_2)]} - {type_dict[input.type_input_2()]}{split_dict_title[input.split_id_2()]}',
477
+ fontsize=30, ha='center', va='bottom',fontstyle='italic')
478
+
479
+ #########################
480
  plot_footer(ax_footer)
481
 
482
  fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
483
 
484
+ # Draw a line halfway down the figure, starting from the top middle and going to the center
485
+ line = plt.Line2D([0.5, 0.5], [0.55, 0.95], transform=fig.transFigure, color="grey",alpha=0.4, linewidth=2)
486
+ fig.add_artist(line)
487
+
488
+ @output
489
+ @render.plot
490
+ @reactive.event(input.generate_plot, ignore_none=False)
491
+ def plot_break():
492
+ req(input.generate_plot())
493
+ # Show progress/loading notification
494
+ with ui.Progress(min=0, max=1) as p:
495
+
496
+
497
+ p.set(message="Generating plot", detail="This may take a while...")
498
+
499
+
500
+ p.set(0.3, "Gathering data...")
501
+ year_input = int(input.year_input_1())
502
+ sport_id = int(input.level_input_1())
503
+ player_input = int(input.pitcher_id_1())
504
+ start_date = str(input.date_id_1()[0])
505
+ end_date = str(input.date_id_1()[1])
506
+
507
+ print(year_input, sport_id, player_input, start_date, end_date)
508
+
509
+ year_input_2 = int(input.year_input_2())
510
+ sport_id_2 = int(input.level_input_2())
511
+ player_input_2 = int(input.pitcher_id_2())
512
+ start_date_2 = str(input.date_id_2()[0])
513
+ end_date_2 = str(input.date_id_2()[1])
514
+
515
+ print(year_input_2, sport_id_2, player_input_2, start_date_2, end_date_2)
516
+
517
+
518
+
519
+ df = cached_data_1()
520
+ print(df)
521
+ df_2 = cached_data_2()
522
+ print(df_2)
523
+ df = df.clone()
524
+ df_2 = df_2.clone()
525
+
526
+ p.set(0.6, "Creating plot...")
527
+
528
+
529
+ #plt.rcParams["figure.figsize"] = [10,10]
530
+ fig = plt.figure(figsize=(26,26))
531
+ plt.rcParams.update({'figure.autolayout': True})
532
+ fig.set_facecolor('white')
533
+ sns.set_theme(style="whitegrid", palette=colour_palette)
534
+ print('this is the one plot')
535
+
536
+ gs = gridspec.GridSpec(4, 12,
537
+ height_ratios=[1,10,40,10],
538
+ width_ratios=[3,3,10,24,10,3,3,10,24,10,3,3])
539
+
540
+
541
+ gs.update(hspace=0.15, wspace=0.15)
542
+
543
+ ax_headshot_1 = fig.add_subplot(gs[1,2])
544
+ ax_bio_1 = fig.add_subplot(gs[1,3])
545
+ ax_logo_1 = fig.add_subplot(gs[1,4])
546
+ ax_break_1 = fig.add_subplot(gs[2,1:6])
547
+ # ax_table_1 = fig.add_subplot(gs[3,1:11])
548
+
549
+
550
+ ax_headshot_2 = fig.add_subplot(gs[1,7])
551
+ ax_bio_2 = fig.add_subplot(gs[1,8])
552
+ ax_logo_2 = fig.add_subplot(gs[1,9])
553
+ ax_break_2 = fig.add_subplot(gs[2,6:11])
554
+ # ax_table_2 = fig.add_subplot(gs[4,1:11])
555
+
556
+
557
+
558
+ ax_footer = fig.add_subplot(gs[-1,1:11])
559
+ ax_header = fig.add_subplot(gs[0,1:11])
560
+ ax_left = fig.add_subplot(gs[:,0])
561
+ ax_right = fig.add_subplot(gs[:,-1])
562
+
563
+ # Hide axes for footer, header, left, and right
564
+ ax_footer.axis('off')
565
+ ax_header.axis('off')
566
+ ax_left.axis('off')
567
+ ax_right.axis('off')
568
+
569
+ sns.set_theme(style="whitegrid", palette=colour_palette)
570
+ fig.set_facecolor('white')
571
 
572
+ # df_teams = scrape.get_teams()
573
+
574
+ player_headshot(player_input=player_input, ax=ax_headshot_1,sport_id=sport_id,season=year_input)
575
+ player_bio(pitcher_id=player_input, ax=ax_bio_1,sport_id=sport_id,year_input=year_input,
576
+ df=df,split=input.split_id_1(),game_type=input.type_input_1())
577
+
578
+ df_teams = scrape.get_teams()
579
+ if input.switch_1():
580
+
581
+ # Get the logo URL from the image dictionary using the team abbreviation
582
+ logo_url = input.logo_select_1()
583
+
584
+ # Send a GET request to the logo URL
585
+ response = requests.get(logo_url)
586
+
587
+ # Open the image from the response content
588
+ img = Image.open(BytesIO(response.content))
589
+
590
+ # Display the image on the axis
591
+ ax_logo_1.set_xlim(0, 1)
592
+ ax_logo_1.set_ylim(0, 1)
593
+ ax_logo_1.imshow(img, extent=[0, 1, 0, 1], origin='upper')
594
+
595
+ # Turn off the axis
596
+ ax_logo_1.axis('off')
597
+
598
+ else:
599
+ plot_logo(pitcher_id=player_input, ax=ax_logo_1, df_team=df_teams,df_players=scrape.get_players(sport_id,year_input))
600
+
601
+ break_plot(df = df,ax=ax_break_1)
602
+
603
+
604
+ #####################
605
+
606
+ player_headshot(player_input=player_input_2, ax=ax_headshot_2,sport_id=sport_id_2,season=year_input_2)
607
+ player_bio(pitcher_id=player_input_2, ax=ax_bio_2,sport_id=sport_id_2,year_input=year_input_2,
608
+ df=df_2,split=input.split_id_2(),game_type=input.type_input_2())
609
+
610
+ # df_teams = scrape.get_teams()
611
+ if input.switch_2():
612
+
613
+ # Get the logo URL from the image dictionary using the team abbreviation
614
+ logo_url = input.logo_select_2()
615
+
616
+ # Send a GET request to the logo URL
617
+ response = requests.get(logo_url)
618
+
619
+ # Open the image from the response content
620
+ img = Image.open(BytesIO(response.content))
621
+
622
+ # Display the image on the axis
623
+ ax_logo_2.set_xlim(0, 1)
624
+ ax_logo_2.set_ylim(0, 1)
625
+ ax_logo_2.imshow(img, extent=[0, 1, 0, 1], origin='upper')
626
+
627
+ # Turn off the axis
628
+ ax_logo_2.axis('off')
629
+
630
+ else:
631
+ plot_logo(pitcher_id=player_input_2, ax=ax_logo_2, df_team=df_teams,df_players=scrape.get_players(sport_id_2,year_input_2))
632
+
633
+
634
+ break_plot(df = df_2,ax=ax_break_2)
635
+
636
+ #########################
637
+ plot_footer_break(ax_footer)
638
+
639
+ fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
640
+
641
+ # Draw a line halfway down the figure, starting from the top middle and going to the center
642
+ line = plt.Line2D([0.5, 0.5], [0.15, 0.95], transform=fig.transFigure, color="grey",alpha=0.4, linewidth=2)
643
+ fig.add_artist(line)
644
 
645
 
646
+ df_legend = pl.concat([df, df_2])
647
+ # # Create legend for pitch types
648
+ items_in_order = (df_legend.sort("pitch_count", descending=True)['pitch_type'].unique(maintain_order=True).to_numpy())
649
+ colour_pitches = [dict_colour[x] for x in items_in_order]
650
+ label = [dict_pitch[x] for x in items_in_order]
651
+ handles = [plt.scatter([], [], color=color, marker='o', s=100) for color in colour_pitches]
652
+
653
+ if len(label) > 10:
654
+ ax_footer.legend(handles, label, bbox_to_anchor=(0.1, 0.2, 0.8, 0.3), ncol=5,
655
+ fancybox=True, loc='lower center', fontsize=16, framealpha=1.0, markerscale=1.7, prop={'family': 'calibi', 'size': 16})
656
+ if len(label) > 5:
657
+ ax_footer.legend(handles, label, bbox_to_anchor=(0.1, 0.2, 0.8, 0.2), ncol=5,
658
+ fancybox=True, loc='lower center', fontsize=16, framealpha=1.0, markerscale=1.7, prop={'family': 'calibi', 'size': 16})
659
+ else:
660
+ ax_footer.legend(handles, label, bbox_to_anchor=(0.1, 0.2, 0.8, 0.14), ncol=5,
661
+ fancybox=True, loc='lower center', fontsize=20, framealpha=1.0, markerscale=2, prop={'family': 'calibi', 'size': 20})
662
+ ax_footer.axis('off')
663
+
664
+
665
+ @output
666
+ @render.plot
667
+ @reactive.event(input.generate_plot, ignore_none=False)
668
+ def plot_table():
669
+ req(input.generate_plot())
670
+ # Show progress/loading notification
671
+ with ui.Progress(min=0, max=1) as p:
672
+
673
+
674
+ p.set(message="Generating plot", detail="This may take a while...")
675
+
676
+
677
+ p.set(0.3, "Gathering data...")
678
+ year_input = int(input.year_input_1())
679
+ sport_id = int(input.level_input_1())
680
+ player_input = int(input.pitcher_id_1())
681
+ start_date = str(input.date_id_1()[0])
682
+ end_date = str(input.date_id_1()[1])
683
 
684
+ print(year_input, sport_id, player_input, start_date, end_date)
685
+
686
+ year_input_2 = int(input.year_input_2())
687
+ sport_id_2 = int(input.level_input_2())
688
+ player_input_2 = int(input.pitcher_id_2())
689
+ start_date_2 = str(input.date_id_2()[0])
690
+ end_date_2 = str(input.date_id_2()[1])
691
+
692
+ print(year_input_2, sport_id_2, player_input_2, start_date_2, end_date_2)
693
+
694
+
695
+
696
+ df = cached_data_1()
697
+ print(df)
698
+ df_2 = cached_data_2()
699
+ print(df_2)
700
+ df = df.clone()
701
+ df_2 = df_2.clone()
702
+
703
+ p.set(0.6, "Creating plot...")
704
+
705
+
706
+ #plt.rcParams["figure.figsize"] = [10,10]
707
+ fig = plt.figure(figsize=(26,26))
708
+ plt.rcParams.update({'figure.autolayout': True})
709
+ fig.set_facecolor('white')
710
+ sns.set_theme(style="whitegrid", palette=colour_palette)
711
+ print('this is the one plot')
712
+
713
+ gs = gridspec.GridSpec(4, 12,
714
+ height_ratios=[1,25,25,5],
715
+ width_ratios=[3,3,10,24,10,3,3,10,24,10,3,3])
716
+
717
+
718
+ gs.update(hspace=0.15, wspace=0.15)
719
+
720
+ # ax_headshot_1 = fig.add_subplot(gs[1,2])
721
+ # ax_bio_1 = fig.add_subplot(gs[1,3])
722
+ # ax_logo_1 = fig.add_subplot(gs[1,4])
723
+ # ax_break_1 = fig.add_subplot(gs[2,1:6])
724
+ ax_table_1 = fig.add_subplot(gs[1,1:11])
725
+
726
+
727
+ # ax_headshot_2 = fig.add_subplot(gs[1,7])
728
+ # ax_bio_2 = fig.add_subplot(gs[1,8])
729
+ # ax_logo_2 = fig.add_subplot(gs[1,9])
730
+ # ax_break_2 = fig.add_subplot(gs[2,6:11])
731
+ ax_table_2 = fig.add_subplot(gs[2,1:11])
732
+
733
+
734
+
735
+ ax_footer = fig.add_subplot(gs[-1,1:11])
736
+ ax_header = fig.add_subplot(gs[0,1:11])
737
+ ax_left = fig.add_subplot(gs[:,0])
738
+ ax_right = fig.add_subplot(gs[:,-1])
739
+
740
+ # Hide axes for footer, header, left, and right
741
+ ax_footer.axis('off')
742
+ ax_header.axis('off')
743
+ ax_left.axis('off')
744
+ ax_right.axis('off')
745
+
746
+ sns.set_theme(style="whitegrid", palette=colour_palette)
747
+ fig.set_facecolor('white')
748
+
749
+ # df_teams = scrape.get_teams()
750
+
751
+ # player_headshot(player_input=player_input, ax=ax_headshot_1,sport_id=sport_id,season=year_input)
752
+ # player_bio(pitcher_id=player_input, ax=ax_bio_1,sport_id=sport_id,year_input=year_input,
753
+ # df=df,split=input.split_id_1(),game_type=input.type_input_1())
754
+
755
+ # df_teams = scrape.get_teams()
756
+ # if input.switch_1():
757
+
758
+ # # Get the logo URL from the image dictionary using the team abbreviation
759
+ # logo_url = input.logo_select_1()
760
+
761
+ # # Send a GET request to the logo URL
762
+ # response = requests.get(logo_url)
763
+
764
+ # # Open the image from the response content
765
+ # img = Image.open(BytesIO(response.content))
766
+
767
+ # # Display the image on the axis
768
+ # ax_logo_1.set_xlim(0, 1)
769
+ # ax_logo_1.set_ylim(0, 1)
770
+ # ax_logo_1.imshow(img, extent=[0, 1, 0, 1], origin='upper')
771
+
772
+ # # Turn off the axis
773
+ # # ax_logo_1.axis('off')
774
+
775
+ # else:
776
+ # plot_logo(pitcher_id=player_input, ax=ax_logo_1, df_team=df_teams,df_players=scrape.get_players(sport_id,year_input))
777
+
778
+ # break_plot(df = df,ax=ax_break_1)
779
+ summary_table(df=df,
780
+ ax=ax_table_1)
781
+ ax_table_1.text(x=0.5,y=0.83,s=f'{df["pitcher_name"][0]} - {year_input} - {level_dict[str(sport_id)]} - {type_dict[input.type_input_1()]}{split_dict_title[input.split_id_1()]}',
782
+ fontsize=30, ha='center', va='bottom',fontstyle='italic')
783
+ # level_dict
784
+ # split_dict
785
+ # type_dict
786
+
787
+ #####################
788
+
789
+ # player_headshot(player_input=player_input_2, ax=ax_headshot_2,sport_id=sport_id_2,season=year_input_2)
790
+ # player_bio(pitcher_id=player_input_2, ax=ax_bio_2,sport_id=sport_id_2,year_input=year_input_2,
791
+ # df=df_2,split=input.split_id_2(),game_type=input.type_input_2())
792
+
793
+ # # df_teams = scrape.get_teams()
794
+ # if input.switch_2():
795
+
796
+ # # Get the logo URL from the image dictionary using the team abbreviation
797
+ # logo_url = input.logo_select_2()
798
+
799
+ # # Send a GET request to the logo URL
800
+ # response = requests.get(logo_url)
801
+
802
+ # # Open the image from the response content
803
+ # img = Image.open(BytesIO(response.content))
804
+
805
+ # # Display the image on the axis
806
+ # ax_logo_2.set_xlim(0, 1)
807
+ # ax_logo_2.set_ylim(0, 1)
808
+ # ax_logo_2.imshow(img, extent=[0, 1, 0, 1], origin='upper')
809
+
810
+ # # Turn off the axis
811
+ # ax_logo_2.axis('off')
812
 
813
+ # else:
814
+ # plot_logo(pitcher_id=player_input_2, ax=ax_logo_2, df_team=df_teams,df_players=scrape.get_players(sport_id_2,year_input_2))
815
+
816
+
817
+ # break_plot(df = df_2,ax=ax_break_2)
818
+ summary_table(df=df_2,
819
+ ax=ax_table_2)
820
+ ax_table_2.text(x=0.5,y=0.83,s=f'{df_2["pitcher_name"][0]} - {year_input_2} - {level_dict[str(sport_id_2)]} - {type_dict[input.type_input_2()]}{split_dict_title[input.split_id_2()]}',
821
+ fontsize=30, ha='center', va='bottom',fontstyle='italic')
822
+
823
+ #########################
824
+ plot_footer(ax_footer)
825
+
826
+ fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
827
+
828
+ # # Draw a line halfway down the figure, starting from the top middle and going to the center
829
+ # line = plt.Line2D([0.5, 0.5], [0.55, 0.95], transform=fig.transFigure, color="grey",alpha=0.4, linewidth=2)
830
+ # fig.add_artist(line)
831
+
832
+
833
+
834
+ app = App(app_ui, server)
835
 
836
+
functions/__pycache__/pitch_summary_functions.cpython-39.pyc CHANGED
Binary files a/functions/__pycache__/pitch_summary_functions.cpython-39.pyc and b/functions/__pycache__/pitch_summary_functions.cpython-39.pyc differ
 
functions/pitch_summary_functions.py CHANGED
@@ -662,18 +662,21 @@ def summary_table(df: pl.DataFrame, ax: plt.Axes):
662
  format_cells(['count_percent', 'zone_percent', 'chase_percent', 'whiff_percent'], '{:,.1%}')
663
  format_cells(['tj_stuff_plus', 'pitch_grade', 'spin_rate'], '{:,.0f}')
664
 
665
- # Create legend for pitch types
666
- items_in_order = (df.sort("pitch_count", descending=True)['pitch_type'].unique(maintain_order=True).to_numpy())
667
- colour_pitches = [dict_colour[x] for x in items_in_order]
668
- label = [dict_pitch[x] for x in items_in_order]
669
- handles = [plt.scatter([], [], color=color, marker='o', s=100) for color in colour_pitches]
670
- if len(label) > 5:
671
- ax.legend(handles, label, bbox_to_anchor=(0.1, 0.81, 0.8, 0.14), ncol=5,
672
- fancybox=True, loc='lower center', fontsize=16, framealpha=1.0, markerscale=1.7, prop={'family': 'calibi', 'size': 16})
673
- else:
674
- ax.legend(handles, label, bbox_to_anchor=(0.1, 0.81, 0.8, 0.14), ncol=5,
675
- fancybox=True, loc='lower center', fontsize=20, framealpha=1.0, markerscale=2, prop={'family': 'calibi', 'size': 20})
676
  ax.axis('off')
 
 
 
677
 
678
  def plot_footer(ax: plt.Axes):
679
  """
@@ -685,8 +688,8 @@ def plot_footer(ax: plt.Axes):
685
  The axis to add the footer text to.
686
  """
687
  # Add footer text
688
- ax.text(0, 1, 'By: @TJStats', ha='left', va='top', fontsize=24)
689
- ax.text(0.5, 0.25,
690
  '''
691
  Colour Coding Compares to League Average By Pitch
692
  tjStuff+ calculates the Expected Run Value (xRV) of a pitch regardless of type
@@ -694,7 +697,31 @@ def plot_footer(ax: plt.Axes):
694
  Pitch Grade scales tjStuff+ to the traditional 20-80 Scouting Scale for a given pitch type
695
  ''',
696
  ha='center', va='bottom', fontsize=12)
697
- ax.text(1, 1, 'Data: MLB, Fangraphs\nImages: MLB, ESPN', ha='right', va='top', fontsize=24)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
698
  ax.axis('off')
699
 
700
  # Function to get an image from a URL and display it on the given axis
@@ -725,7 +752,7 @@ def player_headshot(player_input: str, ax: plt.Axes, sport_id: int, season: int)
725
  img = Image.open(BytesIO(response.content))
726
 
727
  # Display the image on the axis
728
- ax.set_xlim(0, 1.3)
729
  ax.set_ylim(0, 1)
730
  ax.imshow(img, extent=[0, 1, 0, 1] if sport_id == 1 else [1/6, 5/6, 0, 1], origin='upper')
731
  except PIL.UnidentifiedImageError:
@@ -735,7 +762,18 @@ def player_headshot(player_input: str, ax: plt.Axes, sport_id: int, season: int)
735
  # Turn off the axis
736
  ax.axis('off')
737
 
738
- def player_bio(pitcher_id: str, ax: plt.Axes, sport_id: int, year_input: int):
 
 
 
 
 
 
 
 
 
 
 
739
  """
740
  Display the player's bio information on the given axis.
741
 
@@ -750,6 +788,9 @@ def player_bio(pitcher_id: str, ax: plt.Axes, sport_id: int, year_input: int):
750
  year_input : int
751
  The season year.
752
  """
 
 
 
753
  # Construct the URL to fetch player data
754
  url = f"https://statsapi.mlb.com/api/v1/people?personIds={pitcher_id}&hydrate=currentTeam"
755
 
@@ -764,19 +805,24 @@ def player_bio(pitcher_id: str, ax: plt.Axes, sport_id: int, year_input: int):
764
  weight = data['people'][0]['weight']
765
 
766
  # Display the player's name, handedness, age, height, and weight on the axis
767
- ax.text(0.5, 1, f'{player_name}', va='top', ha='center', fontsize=56)
768
- ax.text(0.5, 0.7, f'{pitcher_hand}HP, Age: {age}, {height}/{weight}', va='top', ha='center', fontsize=30)
769
- ax.text(0.5, 0.45, f'Season Pitching Summary', va='top', ha='center', fontsize=40)
770
 
 
771
  # Make API call to retrieve sports information
772
  response = requests.get(url='https://statsapi.mlb.com/api/v1/sports').json()
773
-
774
- # Convert the JSON response into a Polars DataFrame
775
  df_sport_id = pl.DataFrame(response['sports'])
776
  abb = df_sport_id.filter(pl.col('id') == sport_id)['abbreviation'][0]
 
 
 
 
 
777
 
778
  # Display the season and sport abbreviation
779
- ax.text(0.5, 0.20, f'{year_input} {abb} Season', va='top', ha='center', fontsize=30, fontstyle='italic')
 
780
 
781
  # Turn off the axis
782
  ax.axis('off')
@@ -861,9 +907,9 @@ def plot_logo(pitcher_id: str, ax: plt.Axes, df_team: pl.DataFrame, df_players:
861
  img = Image.open(BytesIO(response.content))
862
 
863
  # Display the image on the axis
864
- ax.set_xlim(0, 1.3)
865
  ax.set_ylim(0, 1)
866
- ax.imshow(img, extent=[0.3, 1.3, 0, 1], origin='upper')
867
 
868
  # Turn off the axis
869
  ax.axis('off')
 
662
  format_cells(['count_percent', 'zone_percent', 'chase_percent', 'whiff_percent'], '{:,.1%}')
663
  format_cells(['tj_stuff_plus', 'pitch_grade', 'spin_rate'], '{:,.0f}')
664
 
665
+ # # Create legend for pitch types
666
+ # items_in_order = (df.sort("pitch_count", descending=True)['pitch_type'].unique(maintain_order=True).to_numpy())
667
+ # colour_pitches = [dict_colour[x] for x in items_in_order]
668
+ # label = [dict_pitch[x] for x in items_in_order]
669
+ # handles = [plt.scatter([], [], color=color, marker='o', s=100) for color in colour_pitches]
670
+ # if len(label) > 5:
671
+ # ax.legend(handles, label, bbox_to_anchor=(0.1, 0.81, 0.8, 0.14), ncol=5,
672
+ # fancybox=True, loc='lower center', fontsize=16, framealpha=1.0, markerscale=1.7, prop={'family': 'calibi', 'size': 16})
673
+ # else:
674
+ # ax.legend(handles, label, bbox_to_anchor=(0.1, 0.81, 0.8, 0.14), ncol=5,
675
+ # fancybox=True, loc='lower center', fontsize=20, framealpha=1.0, markerscale=2, prop={'family': 'calibi', 'size': 20})
676
  ax.axis('off')
677
+ # ax.set_title(f'{df["pitcher_name"][0]}', fontdict=font_properties_titles)
678
+ # ax.text(x=0.5,y=0.90,s=f'{df["pitcher_name"][0]}',
679
+ # fontsize=30, ha='center', va='bottom',)
680
 
681
  def plot_footer(ax: plt.Axes):
682
  """
 
688
  The axis to add the footer text to.
689
  """
690
  # Add footer text
691
+ ax.text(0, 1, 'By: Thomas Nestico\n @TJStats', ha='left', va='top', fontsize=24)
692
+ ax.text(0.5, 0.15,
693
  '''
694
  Colour Coding Compares to League Average By Pitch
695
  tjStuff+ calculates the Expected Run Value (xRV) of a pitch regardless of type
 
697
  Pitch Grade scales tjStuff+ to the traditional 20-80 Scouting Scale for a given pitch type
698
  ''',
699
  ha='center', va='bottom', fontsize=12)
700
+ ax.text(1, 1, 'Data: MLB\nImages: MLB, ESPN', ha='right', va='top', fontsize=24)
701
+ ax.axis('off')
702
+
703
+
704
+
705
+ def plot_footer_break(ax: plt.Axes):
706
+ """
707
+ Add footer text to the plot.
708
+
709
+ Parameters
710
+ ----------
711
+ ax : plt.Axes
712
+ The axis to add the footer text to.
713
+ """
714
+ # Add footer text
715
+ ax.text(0, 0.25, 'By: Thomas Nestico\n @TJStats', ha='left', va='bottom', fontsize=24)
716
+ # ax.text(0.5, 0.15,
717
+ # '''
718
+ # Colour Coding Compares to League Average By Pitch
719
+ # tjStuff+ calculates the Expected Run Value (xRV) of a pitch regardless of type
720
+ # tjStuff+ is normally distributed, where 100 is the mean and Standard Deviation is 10
721
+ # Pitch Grade scales tjStuff+ to the traditional 20-80 Scouting Scale for a given pitch type
722
+ # ''',
723
+ # ha='center', va='bottom', fontsize=12)
724
+ ax.text(1, 0.25, 'Data: MLB\nImages: MLB, ESPN', ha='right', va='bottom', fontsize=24)
725
  ax.axis('off')
726
 
727
  # Function to get an image from a URL and display it on the given axis
 
752
  img = Image.open(BytesIO(response.content))
753
 
754
  # Display the image on the axis
755
+ ax.set_xlim(0, 1)
756
  ax.set_ylim(0, 1)
757
  ax.imshow(img, extent=[0, 1, 0, 1] if sport_id == 1 else [1/6, 5/6, 0, 1], origin='upper')
758
  except PIL.UnidentifiedImageError:
 
762
  # Turn off the axis
763
  ax.axis('off')
764
 
765
+ splits_title = {
766
+
767
+ 'all':'',
768
+ 'left':' vs LHH',
769
+ 'right':' vs RHH',
770
+
771
+ }
772
+
773
+ type_dict = {'R':'Regular Season',
774
+ 'S':'Spring',
775
+ 'P':'Playoffs' }
776
+ def player_bio(pitcher_id: str, ax: plt.Axes, sport_id: int,game_type: str, year_input: int,split: str,df: pl.DataFrame):
777
  """
778
  Display the player's bio information on the given axis.
779
 
 
788
  year_input : int
789
  The season year.
790
  """
791
+ start_date = df['game_date'][0]
792
+ end_date = df['game_date'][-1]
793
+
794
  # Construct the URL to fetch player data
795
  url = f"https://statsapi.mlb.com/api/v1/people?personIds={pitcher_id}&hydrate=currentTeam"
796
 
 
805
  weight = data['people'][0]['weight']
806
 
807
  # Display the player's name, handedness, age, height, and weight on the axis
808
+ ax.text(0.5, 1, f'{player_name}', va='top', ha='center', fontsize=36)
809
+ ax.text(0.5, 0.68, f'{pitcher_hand}HP, Age: {age}, {height}/{weight}', va='top', ha='center', fontsize=24)
 
810
 
811
+ # Convert the JSON response into a Polars DataFrame
812
  # Make API call to retrieve sports information
813
  response = requests.get(url='https://statsapi.mlb.com/api/v1/sports').json()
814
+
 
815
  df_sport_id = pl.DataFrame(response['sports'])
816
  abb = df_sport_id.filter(pl.col('id') == sport_id)['abbreviation'][0]
817
+ # title = f'{df["game_date"][0]} vs {df["batter_team"][0]} ({type_dict[game_type[0]]}){split_title[split]}'
818
+ ax.text(0.5, 0.45, f'{year_input} {abb} {type_dict[game_type]}', va='top', ha='center', fontsize=24)
819
+
820
+
821
+
822
 
823
  # Display the season and sport abbreviation
824
+ ax.text(0.5, 0.20, f'{start_date} to {end_date}{splits_title[split]}', va='top', ha='center', fontsize=24, fontstyle='italic')
825
+
826
 
827
  # Turn off the axis
828
  ax.axis('off')
 
907
  img = Image.open(BytesIO(response.content))
908
 
909
  # Display the image on the axis
910
+ ax.set_xlim(0, 1)
911
  ax.set_ylim(0, 1)
912
+ ax.imshow(img, extent=[0, 1, 0, 1], origin='upper')
913
 
914
  # Turn off the axis
915
  ax.axis('off')