Spaces:
Running
Running
Update app.R
Browse files
app.R
CHANGED
|
@@ -772,63 +772,68 @@ server <- function(input, output, session) {
|
|
| 772 |
}
|
| 773 |
})
|
| 774 |
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
|
| 782 |
-
|
| 783 |
-
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
|
| 797 |
-
|
| 798 |
-
|
| 799 |
-
|
| 800 |
-
|
| 801 |
-
|
| 802 |
-
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
|
| 808 |
-
|
| 809 |
-
|
| 810 |
-
|
| 811 |
-
|
| 812 |
-
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
|
| 822 |
-
|
| 823 |
-
|
| 824 |
-
|
| 825 |
-
|
| 826 |
-
|
| 827 |
-
|
| 828 |
-
|
| 829 |
-
|
| 830 |
-
|
| 831 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 832 |
|
| 833 |
output$selected_pitch_info <- renderText({
|
| 834 |
pitch_info <- selected_pitch()
|
|
|
|
| 772 |
}
|
| 773 |
})
|
| 774 |
|
| 775 |
+
output$movement_stats <- DT::renderDataTable({
|
| 776 |
+
req(plot_data(), input$pitcher_select)
|
| 777 |
+
|
| 778 |
+
data <- plot_data()
|
| 779 |
+
|
| 780 |
+
movement_stats <- data %>%
|
| 781 |
+
filter(Pitcher == input$pitcher_select) %>%
|
| 782 |
+
filter(!is.na(HorzBreak), !is.na(InducedVertBreak), !is.na(TaggedPitchType)) %>%
|
| 783 |
+
mutate(
|
| 784 |
+
pitch_group = case_when(
|
| 785 |
+
TaggedPitchType %in% c("Fastball", "FourSeamFastBall", "FourSeamFastB", "Four-Seam", "4-Seam") ~ "Fastball",
|
| 786 |
+
TaggedPitchType %in% c("OneSeamFastBall", "TwoSeamFastBall", "Sinker", "Two-Seam", "One-Seam") ~ "Sinker",
|
| 787 |
+
TaggedPitchType %in% c("ChangeUp", "Changeup") ~ "Changeup",
|
| 788 |
+
TRUE ~ TaggedPitchType
|
| 789 |
+
),
|
| 790 |
+
# Create necessary indicator variables if they don't exist
|
| 791 |
+
in_zone = ifelse("StrikeZoneIndicator" %in% names(.), StrikeZoneIndicator,
|
| 792 |
+
ifelse(!is.na(PlateLocSide) & !is.na(PlateLocHeight) &
|
| 793 |
+
PlateLocSide >= -0.95 & PlateLocSide <= 0.95 &
|
| 794 |
+
PlateLocHeight >= 1.6 & PlateLocHeight <= 3.5, 1, 0)),
|
| 795 |
+
is_whiff = ifelse("WhiffIndicator" %in% names(.), WhiffIndicator,
|
| 796 |
+
ifelse(!is.na(PitchCall) & PitchCall == "StrikeSwinging", 1, 0)),
|
| 797 |
+
chase = ifelse("Chaseindicator" %in% names(.), Chaseindicator,
|
| 798 |
+
ifelse(!is.na(PitchCall) & !is.na(PlateLocSide) & !is.na(PlateLocHeight) &
|
| 799 |
+
PitchCall %in% c("StrikeSwinging", "FoulBallNotFieldable", "FoulBall", "InPlay") &
|
| 800 |
+
(PlateLocSide < -0.95 | PlateLocSide > 0.95 | PlateLocHeight < 1.6 | PlateLocHeight > 3.5), 1, 0))
|
| 801 |
+
)
|
| 802 |
+
|
| 803 |
+
# Calculate total pitches for usage percentage
|
| 804 |
+
total_pitches <- nrow(movement_stats)
|
| 805 |
+
|
| 806 |
+
summary_stats <- movement_stats %>%
|
| 807 |
+
group_by(`Pitch Type` = pitch_group) %>%
|
| 808 |
+
summarise(
|
| 809 |
+
Count = n(),
|
| 810 |
+
`Usage%` = sprintf("%.1f%%", (n() / total_pitches) * 100),
|
| 811 |
+
`Ext.` = ifelse("Extension" %in% names(movement_stats),
|
| 812 |
+
sprintf("%.1f", mean(Extension, na.rm = TRUE)),
|
| 813 |
+
"—"),
|
| 814 |
+
`Avg Velo` = sprintf("%.1f mph", mean(RelSpeed, na.rm = TRUE)),
|
| 815 |
+
`90th Velo` = sprintf("%.1f mph", quantile(RelSpeed, 0.9, na.rm = TRUE)),
|
| 816 |
+
`Max Velo` = sprintf("%.1f mph", max(RelSpeed, na.rm = TRUE)),
|
| 817 |
+
`Avg IVB` = sprintf("%.1f in", mean(InducedVertBreak, na.rm = TRUE)),
|
| 818 |
+
`Avg HB` = sprintf("%.1f in", mean(HorzBreak, na.rm = TRUE)),
|
| 819 |
+
`Avg Spin` = ifelse("SpinRate" %in% names(movement_stats),
|
| 820 |
+
sprintf("%.0f rpm", mean(SpinRate, na.rm = TRUE)),
|
| 821 |
+
"—"),
|
| 822 |
+
`Rel Height` = ifelse("RelHeight" %in% names(movement_stats),
|
| 823 |
+
sprintf("%.1f", mean(RelHeight, na.rm = TRUE)),
|
| 824 |
+
"—"),
|
| 825 |
+
`Zone%` = sprintf("%.1f%%", round(mean(in_zone, na.rm = TRUE) * 100, 1)),
|
| 826 |
+
`Whiff%` = sprintf("%.1f%%", round(mean(is_whiff, na.rm = TRUE) * 100, 1)),
|
| 827 |
+
`Chase%` = sprintf("%.1f%%", round(mean(chase, na.rm = TRUE) * 100, 1)),
|
| 828 |
+
.groups = "drop"
|
| 829 |
+
) %>%
|
| 830 |
+
arrange(desc(Count))
|
| 831 |
+
|
| 832 |
+
DT::datatable(summary_stats,
|
| 833 |
+
options = list(pageLength = 15, dom = 't', scrollX = TRUE),
|
| 834 |
+
rownames = FALSE) %>%
|
| 835 |
+
DT::formatStyle(columns = names(summary_stats), fontSize = '12px')
|
| 836 |
+
})
|
| 837 |
|
| 838 |
output$selected_pitch_info <- renderText({
|
| 839 |
pitch_info <- selected_pitch()
|