igroffman commited on
Commit
7eba80e
Β·
verified Β·
1 Parent(s): 696fb38

Update app.R

Browse files
Files changed (1) hide show
  1. app.R +132 -134
app.R CHANGED
@@ -498,46 +498,47 @@ server <- function(input, output, session) {
498
  })
499
 
500
  # Processing summary
501
- output$process_summary <- renderText({
502
- if (is.null(input$file)) {
503
- return("No file uploaded yet.")
504
- }
505
-
506
- if (is.null(processed_data())) {
507
- return("Processing...")
508
- }
509
-
510
- df <- processed_data()
511
- original_df <- read.csv(input$file$datapath, nrows = 1)
512
- selected_cols_to_remove <- input$columns_to_remove %||% character(0)
513
- removed_cols <- intersect(selected_cols_to_remove, names(original_df))
514
-
515
- # Build the removed columns text
516
- removed_cols_text <- if (length(removed_cols) > 0) {
517
- cols_display <- if (length(removed_cols) > 5) {
518
- paste(paste(head(removed_cols, 5), collapse = ", "), "...")
 
 
 
 
519
  } else {
520
- paste(removed_cols, collapse = ", ")
521
  }
522
- paste("βœ“ Removed columns:", length(removed_cols), "\n -", cols_display)
523
- } else {
524
- "βœ“ Removed columns: 0"
525
- }
526
-
527
- summary_text <- paste(
528
- "βœ“ File processed successfully!",
529
- paste("βœ“ Original columns:", ncol(original_df)),
530
- paste("βœ“ Final columns:", ncol(df)),
531
- paste("βœ“ Target columns: 167"),
532
- paste("βœ“ Rows processed:", nrow(df)),
533
- removed_cols_text,
534
- "βœ“ Duplicates removed",
535
- paste("βœ“ Ready for further processing"),
536
- sep = "\n"
537
- )
538
 
539
- return(summary_text)
540
- })
541
  # Preview table
542
  output$preview <- DT::renderDataTable({
543
  req(processed_data())
@@ -635,6 +636,10 @@ output$process_summary <- renderText({
635
  if (min(distances) <= 2) {
636
  clicked_pitch <- pitcher_data[closest_idx, ]
637
 
 
 
 
 
638
  # Store the original row index in the full dataset
639
  full_data <- plot_data() %>% filter(Pitcher == input$pitcher_select)
640
  original_row <- which(full_data$HorzBreak == clicked_pitch$HorzBreak &
@@ -654,15 +659,6 @@ output$process_summary <- renderText({
654
 
655
  # Show modal
656
  toggleModal(session, "pitchEditModal", toggle = "open")
657
-
658
-
659
- if (min(distances) <= 2) {
660
- clicked_pitch <- pitcher_data[closest_idx, ]
661
-
662
- # DEBUG: Print to console
663
- print("Pitch selected!")
664
- print(clicked_pitch$TaggedPitchType)
665
-
666
  }
667
  })
668
 
@@ -781,97 +777,99 @@ output$process_summary <- renderText({
781
  }
782
  })
783
 
784
- output$movement_stats <- DT::renderDataTable({
785
- req(plot_data(), input$pitcher_select)
786
-
787
- data <- plot_data()
788
-
789
- movement_stats <- data %>%
790
- filter(Pitcher == input$pitcher_select) %>%
791
- filter(!is.na(HorzBreak), !is.na(InducedVertBreak), !is.na(TaggedPitchType)) %>%
792
- mutate(
793
- pitch_group = case_when(
794
- TaggedPitchType %in% c("Fastball", "FourSeamFastBall", "FourSeamFastB", "Four-Seam", "4-Seam") ~ "Fastball",
795
- TaggedPitchType %in% c("OneSeamFastBall", "TwoSeamFastBall", "Sinker", "Two-Seam", "One-Seam") ~ "Sinker",
796
- TaggedPitchType %in% c("ChangeUp", "Changeup") ~ "Changeup",
797
- TRUE ~ TaggedPitchType
798
- ),
799
- # Create necessary indicator variables if they don't exist
800
- in_zone = ifelse("StrikeZoneIndicator" %in% names(.), StrikeZoneIndicator,
801
- ifelse(!is.na(PlateLocSide) & !is.na(PlateLocHeight) &
802
- PlateLocSide >= -0.95 & PlateLocSide <= 0.95 &
803
- PlateLocHeight >= 1.6 & PlateLocHeight <= 3.5, 1, 0)),
804
- is_whiff = ifelse("WhiffIndicator" %in% names(.), WhiffIndicator,
805
- ifelse(!is.na(PitchCall) & PitchCall == "StrikeSwinging", 1, 0)),
806
- chase = ifelse("Chaseindicator" %in% names(.), Chaseindicator,
807
- ifelse(!is.na(PitchCall) & !is.na(PlateLocSide) & !is.na(PlateLocHeight) &
808
- PitchCall %in% c("StrikeSwinging", "FoulBallNotFieldable", "FoulBall", "InPlay") &
809
- (PlateLocSide < -0.95 | PlateLocSide > 0.95 | PlateLocHeight < 1.6 | PlateLocHeight > 3.5), 1, 0))
810
- )
811
-
812
- # Calculate total pitches for usage percentage
813
- total_pitches <- nrow(movement_stats)
814
-
815
- summary_stats <- movement_stats %>%
816
- group_by(`Pitch Type` = pitch_group) %>%
817
- summarise(
818
- Count = n(),
819
- `Usage%` = sprintf("%.1f%%", (n() / total_pitches) * 100),
820
- `Ext.` = ifelse("Extension" %in% names(movement_stats),
821
- sprintf("%.1f", mean(Extension, na.rm = TRUE)),
822
- "β€”"),
823
- `Avg Velo` = sprintf("%.1f mph", mean(RelSpeed, na.rm = TRUE)),
824
- `90th Velo` = sprintf("%.1f mph", quantile(RelSpeed, 0.9, na.rm = TRUE)),
825
- `Max Velo` = sprintf("%.1f mph", max(RelSpeed, na.rm = TRUE)),
826
- `Avg IVB` = sprintf("%.1f in", mean(InducedVertBreak, na.rm = TRUE)),
827
- `Avg HB` = sprintf("%.1f in", mean(HorzBreak, na.rm = TRUE)),
828
- `Avg Spin` = ifelse("SpinRate" %in% names(movement_stats),
829
- sprintf("%.0f rpm", mean(SpinRate, na.rm = TRUE)),
830
- "β€”"),
831
- `Rel Height` = ifelse("RelHeight" %in% names(movement_stats),
832
- sprintf("%.1f", mean(RelHeight, na.rm = TRUE)),
833
- "β€”"),
834
- `Zone%` = sprintf("%.1f%%", round(mean(in_zone, na.rm = TRUE) * 100, 1)),
835
- `Whiff%` = sprintf("%.1f%%", round(mean(is_whiff, na.rm = TRUE) * 100, 1)),
836
- `Chase%` = sprintf("%.1f%%", round(mean(chase, na.rm = TRUE) * 100, 1)),
837
- .groups = "drop"
838
- ) %>%
839
- arrange(desc(Count))
840
-
841
- DT::datatable(summary_stats,
842
- options = list(pageLength = 15, dom = 't', scrollX = TRUE),
843
- rownames = FALSE) %>%
844
- DT::formatStyle(columns = names(summary_stats), fontSize = '12px')
845
- })
846
-
847
- output$selected_pitch_info <- renderText({
848
- pitch_info <- selected_pitch()
849
- if (!is.null(pitch_info)) {
850
- pitch_data <- pitch_info$data
851
-
852
- # Build info text with only available fields
853
- info_lines <- c(
854
- paste("Pitcher:", pitch_info$pitcher),
855
- paste("Current Type:", pitch_data$TaggedPitchType),
856
- paste("Velocity:", round(pitch_data$RelSpeed, 1), "mph"),
857
- paste("Horizontal Break:", round(pitch_data$HorzBreak, 1), "inches"),
858
- paste("Induced Vertical Break:", round(pitch_data$InducedVertBreak, 1), "inches")
859
- )
860
 
861
- # Add optional fields only if they exist and have values
862
- if ("SpinRate" %in% names(pitch_data) && !is.na(pitch_data$SpinRate)) {
863
- info_lines <- c(info_lines, paste("Spin Rate:", round(pitch_data$SpinRate, 0), "rpm"))
864
- }
865
 
866
- if ("Date" %in% names(pitch_data) && !is.na(pitch_data$Date)) {
867
- info_lines <- c(info_lines, paste("Date:", pitch_data$Date))
868
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
869
 
870
- return(paste(info_lines, collapse = "\n"))
871
- } else {
872
- return("No pitch selected")
873
- }
874
- })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
875
 
876
  # Update pitch type
877
  observeEvent(input$update_pitch, {
 
498
  })
499
 
500
  # Processing summary
501
+ output$process_summary <- renderText({
502
+ if (is.null(input$file)) {
503
+ return("No file uploaded yet.")
504
+ }
505
+
506
+ if (is.null(processed_data())) {
507
+ return("Processing...")
508
+ }
509
+
510
+ df <- processed_data()
511
+ original_df <- read.csv(input$file$datapath, nrows = 1)
512
+ selected_cols_to_remove <- input$columns_to_remove %||% character(0)
513
+ removed_cols <- intersect(selected_cols_to_remove, names(original_df))
514
+
515
+ # Build the removed columns text
516
+ removed_cols_text <- if (length(removed_cols) > 0) {
517
+ cols_display <- if (length(removed_cols) > 5) {
518
+ paste(paste(head(removed_cols, 5), collapse = ", "), "...")
519
+ } else {
520
+ paste(removed_cols, collapse = ", ")
521
+ }
522
+ paste("βœ“ Removed columns:", length(removed_cols), "\n -", cols_display)
523
  } else {
524
+ "βœ“ Removed columns: 0"
525
  }
526
+
527
+ summary_text <- paste(
528
+ "βœ“ File processed successfully!",
529
+ paste("βœ“ Original columns:", ncol(original_df)),
530
+ paste("βœ“ Final columns:", ncol(df)),
531
+ paste("βœ“ Target columns: 167"),
532
+ paste("βœ“ Rows processed:", nrow(df)),
533
+ removed_cols_text,
534
+ "βœ“ Duplicates removed",
535
+ paste("βœ“ Ready for further processing"),
536
+ sep = "\n"
537
+ )
538
+
539
+ return(summary_text)
540
+ })
 
541
 
 
 
542
  # Preview table
543
  output$preview <- DT::renderDataTable({
544
  req(processed_data())
 
636
  if (min(distances) <= 2) {
637
  clicked_pitch <- pitcher_data[closest_idx, ]
638
 
639
+ # DEBUG: Print to console
640
+ print("Pitch selected!")
641
+ print(paste("Type:", clicked_pitch$TaggedPitchType))
642
+
643
  # Store the original row index in the full dataset
644
  full_data <- plot_data() %>% filter(Pitcher == input$pitcher_select)
645
  original_row <- which(full_data$HorzBreak == clicked_pitch$HorzBreak &
 
659
 
660
  # Show modal
661
  toggleModal(session, "pitchEditModal", toggle = "open")
 
 
 
 
 
 
 
 
 
662
  }
663
  })
664
 
 
777
  }
778
  })
779
 
780
+ # Movement stats table
781
+ output$movement_stats <- DT::renderDataTable({
782
+ req(plot_data(), input$pitcher_select)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
783
 
784
+ data <- plot_data()
 
 
 
785
 
786
+ movement_stats <- data %>%
787
+ filter(Pitcher == input$pitcher_select) %>%
788
+ filter(!is.na(HorzBreak), !is.na(InducedVertBreak), !is.na(TaggedPitchType)) %>%
789
+ mutate(
790
+ pitch_group = case_when(
791
+ TaggedPitchType %in% c("Fastball", "FourSeamFastBall", "FourSeamFastB", "Four-Seam", "4-Seam") ~ "Fastball",
792
+ TaggedPitchType %in% c("OneSeamFastBall", "TwoSeamFastBall", "Sinker", "Two-Seam", "One-Seam") ~ "Sinker",
793
+ TaggedPitchType %in% c("ChangeUp", "Changeup") ~ "Changeup",
794
+ TRUE ~ TaggedPitchType
795
+ ),
796
+ # Create necessary indicator variables if they don't exist
797
+ in_zone = ifelse("StrikeZoneIndicator" %in% names(.), StrikeZoneIndicator,
798
+ ifelse(!is.na(PlateLocSide) & !is.na(PlateLocHeight) &
799
+ PlateLocSide >= -0.95 & PlateLocSide <= 0.95 &
800
+ PlateLocHeight >= 1.6 & PlateLocHeight <= 3.5, 1, 0)),
801
+ is_whiff = ifelse("WhiffIndicator" %in% names(.), WhiffIndicator,
802
+ ifelse(!is.na(PitchCall) & PitchCall == "StrikeSwinging", 1, 0)),
803
+ chase = ifelse("Chaseindicator" %in% names(.), Chaseindicator,
804
+ ifelse(!is.na(PitchCall) & !is.na(PlateLocSide) & !is.na(PlateLocHeight) &
805
+ PitchCall %in% c("StrikeSwinging", "FoulBallNotFieldable", "FoulBall", "InPlay") &
806
+ (PlateLocSide < -0.95 | PlateLocSide > 0.95 | PlateLocHeight < 1.6 | PlateLocHeight > 3.5), 1, 0))
807
+ )
808
 
809
+ # Calculate total pitches for usage percentage
810
+ total_pitches <- nrow(movement_stats)
811
+
812
+ summary_stats <- movement_stats %>%
813
+ group_by(`Pitch Type` = pitch_group) %>%
814
+ summarise(
815
+ Count = n(),
816
+ `Usage%` = sprintf("%.1f%%", (n() / total_pitches) * 100),
817
+ `Ext.` = ifelse("Extension" %in% names(movement_stats),
818
+ sprintf("%.1f", mean(Extension, na.rm = TRUE)),
819
+ "β€”"),
820
+ `Avg Velo` = sprintf("%.1f mph", mean(RelSpeed, na.rm = TRUE)),
821
+ `90th Velo` = sprintf("%.1f mph", quantile(RelSpeed, 0.9, na.rm = TRUE)),
822
+ `Max Velo` = sprintf("%.1f mph", max(RelSpeed, na.rm = TRUE)),
823
+ `Avg IVB` = sprintf("%.1f in", mean(InducedVertBreak, na.rm = TRUE)),
824
+ `Avg HB` = sprintf("%.1f in", mean(HorzBreak, na.rm = TRUE)),
825
+ `Avg Spin` = ifelse("SpinRate" %in% names(movement_stats),
826
+ sprintf("%.0f rpm", mean(SpinRate, na.rm = TRUE)),
827
+ "β€”"),
828
+ `Rel Height` = ifelse("RelHeight" %in% names(movement_stats),
829
+ sprintf("%.1f", mean(RelHeight, na.rm = TRUE)),
830
+ "β€”"),
831
+ `Zone%` = sprintf("%.1f%%", round(mean(in_zone, na.rm = TRUE) * 100, 1)),
832
+ `Whiff%` = sprintf("%.1f%%", round(mean(is_whiff, na.rm = TRUE) * 100, 1)),
833
+ `Chase%` = sprintf("%.1f%%", round(mean(chase, na.rm = TRUE) * 100, 1)),
834
+ .groups = "drop"
835
+ ) %>%
836
+ arrange(desc(Count))
837
+
838
+ DT::datatable(summary_stats,
839
+ options = list(pageLength = 15, dom = 't', scrollX = TRUE),
840
+ rownames = FALSE) %>%
841
+ DT::formatStyle(columns = names(summary_stats), fontSize = '12px')
842
+ })
843
+
844
+ # Selected pitch info in modal
845
+ output$selected_pitch_info <- renderText({
846
+ pitch_info <- selected_pitch()
847
+ if (!is.null(pitch_info)) {
848
+ pitch_data <- pitch_info$data
849
+
850
+ # Build info text with only available fields
851
+ info_lines <- c(
852
+ paste("Pitcher:", pitch_info$pitcher),
853
+ paste("Current Type:", pitch_data$TaggedPitchType),
854
+ paste("Velocity:", round(pitch_data$RelSpeed, 1), "mph"),
855
+ paste("Horizontal Break:", round(pitch_data$HorzBreak, 1), "inches"),
856
+ paste("Induced Vertical Break:", round(pitch_data$InducedVertBreak, 1), "inches")
857
+ )
858
+
859
+ # Add optional fields only if they exist and have values
860
+ if ("SpinRate" %in% names(pitch_data) && !is.na(pitch_data$SpinRate)) {
861
+ info_lines <- c(info_lines, paste("Spin Rate:", round(pitch_data$SpinRate, 0), "rpm"))
862
+ }
863
+
864
+ if ("Date" %in% names(pitch_data) && !is.na(pitch_data$Date)) {
865
+ info_lines <- c(info_lines, paste("Date:", pitch_data$Date))
866
+ }
867
+
868
+ return(paste(info_lines, collapse = "\n"))
869
+ } else {
870
+ return("No pitch selected")
871
+ }
872
+ })
873
 
874
  # Update pitch type
875
  observeEvent(input$update_pitch, {