Spaces:
Sleeping
Sleeping
| library(shiny) | |
| library(plotly) | |
| library(gridlayout) | |
| library(bslib) | |
| library(DT) | |
| library(rsconnect) | |
| library(baseballr) | |
| library(dplyr) | |
| library(tidyverse) | |
| library(rvest) | |
| library(ggplot2) | |
| library(janitor) | |
| library(ggthemes) | |
| library(ggpubr) | |
| library(jsonlite) | |
| library(utils) | |
| Sys.setenv(TZ='EST') | |
| is_barrel <- function(df) { | |
| df$barrel <- with(df, ifelse(hit_angle <= 50 & hit_speed >= 97 & hit_speed * 1.5 - | |
| hit_angle >= 117 & hit_speed + hit_angle >= 123, 1, 0)) | |
| return(df) | |
| } | |
| VAA <- function(milbtotal){ | |
| milbtotal <- milbtotal |> | |
| mutate(VAA = -atan((vz0+(az*(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))-vy0)/ | |
| ay))/(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))))*(180/pi)) | |
| } | |
| pitcher_summary <- function(game_pk,date){ | |
| gdate <- as.Date.character(date) | |
| gdate <- as.Date(gdate) | |
| tmilb <- mlb_pbp(game_pk) | |
| tmilb <- tmilb |> | |
| filter(type == "pitch") | |
| tmilb <- tmilb |> | |
| select(matchup.batter.fullName,matchup.batter.id,matchup.pitcher.fullName, | |
| matchup.pitcher.id,result.event,details.description,details.type.description, | |
| result.description,pitchData.startSpeed, | |
| pitchData.plateTime,pitchData.zone,pitchData.breaks.spinRate,pitchData.extension, | |
| pitchData.coordinates.pX,pitchData.coordinates.pZ,pitchData.coordinates.x0, | |
| pitchData.coordinates.y0,pitchData.coordinates.z0,pitchData.coordinates.aX, | |
| pitchData.coordinates.aY,pitchData.coordinates.aZ,pitchData.coordinates.vX0, | |
| pitchData.coordinates.vZ0,pitchData.coordinates.vY0,pitchData.coordinates.pfxX, | |
| pitchData.coordinates.pfxZ,pitchData.breaks.breakVerticalInduced,pitchData.breaks.breakHorizontal, | |
| hitData.launchSpeed,hitData.launchAngle,hitData.totalDistance,details.isInPlay,last.pitch.of.ab) | |
| colnames(tmilb) <- c("Batter Name","Batter ID","Pitcher Name","Pitcher ID", | |
| "result","description","pitch_name", | |
| "des","start_speed","plateTime","zone","spin_rate", | |
| "extension","px","pz","x0","y0","z0","ax","ay","az", | |
| "vx0","vz0","vy0","pfxX","pfxZ","IVB","HB","hit_speed", | |
| "hit_angle","hit_distance","inPlay","lastPitch") | |
| tmilb <- is_barrel(tmilb) | |
| tmilb <- tmilb |> | |
| mutate(is_strike_swinging = ifelse(description == "Swinging Strike" | | |
| description == "Foul Tip",TRUE,FALSE)) | |
| tmilb <- tmilb |> | |
| mutate(date = gdate) | |
| return(tmilb) | |
| } | |
| break_plot <- function(game){ | |
| ggplot()+ | |
| geom_point(game,mapping = | |
| aes(x=HB, y = IVB,color = pitch_name),size = 4) + | |
| geom_vline(xintercept = 0, color = "lightblue",linewidth = 1,linetype = 4)+ | |
| geom_hline(yintercept = 0, color = "lightblue",linewidth = 1,linetype = 4)+ | |
| xlab("Horizontal Break from Pitcher's Perspective (in) ")+ | |
| ylab("Induced Verical Break (in)") + | |
| xlim(-25,25) + | |
| ylim(-25,25) + | |
| ggthemes::theme_igray()+ | |
| theme(panel.grid.minor = element_line(color = "gray", | |
| size = 0.25, | |
| linetype = 1))+ | |
| guides(color = guide_legend(title = "Pitch Type")) | |
| } | |
| pitch_plot <- function(game){ | |
| ggplot()+ | |
| geom_point(game,mapping = | |
| aes(x=px, y = pz,color = pitch_name),size = 3.5) + | |
| geom_segment(aes(x=-0.71,xend = 0.71, y = 1.5,yend = 1.5))+ | |
| geom_segment(aes(x=-0.71,xend = 0.71, y = 3.6,yend = 3.6))+ | |
| geom_segment(aes(x= 0.71,xend = 0.71, y = 1.5,yend = 3.6))+ | |
| geom_segment(aes(x=-0.71,xend = -0.71, y = 1.5,yend =3.6))+ | |
| xlab(" ")+ | |
| ylab(" ") + | |
| xlim(-3,3)+ | |
| ylim(0.2,4)+ | |
| ggthemes::theme_few()+ | |
| guides(color = guide_legend(title = "Pitch Type"))+ | |
| theme(axis.text.x=element_blank(), | |
| axis.ticks.x=element_blank(), | |
| axis.text.y=element_blank(), | |
| axis.ticks.y=element_blank()) | |
| } | |
| summary_table <- function(game){ | |
| rows <- nrow(game) | |
| sumtable <- game |> | |
| mutate(VAA = -atan((vz0+(az*(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))-vy0)/ | |
| ay))/(-sqrt((vy0*vy0)-(2*ay*(y0-(17/12))))))*(180/pi)) |> | |
| mutate(team_fielding_id = ifelse(description == "Called Strike" | | |
| description == "Swinging Strike" | | |
| description == "Swinging Strike (Blocked)",1,0))|> | |
| mutate(swing = ifelse(description == "Foul" | | |
| description == "Foul Pitchout" | | |
| description == "In play, no out" | | |
| description == "In play, out(s)" | | |
| description == "In play, run(s)" | | |
| description == "Swinging Strike" | | |
| description == "swinging Strike (Blocked)" | | |
| description == "Foul Tip",1,0)) |> | |
| mutate(is_strike_swinging = ifelse(is_strike_swinging == TRUE,1,0)) |> | |
| mutate(Pitch = pitch_name) |> | |
| group_by(Pitch)|> | |
| summarize(Pitches = n(), | |
| 'Pitch%' = round(sum(Pitches)/sum(rows) * 100,digits = 1), | |
| 'Avg. Velo' = round(sum(start_speed)/Pitches,digits = 1), | |
| 'Spin Rate' = round(sum(spin_rate)/ Pitches,digits = 1), | |
| 'Extension' = mean(extension,na.rm = TRUE), | |
| 'IVB' = mean(IVB,na.rm=TRUE), | |
| 'HB' = mean(HB,na.rm = TRUE), | |
| 'VAA' = mean(VAA,na.rm= TRUE), | |
| 'CSW%' = round(sum(team_fielding_id)/ | |
| sum(!is.na(team_fielding_id))*100,digits = 1), | |
| 'Whiff%' = round(sum(is_strike_swinging)/ | |
| sum(swing)*100,digits = 1)) | |
| } | |
| #MLB ID Stuff | |
| mlbid <- mlb_schedule(season = 2024, level_ids = "1") | |
| mlbteamH <- mlbid |> | |
| select(teams_home_team_name) | |
| mlbteamH <- distinct(mlbteamH) | |
| mlbteamA <- mlbid |> | |
| select(teams_away_team_name) | |
| mlbteamA <- distinct(mlbteamA) | |
| #AAA ID Stuff | |
| aaaid <- mlb_schedule(season = 2024, level_ids = "11") | |
| aaateamH <- aaaid |> | |
| select(teams_home_team_name) | |
| aaateamH <- distinct(aaateamH) | |
| aaateamA <- aaaid |> | |
| select(teams_away_team_name) | |
| aaateamA <- distinct(aaateamA) | |
| #FSL ID Stuff | |
| fslid <- mlb_schedule(season = 2024, level_ids = "14") | |
| fslid <- fslid |> | |
| filter(teams_home_team_name == "Daytona Tortugas" | | |
| teams_home_team_name == "Jupiter Hammerheads" | | |
| teams_home_team_name == "Palm Beach Cardinals" | | |
| teams_home_team_name == "St. Lucie Mets" | | |
| teams_home_team_name == "Bradenton Marauders" | | |
| teams_home_team_name == "Clearwater Threshers" | | |
| teams_home_team_name == "Dunedin Blue Jays" | | |
| teams_home_team_name == "Fort Myers Mighty Mussels" | | |
| teams_home_team_name == "Lakeland Flying Tigers" | | |
| teams_home_team_name == "Tampa Tarpons") | |
| fslteamH <- fslid |> | |
| select(teams_home_team_name) | |
| fslteamH <- distinct(fslteamH) | |
| fslteamA <- fslid |> | |
| select(teams_away_team_name) | |
| fslteamA <- distinct(fslteamA) | |
| #SB ID Stuff | |
| sbid <- mlb_schedule(season = 2024, level_ids = "21") | |
| sbteamH <- sbid |> | |
| select(teams_home_team_name) | |
| sbteamH <- distinct(sbteamH) | |
| sbteamA <- sbid |> | |
| select(teams_away_team_name) | |
| sbteamA <- distinct(sbteamA) | |
| ui <- grid_page( | |
| layout = c( | |
| "header header", | |
| "sidebar area2 " | |
| ), | |
| row_sizes = c( | |
| "40px", | |
| "1fr" | |
| ), | |
| col_sizes = c( | |
| "280px", | |
| "1fr" | |
| ), | |
| gap_size = "1rem", | |
| sidebarPanel( | |
| dateInput("date", | |
| "Date:"), | |
| selectizeInput("level","Level:",c("MLB","AAA","FSL","Spring Breakout")), | |
| selectizeInput("homeT","Home Team:",mlbteamH[,1]), | |
| selectizeInput("awayT", "Away Team:",mlbteamA[,1]), | |
| selectizeInput("gamenum","Game Number (For Doubleheaders):",c("1","2")), | |
| actionButton("update","Find Pitchers",icon("magnifying-glass"), | |
| style = "color: #FFFFFF; background-color: #0077B6"), | |
| selectizeInput("pitcher","Pitcher (For Spring Breakout if pitchers not loading, try changing game number)",c(" ")), | |
| selectizeInput("league","MLB or MiLB Photo",c("MLB","MiLB")), | |
| textInput("title","Card Title"), | |
| actionButton("update1","Make Card",icon("plus"), | |
| style = "color: #FFFFFF; background-color: #0077B6"), | |
| width = 15, | |
| ), | |
| grid_card_text( | |
| area = "header", | |
| content = "2024 MLB/AAA/FSL Summary Cards (BETA: Meant for Computers, Set Zoom to 100% for best use)", | |
| alignment = "start", | |
| is_title = FALSE | |
| ), | |
| grid_card( | |
| area = "area2", | |
| full_screen = TRUE, | |
| card_header(textOutput(outputId = "cardTitle")), | |
| card_body( | |
| gap = "0px", | |
| grid_container( | |
| layout = c( | |
| "area3 chart chart chart ", | |
| "breakgraph breakgraph strikezone strikezone" | |
| ), | |
| row_sizes = c( | |
| "1fr", | |
| "1fr" | |
| ), | |
| col_sizes = c( | |
| "1fr", | |
| "1fr", | |
| "1fr", | |
| "1fr" | |
| ), | |
| gap_size = "0px", | |
| grid_card_plot(area = "breakgraph"), | |
| grid_card_plot(area = "strikezone"), | |
| grid_card( | |
| area = "chart", | |
| card_body( | |
| tableOutput( | |
| outputId = "myTable" | |
| ), | |
| ) | |
| ), | |
| grid_card( | |
| area = "area3", | |
| plotOutput(outputId = "picture") | |
| ) | |
| ) | |
| ) | |
| ) | |
| ) | |
| server <- function(input, output) { | |
| # game <- reactive(pitcher_summary(input$game_pk)) | |
| # id <- reactive(as.character(game()[1,4])) | |
| observeEvent(input$level,{ | |
| if(input$level == "AAA"){ | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"homeT","Home Team:",choices = aaateamH[,1]) | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"awayT","Away Team:",choices = aaateamA[,1]) | |
| } | |
| if(input$level == "FSL"){ | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"homeT","Home Team:",choices = fslteamH[,1]) | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"awayT","Away Team:",choices = fslteamA[,1]) | |
| } | |
| if(input$level == "MLB"){ | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"homeT","Home Team:",choices = mlbteamH[,1]) | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"awayT","Away Team:",choices = mlbteamA[,1]) | |
| } | |
| if(input$level == "Spring Breakout"){ | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"homeT","Home Team:",choices = sbteamH[,1]) | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"awayT","Away Team:",choices = sbteamA[,1]) | |
| } | |
| }) | |
| observeEvent(input$update,{ | |
| if(input$level == "AAA"){ | |
| pname <- aaaid |> | |
| filter(date == input$date) |> | |
| filter(teams_home_team_name == input$homeT) |> | |
| filter(teams_away_team_name == input$awayT) |> | |
| filter(game_number == input$gamenum) | |
| pname <- tryCatch({pitcher_summary(pname[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")}) | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"pitcher","Pitcher:(For Spring Breakout if pitchers not loading, try changing game number)",choices = pname[,3]) | |
| } | |
| if(input$level == "FSL"){ | |
| pname <- fslid |> | |
| filter(date == input$date) |> | |
| filter(teams_home_team_name == input$homeT) |> | |
| filter(teams_away_team_name == input$awayT) |> | |
| filter(game_number == input$gamenum) | |
| pname <- tryCatch({pitcher_summary(pname[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")}) | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"pitcher","Pitcher:(For Spring Breakout if pitchers not loading, try changing game number)",choices = pname[,3]) | |
| } | |
| if(input$level == "MLB"){ | |
| pname <- mlbid |> | |
| filter(date == as.character.Date(input$date)) |> | |
| filter(teams_home_team_name == input$homeT) |> | |
| filter(teams_away_team_name == input$awayT) |> | |
| filter(game_number == input$gamenum) | |
| pname <- tryCatch({pitcher_summary(pname[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")}) | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"pitcher","Pitcher:(For Spring Breakout if pitchers not loading, try changing game number)",choices = pname[,3]) | |
| } | |
| if(input$level == "Spring Breakout"){ | |
| pname <- sbid |> | |
| filter(date == input$date) |> | |
| filter(teams_home_team_name == input$homeT) |> | |
| filter(teams_away_team_name == input$awayT) |> | |
| filter(game_number == input$gamenum) | |
| pname <- tryCatch({pitcher_summary(pname[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")}) | |
| updateSelectizeInput(session = getDefaultReactiveDomain(),"pitcher","Pitcher:(For Spring Breakout if pitchers not loading, try changing game number)",choices = pname[,3]) | |
| } | |
| }) | |
| observeEvent(input$update1,{ | |
| if(input$level == "MLB"){ | |
| id <- mlbid |> | |
| filter(date == as.character.Date(input$date)) |> | |
| filter(teams_home_team_name == input$homeT) |> | |
| filter(teams_away_team_name == input$awayT) |> | |
| filter(game_number == input$gamenum) | |
| game <- tryCatch({pitcher_summary(id[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")}) | |
| game <- game |> | |
| filter(`Pitcher Name` == input$pitcher) | |
| id <- as.character(game[1,4]) | |
| } | |
| if(input$level == "FSL"){ | |
| id <- fslid |> | |
| filter(date == as.character.Date(input$date)) |> | |
| filter(teams_home_team_name == input$homeT) |> | |
| filter(teams_away_team_name == input$awayT) |> | |
| filter(game_number == input$gamenum) | |
| game <- tryCatch({pitcher_summary(id[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")}) | |
| game <- game |> | |
| filter(`Pitcher Name` == input$pitcher) | |
| id <- as.character(game[1,4]) | |
| } | |
| if(input$level == "AAA"){ | |
| id <- aaaid |> | |
| filter(date == as.character.Date(input$date)) |> | |
| filter(teams_home_team_name == input$homeT) |> | |
| filter(teams_away_team_name == input$awayT) |> | |
| filter(game_number == input$gamenum) | |
| game <- tryCatch({pitcher_summary(id[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")}) | |
| game <- game |> | |
| filter(`Pitcher Name` == input$pitcher) | |
| id <- as.character(game[1,4]) | |
| } | |
| if(input$level == "Spring Breakout"){ | |
| id <- sbid |> | |
| filter(date == as.character.Date(input$date)) |> | |
| filter(teams_home_team_name == input$homeT) |> | |
| filter(teams_away_team_name == input$awayT) |> | |
| filter(game_number == input$gamenum) | |
| game <- tryCatch({pitcher_summary(id[,6],input$date)},error=function(e){cat("ERROR :",conditionMessage(e), "\n")}) | |
| game <- game |> | |
| filter(`Pitcher Name` == input$pitcher) | |
| id <- as.character(game[1,4]) | |
| } | |
| output$breakgraph <- renderPlot({ | |
| break_plot(game) | |
| }) | |
| output$strikezone <- renderPlot({ | |
| pitch_plot(game) | |
| }) | |
| output$myTable <- renderTable({ | |
| t <- summary_table(game) | |
| return(t) | |
| },width = '1150px',digits = 1,na = "NA",align = 'left') | |
| output$picture <- renderImage({ | |
| if(input$league == "MLB"){ | |
| y <- paste("https://midfield.mlbstatic.com/v1/people/","/mlb/300?circle=false",sep = id) | |
| } | |
| if(input$league == "MiLB"){ | |
| y <- paste("https://midfield.mlbstatic.com/v1/people/","/milb/300?circle=false",sep = id) | |
| } | |
| photo <- tempfile() | |
| download.file(y,photo,mode = 'wb') | |
| list( | |
| src = photo | |
| ) | |
| },deleteFile = TRUE) | |
| output$cardTitle <- renderText( | |
| title <- paste(input$title,"Made by @TimStats", sep = " ") | |
| ) | |
| }) | |
| } | |
| shinyApp(ui, server) |