TimStats commited on
Commit
0a090a2
·
verified ·
1 Parent(s): e4ec3b2

Update app.R

Browse files
Files changed (1) hide show
  1. app.R +198 -216
app.R CHANGED
@@ -1,3 +1,4 @@
 
1
  library(shiny)
2
  library(plotly)
3
  library(gridlayout)
@@ -19,9 +20,26 @@ library(gridExtra)
19
  library(png)
20
  library(xgboost)
21
  library(httr)
 
 
22
  pdf(file = NULL)
23
  Sys.setenv(TZ='EST')
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  is_barrel <- function(df) {
26
  df$barrel <- with(df, ifelse(hit_angle <= 50 & hit_speed >= 97 & hit_speed * 1.5 -
27
  hit_angle >= 117 & hit_speed + hit_angle >= 123, 1, 0))
@@ -29,7 +47,7 @@ is_barrel <- function(df) {
29
  }
30
 
31
  VAA <- function(milbtotal){
32
- milbtotal <- milbtotal |>
33
  mutate(VAA = -atan((vz0+(az*(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))-vy0)/
34
  ay))/(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))))*(180/pi))
35
  }
@@ -38,9 +56,9 @@ pitcher_summary <- function(game_pk,date){
38
  gdate <- as.Date.character(date)
39
  gdate <- as.Date(gdate)
40
  tmilb <- mlb_pbp(game_pk)
41
- tmilb <- tmilb |>
42
  filter(type == "pitch")
43
- tmilb <- tmilb |>
44
  select(matchup.batter.fullName,matchup.batter.id,matchup.pitcher.fullName,
45
  matchup.pitcher.id,result.event,details.description,details.type.description,
46
  result.description,pitchData.startSpeed,pitchData.plateTime,pitchData.zone,
@@ -59,10 +77,10 @@ pitcher_summary <- function(game_pk,date){
59
  "IVB","HB","hit_speed","hit_angle","hit_distance","inPlay",
60
  "lastPitch","spinDirection","phand")
61
  tmilb <- is_barrel(tmilb)
62
- tmilb <- tmilb |>
63
  mutate(is_strike_swinging = ifelse(description == "Swinging Strike" |
64
  description == "Foul Tip",TRUE,FALSE))
65
- tmilb <- tmilb |>
66
  mutate(date = gdate)
67
  return(tmilb)
68
  }
@@ -105,69 +123,46 @@ pitch_plot <- function(game){
105
  axis.ticks.y=element_blank())
106
  }
107
 
108
- # Assume FB.model, Off.model, and Break.model are already loaded in the environment
 
 
 
109
 
110
- # Function to calculate VAA (Vertical Approach Angle)
111
  calculate_VAA <- function(vz0, ay, az, vy0, y0) {
112
  -atan((vz0+(az*(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))-vy0)/
113
- ay))/(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))))*(180/pi)
114
  }
115
 
116
- # Function to calculate EAA (Effective Approach Angle)
117
  calculate_EAA <- function(extension) {
118
  extension / 6.3
119
  }
120
 
121
- # Function to calculate SADiff (Spin Axis Differential)
122
  calculate_SADiff <- function(pfxX, pfxZ, spinDirection) {
123
- # Calculate initial inSA
124
  inSA <- atan2(pfxZ, pfxX) * 180/pi + 90
125
-
126
- # Adjust inSA if it's negative
127
  inSA <- ifelse(inSA < 0, inSA + 360, inSA)
128
-
129
- # Calculate SADiff
130
  SADiff <- spinDirection - inSA
131
-
132
- # Adjust SADiff to be within -180 to 180 range
133
  SADiff <- ifelse(SADiff > 180, SADiff - 360, SADiff)
134
  SADiff <- ifelse(SADiff < -180, SADiff + 360, SADiff)
135
-
136
  return(SADiff)
137
  }
138
 
139
- # Function to scale TimStuff
140
  scale_TimStuff <- function(raw_score, model_mean, model_sd) {
141
  scaled_score <- (raw_score - model_mean) / model_sd
142
  result <- 50 - (scaled_score * 10)
143
-
144
- # Add logging
145
- # cat("Raw score:", raw_score, "\n")
146
- # cat("Model mean:", model_mean, "\n")
147
- # cat("Model SD:", model_sd, "\n")
148
- # cat("Scaled score:", scaled_score, "\n")
149
- # cat("Final result:", result, "\n")
150
-
151
- # Ensure the result is within a reasonable range
152
- # result <- max(min(result, 100), 0)
153
-
154
  return(result)
155
  }
156
- FB <- xgb.load('FB.model')
157
- Off <- xgb.load('Off.model')
158
- Break <- xgb.load('Break.model')
159
- # Modify the summary_table function
160
  summary_table <- function(game) {
161
  rows <- nrow(game)
162
- sumtable <- game |>
163
  mutate(
164
  VAA = calculate_VAA(vz0, ay,az, vy0, y0),
165
  EAA = calculate_EAA(extension),
166
  SADiff = calculate_SADiff(pfxX, pfxZ,spinDirection)
167
- ) |>
168
  mutate(team_fielding_id = ifelse(description == "Called Strike" |
169
  description == "Swinging Strike" |
170
- description == "Swinging Strike (Blocked)", 1, 0)) |>
171
  mutate(swing = ifelse(description == "Foul" |
172
  description == "Foul Pitchout" |
173
  description == "In play, no out" |
@@ -175,10 +170,10 @@ summary_table <- function(game) {
175
  description == "In play, run(s)" |
176
  description == "Swinging Strike" |
177
  description == "swinging Strike (Blocked)" |
178
- description == "Foul Tip", 1, 0)) |>
179
- mutate(is_strike_swinging = ifelse(is_strike_swinging == TRUE, 1, 0)) |>
180
- mutate(Pitch = pitch_name) |>
181
- rowwise() |>
182
  mutate(TimStuff = if (phand == 'L') {
183
  case_when(
184
  Pitch %in% c("Four-Seam Fastball", "Sinker", "Cutter", "Fastball") ~
@@ -217,8 +212,8 @@ summary_table <- function(game) {
217
  -0.004822031, 0.007912765)),
218
  TRUE ~ NA_real_
219
  )
220
- }) |>
221
- group_by(Pitch) |>
222
  summarize(
223
  Pitches = n(),
224
  'Pitch%' = round(sum(Pitches)/sum(rows) * 100, digits = 1),
@@ -228,39 +223,34 @@ summary_table <- function(game) {
228
  'IVB' = round(mean(IVB, na.rm = TRUE), digits = 1),
229
  'HB' = round(mean(HB, na.rm = TRUE), digits = 1),
230
  'VAA' = round(mean(VAA, na.rm = TRUE), digits = 1),
231
- # 'EAA' = round(mean(EAA, na.rm = TRUE), digits = 2),
232
- # 'x0' = round(mean(x0, na.rm = TRUE), digits = 2),
233
- # 'z0' = round(mean(z0, na.rm = TRUE), digits = 2),
234
- # 'SADiff' = round(mean(SADiff, na.rm = TRUE), digits = 2),
235
  'CSW%' = round(sum(team_fielding_id, na.rm = TRUE) / sum(!is.na(team_fielding_id)) * 100, digits = 1),
236
  'Whiff%' = round(sum(is_strike_swinging, na.rm = TRUE) / sum(swing, na.rm = TRUE) * 100, digits = 1),
237
  'TimStuff' = round(mean(TimStuff, na.rm = TRUE), digits = 0)
238
- ) |>
239
  arrange(-Pitches)
240
 
241
  return(sumtable)
242
  }
243
 
244
- # The rest of your code remains the same
245
-
246
  mlbid <- mlb_schedule(season = 2024, level_ids = "1")
247
- mlbteamH <- mlbid |>
248
  select(teams_home_team_name)
249
  mlbteamH <- distinct(mlbteamH)
250
- mlbteamA <- mlbid |>
251
  select(teams_away_team_name)
252
  mlbteamA <- distinct(mlbteamA)
253
 
254
  aaaid <- mlb_schedule(season = 2024, level_ids = "11")
255
- aaateamH <- aaaid |>
256
  select(teams_home_team_name)
257
  aaateamH <- distinct(aaateamH)
258
- aaateamA <- aaaid |>
259
  select(teams_away_team_name)
260
  aaateamA <- distinct(aaateamA)
261
 
262
  fslid <- mlb_schedule(season = 2024, level_ids = "14")
263
- fslid <- fslid |>
264
  filter(teams_home_team_name == "Daytona Tortugas" |
265
  teams_home_team_name == "Jupiter Hammerheads" |
266
  teams_home_team_name == "Palm Beach Cardinals" |
@@ -271,21 +261,22 @@ fslid <- fslid |>
271
  teams_home_team_name == "Fort Myers Mighty Mussels" |
272
  teams_home_team_name == "Lakeland Flying Tigers" |
273
  teams_home_team_name == "Tampa Tarpons")
274
- fslteamH <- fslid |>
275
  select(teams_home_team_name)
276
  fslteamH <- distinct(fslteamH)
277
- fslteamA <- fslid |>
278
  select(teams_away_team_name)
279
  fslteamA <- distinct(fslteamA)
280
 
281
  sbid <- mlb_schedule(season = 2024, level_ids = "22")
282
- sbteamH <- sbid |>
283
  select(teams_home_team_name)
284
  sbteamH <- distinct(sbteamH)
285
- sbteamA <- sbid |>
286
  select(teams_away_team_name)
287
  sbteamA <- distinct(sbteamA)
288
 
 
289
  ui <- fluidPage(
290
  tags$head(
291
  tags$style(HTML("
@@ -310,7 +301,6 @@ ui <- fluidPage(
310
  actionButton("update", "Find Pitcher", icon("magnifying-glass"),
311
  style = "color: #FFFFFF; background-color: #0077B6; width: 100%;"),
312
  selectizeInput("pitcher", "Pitcher Name:", c(" ")),
313
- # selectizeInput("league", "MLB or MiLB Photo", c("MLB", "MiLB")),
314
  textInput("title", "Card Title"),
315
  actionButton("update1", "Make Card", icon("plus"),
316
  style = "color: #FFFFFF; background-color: #0077B6; width: 100%;"),
@@ -321,7 +311,6 @@ ui <- fluidPage(
321
  )
322
  )
323
  )
324
-
325
  server <- function(input, output, session) {
326
  observeEvent(input$level, {
327
  if(input$level == "AAA"){
@@ -343,168 +332,162 @@ server <- function(input, output, session) {
343
  })
344
 
345
  observeEvent(input$update, {
346
- if(input$level == "AAA"){
347
- pname <- aaaid |>
348
- filter(date == input$date) |>
349
- filter(teams_home_team_name == input$homeT) |>
350
- filter(teams_away_team_name == input$awayT) |>
351
- filter(game_number == input$gamenum)
352
- pname <- tryCatch({pitcher_summary(pname[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")})
353
- updateSelectizeInput(session = getDefaultReactiveDomain(),"pitcher","Pitcher:",choices = pname[,3])
354
- }
355
- if(input$level == "FSL"){
356
- pname <- fslid |>
357
- filter(date == input$date) |>
358
- filter(teams_home_team_name == input$homeT) |>
359
- filter(teams_away_team_name == input$awayT) |>
360
- filter(game_number == input$gamenum)
361
- pname <- tryCatch({pitcher_summary(pname[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")})
362
- updateSelectizeInput(session = getDefaultReactiveDomain(),"pitcher","Pitcher:",choices = pname[,3])
363
- }
364
- if(input$level == "MLB"){
365
- pname <- mlbid |>
366
- filter(date == as.character.Date(input$date)) |>
367
- filter(teams_home_team_name == input$homeT) |>
368
- filter(teams_away_team_name == input$awayT) |>
369
- filter(game_number == input$gamenum)
370
- pname <- tryCatch({pitcher_summary(pname[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")})
371
- updateSelectizeInput(session = getDefaultReactiveDomain(),"pitcher","Pitcher:",choices = pname[,3])
372
- }
373
- if(input$level == "College (Statcast Parks Only)"){
374
- pname <- sbid |>
375
- filter(date == input$date) |>
376
- filter(teams_home_team_name == input$homeT) |>
377
- filter(teams_away_team_name == input$awayT) |>
378
- filter(game_number == input$gamenum)
379
- pname <- tryCatch({pitcher_summary(pname[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")})
380
- updateSelectizeInput(session = getDefaultReactiveDomain(),"pitcher","Pitcher:",choices = pname[,3])
381
- }
 
 
 
 
 
 
382
  })
383
 
384
  combinedPlot <- reactiveVal()
385
  observeEvent(input$update1, {
386
- if(input$level == "MLB"){
387
- id <- mlbid |>
388
- filter(date == as.character.Date(input$date)) |>
389
- filter(teams_home_team_name == input$homeT) |>
390
- filter(teams_away_team_name == input$awayT) |>
391
- filter(game_number == input$gamenum)
392
- game <- tryCatch({pitcher_summary(id[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")})
393
- game <- game |>
394
- filter(`Pitcher Name` == input$pitcher)
395
- id <- as.character(game[1,4])
396
- }
397
- if(input$level == "FSL"){
398
- id <- fslid |>
399
- filter(date == as.character.Date(input$date)) |>
400
- filter(teams_home_team_name == input$homeT) |>
401
- filter(teams_away_team_name == input$awayT) |>
402
- filter(game_number == input$gamenum)
403
- game <- tryCatch({pitcher_summary(id[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")})
404
- game <- game |>
405
- filter(`Pitcher Name` == input$pitcher)
406
- id <- as.character(game[1,4])
407
- }
408
- if(input$level == "AAA"){
409
- id <- aaaid |>
410
- filter(date == as.character.Date(input$date)) |>
411
- filter(teams_home_team_name == input$homeT) |>
412
- filter(teams_away_team_name == input$awayT) |>
413
- filter(game_number == input$gamenum)
414
- game <- tryCatch({pitcher_summary(id[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")})
415
- game <- game |>
416
- filter(`Pitcher Name` == input$pitcher)
417
- id <- as.character(game[1,4])
418
- }
419
- if(input$level == "College (Statcast Parks Only)"){
420
- id <- sbid |>
421
- filter(date == as.character.Date(input$date)) |>
422
- filter(teams_home_team_name == input$homeT) |>
423
- filter(teams_away_team_name == input$awayT) |>
424
- filter(game_number == input$gamenum)
425
- game <- tryCatch({pitcher_summary(id[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")})
426
- game <- game |>
427
- filter(`Pitcher Name` == input$pitcher)
428
- id <- as.character(game[1,4])
429
- }
430
-
431
- break_plot <- break_plot(game)
432
- pitch_plot <- pitch_plot(game)
433
-
434
- # Create table plot
435
- # Update the tableGrob in the observeEvent(input$update1, {...}) block
436
- table_plot <- tableGrob(summary_table(game), theme = ttheme_default(
437
- core = list(fg_params = list(cex = 1.7)),
438
- colhead = list(fg_params = list(cex = 1.2)),
439
- rowhead = list(fg_params = list(cex = 2.5))))
440
- table_plot$widths[[1]] <- unit(0, "cm")
441
-
442
- # Find the text grobs in the first column
443
- text_grobs <- which(sapply(table_plot$grobs, function(g) {
444
- g$name == "core-fg-1" && inherits(g$children[[1]], "text")
445
- }))
446
-
447
- # Remove the numbers from these text grobs
448
- for (i in text_grobs) {
449
- text <- table_plot$grobs[[i]]$children[[1]]$label
450
- table_plot$grobs[[i]]$children[[1]]$label <- sub("^\\d+\\s*", "", text)
451
- }
452
-
453
- # Draw the modified table
454
- grid.newpage()
455
- grid.draw(table_plot)
456
- # Download and process image
457
- # if(input$league == "MLB"){
458
- # y <- paste("https://midfield.mlbstatic.com/v1/people/","/mlb/300?circle=false",sep = id)
459
- # } else {
460
- # y <- paste("https://midfield.mlbstatic.com/v1/people/","/milb/300?circle=false",sep = id)
461
- # }
462
- #if (input$league == "MLB") {
463
- y <- paste0("https://midfield.mlbstatic.com/v1/people/", id, "/mlb/300?circle=false")
464
- # } else {
465
- # y <- paste0("https://midfield.mlbstatic.com/v1/people/", id, "/milb/300?circle=false")
466
- # }
467
-
468
- # Initialize the URL to NULL
469
- y <- NULL
470
-
471
- # Try the MLB URL first
472
- mlb_url <- paste0("https://midfield.mlbstatic.com/v1/people/", id, "/mlb/300?circle=false")
473
- response <- httr::GET(mlb_url)
474
-
475
- if (httr::status_code(response) == 200) {
476
- y <- mlb_url # Use MLB URL if it returns a 200 status code
477
- } else if (httr::status_code(response) == 404) {
478
- # If MLB URL fails with a 404, try the MiLB URL
479
  milb_url <- paste0("https://midfield.mlbstatic.com/v1/people/", id, "/milb/300?circle=false")
480
- response <- httr::GET(milb_url)
481
 
482
- if (httr::status_code(response) == 200) {
483
- y <- milb_url # Use MiLB URL if it returns a 200 status code
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484
  } else {
485
- stop("Both MLB and MiLB URLs failed with 404 error")
486
  }
487
- } else {
488
- stop("MLB URL failed with an unexpected error")
489
- }
490
-
491
- photo <- tempfile()
492
- download.file(y, photo, mode = 'wb')
493
- img <- readPNG(photo)
494
- img_grob <- rasterGrob(img, interpolate = TRUE)
495
-
496
- # Combine all elements into one plot
497
- combined <- grid.arrange(
498
- arrangeGrob(table_plot, img_grob, ncol = 2, widths = c(2/3, 1/3)),
499
- arrangeGrob(break_plot, pitch_plot, ncol = 2),
500
- nrow = 2,
501
- top = textGrob(paste(input$title, "Made by @TimStats", sep = " "), gp = gpar(fontsize = 20, font = 2))
502
- )
503
-
504
- combinedPlot(combined)
505
-
506
- output$combinedPlot <- renderPlot({
507
- grid.draw(combinedPlot())
508
  })
509
  })
510
 
@@ -518,5 +501,4 @@ server <- function(input, output, session) {
518
  )
519
  }
520
 
521
-
522
  shinyApp(ui, server)
 
1
+ # Load required libraries
2
  library(shiny)
3
  library(plotly)
4
  library(gridlayout)
 
20
  library(png)
21
  library(xgboost)
22
  library(httr)
23
+ library(magick)
24
+
25
  pdf(file = NULL)
26
  Sys.setenv(TZ='EST')
27
 
28
+ # Helper functions
29
+ download_and_process_image <- function(url) {
30
+ tryCatch({
31
+ temp_file <- tempfile(fileext = ".jpg")
32
+ download.file(url, temp_file, mode = "wb")
33
+ img <- image_read(temp_file)
34
+ png_file <- tempfile(fileext = ".png")
35
+ image_write(img, path = png_file, format = "png")
36
+ return(png_file)
37
+ }, error = function(e) {
38
+ warning(paste("Error processing image:", e$message))
39
+ return(NULL)
40
+ })
41
+ }
42
+
43
  is_barrel <- function(df) {
44
  df$barrel <- with(df, ifelse(hit_angle <= 50 & hit_speed >= 97 & hit_speed * 1.5 -
45
  hit_angle >= 117 & hit_speed + hit_angle >= 123, 1, 0))
 
47
  }
48
 
49
  VAA <- function(milbtotal){
50
+ milbtotal <- milbtotal %>%
51
  mutate(VAA = -atan((vz0+(az*(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))-vy0)/
52
  ay))/(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))))*(180/pi))
53
  }
 
56
  gdate <- as.Date.character(date)
57
  gdate <- as.Date(gdate)
58
  tmilb <- mlb_pbp(game_pk)
59
+ tmilb <- tmilb %>%
60
  filter(type == "pitch")
61
+ tmilb <- tmilb %>%
62
  select(matchup.batter.fullName,matchup.batter.id,matchup.pitcher.fullName,
63
  matchup.pitcher.id,result.event,details.description,details.type.description,
64
  result.description,pitchData.startSpeed,pitchData.plateTime,pitchData.zone,
 
77
  "IVB","HB","hit_speed","hit_angle","hit_distance","inPlay",
78
  "lastPitch","spinDirection","phand")
79
  tmilb <- is_barrel(tmilb)
80
+ tmilb <- tmilb %>%
81
  mutate(is_strike_swinging = ifelse(description == "Swinging Strike" |
82
  description == "Foul Tip",TRUE,FALSE))
83
+ tmilb <- tmilb %>%
84
  mutate(date = gdate)
85
  return(tmilb)
86
  }
 
123
  axis.ticks.y=element_blank())
124
  }
125
 
126
+ # Load models
127
+ FB <- xgb.load('FB.model')
128
+ Off <- xgb.load('Off.model')
129
+ Break <- xgb.load('Break.model')
130
 
 
131
  calculate_VAA <- function(vz0, ay, az, vy0, y0) {
132
  -atan((vz0+(az*(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))-vy0)/
133
+ ay))/(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))))*(180/pi)
134
  }
135
 
 
136
  calculate_EAA <- function(extension) {
137
  extension / 6.3
138
  }
139
 
 
140
  calculate_SADiff <- function(pfxX, pfxZ, spinDirection) {
 
141
  inSA <- atan2(pfxZ, pfxX) * 180/pi + 90
 
 
142
  inSA <- ifelse(inSA < 0, inSA + 360, inSA)
 
 
143
  SADiff <- spinDirection - inSA
 
 
144
  SADiff <- ifelse(SADiff > 180, SADiff - 360, SADiff)
145
  SADiff <- ifelse(SADiff < -180, SADiff + 360, SADiff)
 
146
  return(SADiff)
147
  }
148
 
 
149
  scale_TimStuff <- function(raw_score, model_mean, model_sd) {
150
  scaled_score <- (raw_score - model_mean) / model_sd
151
  result <- 50 - (scaled_score * 10)
 
 
 
 
 
 
 
 
 
 
 
152
  return(result)
153
  }
154
+
 
 
 
155
  summary_table <- function(game) {
156
  rows <- nrow(game)
157
+ sumtable <- game %>%
158
  mutate(
159
  VAA = calculate_VAA(vz0, ay,az, vy0, y0),
160
  EAA = calculate_EAA(extension),
161
  SADiff = calculate_SADiff(pfxX, pfxZ,spinDirection)
162
+ ) %>%
163
  mutate(team_fielding_id = ifelse(description == "Called Strike" |
164
  description == "Swinging Strike" |
165
+ description == "Swinging Strike (Blocked)", 1, 0)) %>%
166
  mutate(swing = ifelse(description == "Foul" |
167
  description == "Foul Pitchout" |
168
  description == "In play, no out" |
 
170
  description == "In play, run(s)" |
171
  description == "Swinging Strike" |
172
  description == "swinging Strike (Blocked)" |
173
+ description == "Foul Tip", 1, 0)) %>%
174
+ mutate(is_strike_swinging = ifelse(is_strike_swinging == TRUE, 1, 0)) %>%
175
+ mutate(Pitch = pitch_name) %>%
176
+ rowwise() %>%
177
  mutate(TimStuff = if (phand == 'L') {
178
  case_when(
179
  Pitch %in% c("Four-Seam Fastball", "Sinker", "Cutter", "Fastball") ~
 
212
  -0.004822031, 0.007912765)),
213
  TRUE ~ NA_real_
214
  )
215
+ }) %>%
216
+ group_by(Pitch) %>%
217
  summarize(
218
  Pitches = n(),
219
  'Pitch%' = round(sum(Pitches)/sum(rows) * 100, digits = 1),
 
223
  'IVB' = round(mean(IVB, na.rm = TRUE), digits = 1),
224
  'HB' = round(mean(HB, na.rm = TRUE), digits = 1),
225
  'VAA' = round(mean(VAA, na.rm = TRUE), digits = 1),
 
 
 
 
226
  'CSW%' = round(sum(team_fielding_id, na.rm = TRUE) / sum(!is.na(team_fielding_id)) * 100, digits = 1),
227
  'Whiff%' = round(sum(is_strike_swinging, na.rm = TRUE) / sum(swing, na.rm = TRUE) * 100, digits = 1),
228
  'TimStuff' = round(mean(TimStuff, na.rm = TRUE), digits = 0)
229
+ ) %>%
230
  arrange(-Pitches)
231
 
232
  return(sumtable)
233
  }
234
 
235
+ # Initialize schedule data
 
236
  mlbid <- mlb_schedule(season = 2024, level_ids = "1")
237
+ mlbteamH <- mlbid %>%
238
  select(teams_home_team_name)
239
  mlbteamH <- distinct(mlbteamH)
240
+ mlbteamA <- mlbid %>%
241
  select(teams_away_team_name)
242
  mlbteamA <- distinct(mlbteamA)
243
 
244
  aaaid <- mlb_schedule(season = 2024, level_ids = "11")
245
+ aaateamH <- aaaid %>%
246
  select(teams_home_team_name)
247
  aaateamH <- distinct(aaateamH)
248
+ aaateamA <- aaaid %>%
249
  select(teams_away_team_name)
250
  aaateamA <- distinct(aaateamA)
251
 
252
  fslid <- mlb_schedule(season = 2024, level_ids = "14")
253
+ fslid <- fslid %>%
254
  filter(teams_home_team_name == "Daytona Tortugas" |
255
  teams_home_team_name == "Jupiter Hammerheads" |
256
  teams_home_team_name == "Palm Beach Cardinals" |
 
261
  teams_home_team_name == "Fort Myers Mighty Mussels" |
262
  teams_home_team_name == "Lakeland Flying Tigers" |
263
  teams_home_team_name == "Tampa Tarpons")
264
+ fslteamH <- fslid %>%
265
  select(teams_home_team_name)
266
  fslteamH <- distinct(fslteamH)
267
+ fslteamA <- fslid %>%
268
  select(teams_away_team_name)
269
  fslteamA <- distinct(fslteamA)
270
 
271
  sbid <- mlb_schedule(season = 2024, level_ids = "22")
272
+ sbteamH <- sbid %>%
273
  select(teams_home_team_name)
274
  sbteamH <- distinct(sbteamH)
275
+ sbteamA <- sbid %>%
276
  select(teams_away_team_name)
277
  sbteamA <- distinct(sbteamA)
278
 
279
+ # UI Definition
280
  ui <- fluidPage(
281
  tags$head(
282
  tags$style(HTML("
 
301
  actionButton("update", "Find Pitcher", icon("magnifying-glass"),
302
  style = "color: #FFFFFF; background-color: #0077B6; width: 100%;"),
303
  selectizeInput("pitcher", "Pitcher Name:", c(" ")),
 
304
  textInput("title", "Card Title"),
305
  actionButton("update1", "Make Card", icon("plus"),
306
  style = "color: #FFFFFF; background-color: #0077B6; width: 100%;"),
 
311
  )
312
  )
313
  )
 
314
  server <- function(input, output, session) {
315
  observeEvent(input$level, {
316
  if(input$level == "AAA"){
 
332
  })
333
 
334
  observeEvent(input$update, {
335
+ tryCatch({
336
+ if(input$level == "AAA"){
337
+ pname <- aaaid %>%
338
+ filter(date == input$date) %>%
339
+ filter(teams_home_team_name == input$homeT) %>%
340
+ filter(teams_away_team_name == input$awayT) %>%
341
+ filter(game_number == input$gamenum)
342
+ pname <- pitcher_summary(pname[,6],input$date)
343
+ }
344
+ if(input$level == "FSL"){
345
+ pname <- fslid %>%
346
+ filter(date == input$date) %>%
347
+ filter(teams_home_team_name == input$homeT) %>%
348
+ filter(teams_away_team_name == input$awayT) %>%
349
+ filter(game_number == input$gamenum)
350
+ pname <- pitcher_summary(pname[,6],input$date)
351
+ }
352
+ if(input$level == "MLB"){
353
+ pname <- mlbid %>%
354
+ filter(date == as.character.Date(input$date)) %>%
355
+ filter(teams_home_team_name == input$homeT) %>%
356
+ filter(teams_away_team_name == input$awayT) %>%
357
+ filter(game_number == input$gamenum)
358
+ pname <- pitcher_summary(pname[,6],input$date)
359
+ }
360
+ if(input$level == "College (Statcast Parks Only)"){
361
+ pname <- sbid %>%
362
+ filter(date == input$date) %>%
363
+ filter(teams_home_team_name == input$homeT) %>%
364
+ filter(teams_away_team_name == input$awayT) %>%
365
+ filter(game_number == input$gamenum)
366
+ pname <- pitcher_summary(pname[,6],input$date)
367
+ }
368
+
369
+ if(nrow(pname) == 0) {
370
+ showNotification("No pitchers found for the selected game.", type = "warning")
371
+ } else {
372
+ updateSelectizeInput(session = getDefaultReactiveDomain(),"pitcher","Pitcher:",choices = pname[,3])
373
+ }
374
+ }, error = function(e) {
375
+ showNotification(paste("Error finding pitchers:", e$message), type = "error")
376
+ })
377
  })
378
 
379
  combinedPlot <- reactiveVal()
380
  observeEvent(input$update1, {
381
+ tryCatch({
382
+ if(input$level == "MLB"){
383
+ id <- mlbid %>%
384
+ filter(date == as.character.Date(input$date)) %>%
385
+ filter(teams_home_team_name == input$homeT) %>%
386
+ filter(teams_away_team_name == input$awayT) %>%
387
+ filter(game_number == input$gamenum)
388
+ game <- pitcher_summary(id[,6],input$date)
389
+ game <- game %>%
390
+ filter(`Pitcher Name` == input$pitcher)
391
+ id <- as.character(game[1,4])
392
+ }
393
+ if(input$level == "FSL"){
394
+ id <- fslid %>%
395
+ filter(date == as.character.Date(input$date)) %>%
396
+ filter(teams_home_team_name == input$homeT) %>%
397
+ filter(teams_away_team_name == input$awayT) %>%
398
+ filter(game_number == input$gamenum)
399
+ game <- pitcher_summary(id[,6],input$date)
400
+ game <- game %>%
401
+ filter(`Pitcher Name` == input$pitcher)
402
+ id <- as.character(game[1,4])
403
+ }
404
+ if(input$level == "AAA"){
405
+ id <- aaaid %>%
406
+ filter(date == as.character.Date(input$date)) %>%
407
+ filter(teams_home_team_name == input$homeT) %>%
408
+ filter(teams_away_team_name == input$awayT) %>%
409
+ filter(game_number == input$gamenum)
410
+ game <- pitcher_summary(id[,6],input$date)
411
+ game <- game %>%
412
+ filter(`Pitcher Name` == input$pitcher)
413
+ id <- as.character(game[1,4])
414
+ }
415
+ if(input$level == "College (Statcast Parks Only)"){
416
+ id <- sbid %>%
417
+ filter(date == as.character.Date(input$date)) %>%
418
+ filter(teams_home_team_name == input$homeT) %>%
419
+ filter(teams_away_team_name == input$awayT) %>%
420
+ filter(game_number == input$gamenum)
421
+ game <- pitcher_summary(id[,6],input$date)
422
+ game <- game %>%
423
+ filter(`Pitcher Name` == input$pitcher)
424
+ id <- as.character(game[1,4])
425
+ }
426
+
427
+ if(nrow(game) == 0) {
428
+ showNotification("No data available for the selected pitcher.", type = "warning")
429
+ return()
430
+ }
431
+
432
+ break_plot <- break_plot(game)
433
+ pitch_plot <- pitch_plot(game)
434
+
435
+ table_plot <- tableGrob(summary_table(game), theme = ttheme_default(
436
+ core = list(fg_params = list(cex = 1.7)),
437
+ colhead = list(fg_params = list(cex = 1.2)),
438
+ rowhead = list(fg_params = list(cex = 2.5))))
439
+ table_plot$widths[[1]] <- unit(0, "cm")
440
+
441
+ text_grobs <- which(sapply(table_plot$grobs, function(g) {
442
+ g$name == "core-fg-1" && inherits(g$children[[1]], "text")
443
+ }))
444
+
445
+ for (i in text_grobs) {
446
+ text <- table_plot$grobs[[i]]$children[[1]]$label
447
+ table_plot$grobs[[i]]$children[[1]]$label <- sub("^\\d+\\s*", "", text)
448
+ }
449
+
450
+ grid.newpage()
451
+ grid.draw(table_plot)
452
+
453
+ mlb_url <- paste0("https://midfield.mlbstatic.com/v1/people/", id, "/mlb/300?circle=false")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
  milb_url <- paste0("https://midfield.mlbstatic.com/v1/people/", id, "/milb/300?circle=false")
 
455
 
456
+ img_file <- tryCatch({
457
+ download_and_process_image(mlb_url)
458
+ }, error = function(e) {
459
+ NULL
460
+ })
461
+
462
+ if (is.null(img_file)) {
463
+ img_file <- tryCatch({
464
+ download_and_process_image(milb_url)
465
+ }, error = function(e) {
466
+ NULL
467
+ })
468
+ }
469
+
470
+ if (!is.null(img_file)) {
471
+ img <- readPNG(img_file)
472
+ img_grob <- rasterGrob(img, interpolate = TRUE)
473
  } else {
474
+ img_grob <- textGrob("Image not available", gp = gpar(col = "red", fontsize = 20))
475
  }
476
+
477
+ combined <- grid.arrange(
478
+ arrangeGrob(table_plot, img_grob, ncol = 2, widths = c(2/3, 1/3)),
479
+ arrangeGrob(break_plot, pitch_plot, ncol = 2),
480
+ nrow = 2,
481
+ top = textGrob(paste(input$title, "Made by @TimStats", sep = " "), gp = gpar(fontsize = 20, font = 2))
482
+ )
483
+
484
+ combinedPlot(combined)
485
+
486
+ output$combinedPlot <- renderPlot({
487
+ grid.draw(combinedPlot())
488
+ })
489
+ }, error = function(e) {
490
+ showNotification(paste("Error generating card:", e$message), type = "error")
 
 
 
 
 
 
491
  })
492
  })
493
 
 
501
  )
502
  }
503
 
 
504
  shinyApp(ui, server)