SummaryCard / app.R
TimStats's picture
Update app.R
3322f58 verified
Raw
History Blame
19.6 kB
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))
}
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)
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)
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)
sbid <- mlb_schedule(season = 2024, level_ids = "22")
sbteamH <- sbid |>
select(teams_home_team_name)
sbteamH <- distinct(sbteamH)
sbteamA <- sbid |>
select(teams_away_team_name)
sbteamA <- distinct(sbteamA)
ui <- fluidPage(
tags$head(
tags$style(HTML("
@media (max-width: 768px) {
.sidebar { width: 100%; float: none; }
.main-content { margin-left: 0; }
.selectize-input { font-size: 14px; }
.form-group { margin-bottom: 10px; }
.action-button { width: 100%; }
.grid-card { width: 100% !important; }
}
"))
),
titlePanel("2024 MLB/AAA/FSL Summary Cards"),
sidebarLayout(
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")),
selectizeInput("pitcher", "Pitcher Name:", 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: 100%;")
),
mainPanel(
fluidRow(
column(12, textOutput(outputId = "cardTitle"))
),
fluidRow(
column(6, plotlyOutput(outputId = "breakgraph")),
column(6, plotlyOutput(outputId = "strikezone"))
),
fluidRow(
column(12, DTOutput(outputId = "myTable"))
),
fluidRow(
column(12, imageOutput(outputId = "picture"))
)
)
)
)
server <- function(input, output, session) {
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$awayT, {
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$homeT, {
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$gamenum, {
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 <- renderPlotly({
ggplotly(break_plot(game))
})
output$strikezone <- renderPlotly({
ggplotly(pitch_plot(game))
})
output$myTable <- renderDT({
t <- summary_table(game)
datatable(t, options = list(scrollX = TRUE))
})
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({
paste(input$title, "Made by @TimStats", sep = " ")
})
})
}
shinyApp(ui, server)