Upload 6 files
Browse files- app.py +592 -112
- functions/__pycache__/pitch_summary_functions.cpython-39.pyc +0 -0
- functions/pitch_summary_functions.py +70 -24
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('
|
| 115 |
-
ui.column(4, ui.input_select('
|
| 116 |
-
ui.column(4, ui.input_select('
|
| 117 |
),
|
| 118 |
# Row for the action button to get player list
|
| 119 |
-
ui.row(ui.input_action_button("
|
|
|
|
|
|
|
| 120 |
# Row for selecting the player
|
| 121 |
-
ui.row(ui.column(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
# Row for selecting the date range
|
| 123 |
-
ui.row(ui.column(12, ui.output_ui('
|
| 124 |
-
|
| 125 |
-
# Rows for selecting plots and split options
|
| 126 |
ui.row(
|
| 127 |
-
ui.column(
|
| 128 |
-
ui.column(
|
| 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(
|
| 133 |
-
ui.column(
|
|
|
|
| 134 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
ui.row(
|
| 136 |
-
ui.column(6, ui.input_switch("
|
| 137 |
-
ui.column(6, ui.input_select('
|
| 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("
|
| 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.
|
| 159 |
-
def
|
| 160 |
-
|
| 161 |
-
year_input = int(input.
|
| 162 |
-
sport_id = int(input.
|
| 163 |
-
player_input = int(input.
|
| 164 |
-
start_date = str(input.
|
| 165 |
-
end_date = str(input.
|
| 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.
|
| 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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
| 187 |
-
def
|
|
|
|
| 188 |
# Get the list of pitchers for the selected level and season
|
| 189 |
-
df_pitcher_info = scrape.get_players(sport_id=int(input.
|
| 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("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
|
| 198 |
@render.ui
|
| 199 |
-
@reactive.event(input.
|
| 200 |
-
def
|
| 201 |
# Create a date range input for selecting the date range within the selected year
|
| 202 |
-
return ui.input_date_range("
|
| 203 |
-
start=f"{int(input.
|
| 204 |
-
end=f"{int(input.
|
| 205 |
-
min=f"{int(input.
|
| 206 |
-
max=f"{int(input.
|
|
|
|
|
|
|
| 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.
|
| 226 |
-
sport_id = int(input.
|
| 227 |
-
player_input = int(input.
|
| 228 |
-
start_date = str(input.
|
| 229 |
-
end_date = str(input.
|
| 230 |
|
| 231 |
print(year_input, sport_id, player_input, start_date, end_date)
|
| 232 |
|
| 233 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,
|
| 247 |
-
height_ratios=[
|
| 248 |
-
width_ratios=[
|
|
|
|
| 249 |
|
|
|
|
| 250 |
|
| 251 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|
| 267 |
-
ax_header = fig.add_subplot(gs[0,1:
|
| 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 |
-
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
|
| 285 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
|
| 287 |
# Get the logo URL from the image dictionary using the team abbreviation
|
| 288 |
-
logo_url = input.
|
| 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 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
|
| 301 |
# Turn off the axis
|
| 302 |
-
|
| 303 |
|
| 304 |
else:
|
| 305 |
-
plot_logo(pitcher_id=
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
|
| 356 |
-
|
|
|
|
| 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 |
-
|
| 672 |
-
|
| 673 |
-
else:
|
| 674 |
-
|
| 675 |
-
|
| 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.
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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=
|
| 768 |
-
ax.text(0.5, 0.
|
| 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'{
|
|
|
|
| 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
|
| 865 |
ax.set_ylim(0, 1)
|
| 866 |
-
ax.imshow(img, extent=[0
|
| 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')
|