library(shiny) library(dplyr) library(data.table) library(xgboost) library(caret) library(shinythemes) calculate_timstuff <- function(data, pitch_type) { data_matrix <- as.matrix(data) if (pitch_type == "Offspeed") { mod <- xgb.load('Off.model') prediction <- predict(mod, data_matrix) timstuff <- (50 - (prediction - -0.002239657) / 0.01043216 * 10) } else { mod <- xgb.load('FB.model') prediction <- predict(mod, data_matrix) timstuff <- (50 - (prediction - -0.0011801) / 0.007989927 * 10) } return(timstuff) } ui <- fluidPage( theme = shinytheme("flatly"), # Application title titlePanel("TimStuff Fastball/Offspeed Calculator"), # Sidebar with inputs sidebarLayout( sidebarPanel( width = 4, radioButtons("pitch", "Pitch Type", choices = c("Fastball", "Offspeed"), inline = TRUE), radioButtons("phand", "Hand", choices = c("R", "L"), inline = TRUE), numericInput("velo", "Velocity (mph)", value = 90, min = 70, max = 110, step = 1), numericInput("ivb", "Induced Vertical Break (in)", value = 16, min = -20, max = 30, step = 0.1), numericInput("hb", "Horizontal Break (in)", value = 6, min = -20, max = 20, step = 0.1), numericInput("spinrate", "Spin Rate (rpm)", value = 2300, min = 1000, max = 3500, step = 10), numericInput("ext", "Extension (ft)", value = 6, min = 5, max = 8, step = 0.1), numericInput("spinaxisdiff", "Spin Axis Difference (°)", value = 0, min = -180, max = 180, step = 1), numericInput("x0", "Horizontal Release Point (ft)", value = 0, min = -5, max = 5, step = 0.1), numericInput("z0", "Vertical Release Point (ft)", value = 5, min = 3, max = 8, step = 0.1), selectInput("feature_to_vary", "Select Feature to Vary:", choices = c("Velocity" = "start_speed", "Induced Vertical Break" = "IVB", "Horizontal Break" = "HB", "Spin Rate" = "spin_rate", "Extension" = "EAA", "Spin Axis Difference" = "SADiff", "Horizontal Release Point" = "x0", "Vertical Release Point" = "z0")) ), # Main panel with output mainPanel( width = 8, wellPanel( h3("Results"), verbatimTextOutput("stuff"), plotOutput("feature_impact", height = "400px") # Placeholder for future visualization ) ) ) ) server <- function(input, output) { observeEvent(input$pitch, { output$stuff <- renderText({ data <- data.frame( start_speed = input$velo, IVB = input$ivb, HB = ifelse(input$phand == "L", -input$hb, input$hb), EAA = input$ext / 6.3, x0 = ifelse(input$phand == "L", -input$x0, input$x0), z0 = input$z0, spin_rate = input$spinrate, SADiff = input$spinaxisdiff ) data <- as.matrix(data) if (input$pitch == "Offspeed") { mod <- xgb.load('Off.model') prediction <- predict(mod, data) TimStuff <- (50 - (prediction - -0.002239657) / 0.01043216 * 10) } else { mod <- xgb.load('FB.model') prediction <- predict(mod, data) TimStuff <- (50 - (prediction - -0.0011801) / 0.007989927 * 10) } paste("TimStuff:", round(TimStuff, 2), "\n20-80 Scale, 50 is Average, 1 SD is 10") }) # Add this new reactive expression # Add this new reactive expression feature_range <- reactive({ ranges <- list( start_speed = c(70, 110), IVB = c(-20, 30), HB = c(-20, 20), spin_rate = c(1000, 3500), EAA = c(5/6.3, 8/6.3), SADiff = c(-180, 180), x0 = c(-5, 5), z0 = c(3, 8) ) ranges[[input$feature_to_vary]] }) # Update the feature_impact plot output$feature_impact <- renderPlot({ # Create base data frame with current inputs base_data <- data.frame( start_speed = input$velo, IVB = input$ivb, HB = ifelse(input$phand == "L", -input$hb, input$hb), EAA = input$ext / 6.3, x0 = ifelse(input$phand == "L", -input$x0, input$x0), z0 = input$z0, spin_rate = input$spinrate, SADiff = input$spinaxisdiff ) # Generate sequence for the selected feature feature_seq <- seq(feature_range()[1], feature_range()[2], length.out = 100) # Calculate TimStuff for the sequence varied_data <- do.call(rbind, replicate(100, base_data, simplify = FALSE)) varied_data[[input$feature_to_vary]] <- feature_seq timstuff <- calculate_timstuff(varied_data, input$pitch) # Create plot data plot_data <- data.frame( Feature = names(base_data)[which(names(base_data) == input$feature_to_vary)], Value = feature_seq, TimStuff = timstuff ) # Create plot ggplot(plot_data, aes(x = Value, y = TimStuff)) + geom_line(color = "blue", size = 1) + geom_point(aes(x = base_data[[input$feature_to_vary]], y = calculate_timstuff(base_data, input$pitch)), color = "red", size = 3) + theme_minimal() + ylim(0,100) + labs(title = paste("Impact of", input$feature_to_vary, "on TimStuff"), x = input$feature_to_vary, y = "TimStuff") + theme(text = element_text(size = 14)) }) }) } # Run the application shinyApp(ui = ui, server = server)