Spaces:
Sleeping
Sleeping
Update app.R
Browse files
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 |
-
#
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
#
|
| 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 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 382 |
})
|
| 383 |
|
| 384 |
combinedPlot <- reactiveVal()
|
| 385 |
observeEvent(input$update1, {
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 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 |
-
|
| 483 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 484 |
} else {
|
| 485 |
-
|
| 486 |
}
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 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)
|