knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE)
library(readxl)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)
library(knitr)

# Load the data
Upper630G1 <- read_excel("C:\\Users\\Franco Castagliuolo\\OneDrive - Bentley University\\Upper 630 G1.xlsx")
# Filter the data for the pitcher Dennis Helwig
Dennis_Helwig_data <- Upper630G1 %>%
  filter(Pitcher == "Helwig, Dennis")

# Create a detailed table for each pitch
detailed_pitch_table <- Dennis_Helwig_data %>%
  select(AutoPitchType, RelSpeed, SpinRate, SpinAxis, HorzBreak, InducedVertBreak, PitchCall, RelHeight, RelSide, Extension) %>%
  rename(
    ReleaseSpeed = RelSpeed,
    Tilt = SpinAxis,
    HorizontalBreak = HorzBreak,
    InducedVerticalBreak = InducedVertBreak,
    ReleaseHeight = RelHeight,
    ReleaseSide = RelSide
  ) %>%
  mutate(
    ReleaseSpeed = round(ReleaseSpeed, 2),
    SpinRate = round(SpinRate, 2),
    Tilt = round(Tilt, 2),
    HorizontalBreak = round(HorizontalBreak, 2),
    InducedVerticalBreak = round(InducedVerticalBreak, 2),
    ReleaseHeight = round(ReleaseHeight, 2),
    ReleaseSide = round(ReleaseSide, 2),
    Extension = round(Extension, 2),
    ClockTilt = round((Tilt / 30) %% 12, 1) # Interpret Tilt as clock face
  )

# Display the detailed table
knitr::kable(detailed_pitch_table, caption = "Detailed Pitch Table for Dennis Helwig")
Detailed Pitch Table for Dennis Helwig
AutoPitchType ReleaseSpeed SpinRate Tilt HorizontalBreak InducedVerticalBreak PitchCall ReleaseHeight ReleaseSide Extension ClockTilt
Four-Seam 91.93 2188.03 217.98 14.85 20.13 StrikeCalled 6.34 1.23 6.97 7.3
Four-Seam 94.53 2317.03 219.40 16.17 20.81 StrikeCalled 6.36 1.08 6.79 7.3
Slider 84.15 2357.61 112.19 -4.27 3.11 StrikeSwinging 5.66 1.94 6.52 3.7
Slider 82.57 2299.42 98.96 -7.12 2.51 StrikeCalled 6.04 1.96 6.37 3.3
Slider 81.05 2217.49 129.88 -7.85 8.12 InPlay 5.83 2.11 6.34 4.3
Four-Seam 93.82 2375.62 213.79 15.06 23.66 StrikeCalled 6.35 1.15 6.68 7.1
Four-Seam 94.24 2294.62 222.23 18.40 21.46 BallCalled 6.21 1.28 6.97 7.4
Sinker 93.63 2147.42 222.60 16.72 19.30 InPlay 6.35 0.98 6.82 7.4
Slider 83.97 2218.56 166.24 -1.67 8.12 StrikeCalled 5.89 3.01 6.57 5.5
Four-Seam 93.17 2186.09 210.51 13.15 23.44 StrikeSwinging 6.38 1.91 6.72 7.0
Slider 83.30 2022.90 171.91 -0.65 6.25 StrikeSwinging 5.66 3.12 6.56 5.7
Splitter 85.57 1207.53 245.73 14.12 7.68 FoulBallNotFieldable 5.98 1.43 6.68 8.2
Slider 82.46 2312.14 115.60 -6.59 4.82 InPlay 5.73 2.17 6.65 3.9
Four-Seam 94.09 2400.25 226.66 15.62 16.04 InPlay 6.28 1.53 6.63 7.6
Four-Seam 92.14 2308.52 202.64 9.23 23.32 StrikeCalled 6.32 2.02 6.87 6.8
Slider 81.78 2225.58 126.07 -7.82 7.03 StrikeSwinging 5.82 3.00 6.31 4.2
Four-Seam 94.27 2450.81 200.90 8.88 24.43 StrikeCalled 6.38 2.02 6.69 6.7
Four-Seam 93.88 2327.57 202.84 10.06 25.10 StrikeCalled 6.24 2.20 6.90 6.8
Changeup 85.86 1368.53 240.62 14.03 9.17 BallCalled 5.75 2.72 6.75 8.0
Four-Seam 93.83 2394.79 227.25 19.15 18.97 BallCalled 6.32 2.11 6.75 7.6
Sinker 93.00 2313.85 234.11 20.49 16.08 InPlay 6.31 2.17 6.91 7.8
Slider 81.65 2307.61 43.49 -4.71 -3.46 StrikeSwinging 5.94 2.16 6.33 1.4
Slider 82.65 2215.81 100.53 -6.70 2.62 StrikeSwinging 5.61 2.35 6.31 3.4
Four-Seam 94.00 2323.28 201.56 7.84 20.98 BallCalled 6.31 0.80 6.82 6.7
Curveball 81.78 2308.91 91.29 -17.01 1.84 StrikeSwinging 5.61 2.24 6.33 3.0
Slider 82.87 2150.27 101.56 -7.51 3.03 StrikeSwinging 5.78 1.97 6.38 3.4
Slider 82.56 2239.69 105.68 -8.18 3.75 StrikeCalled 5.78 1.96 6.34 3.5
Slider 80.32 2265.79 91.42 -20.69 2.10 InPlay 5.62 2.12 6.20 3.0
Four-Seam 91.55 2177.48 214.02 13.56 21.19 BallCalled 6.24 2.08 7.11 7.1
Four-Seam 91.27 2203.80 216.06 14.72 21.34 StrikeCalled 6.37 2.03 7.07 7.2
Slider 80.44 2176.69 90.45 -15.29 1.89 BallinDirt 5.81 2.90 6.43 3.0
Four-Seam 92.40 2204.43 209.24 11.13 20.97 BallCalled 6.33 1.97 7.14 7.0
Four-Seam 90.65 2105.03 210.23 11.84 21.42 StrikeSwinging 6.31 2.15 6.88 7.0
Slider 81.91 2121.06 142.63 -5.73 9.16 BallinDirt 5.75 3.12 6.46 4.8
Slider 81.33 2198.87 97.58 -9.16 2.84 HitByPitch 5.70 3.20 6.46 3.3
Four-Seam 91.73 2097.09 213.46 13.10 20.93 BallCalled 6.32 2.36 6.99 7.1
Four-Seam 92.22 2068.11 204.14 9.35 21.91 StrikeCalled 6.26 2.14 6.92 6.8
Four-Seam 92.07 2099.88 210.97 11.38 20.01 InPlay 6.22 2.09 7.08 7.0
Four-Seam 92.14 2066.42 212.15 12.18 20.49 StrikeSwinging 6.27 2.16 6.99 7.1
Slider 82.09 2046.97 200.69 2.47 8.24 InPlay 5.69 3.03 6.46 6.7
Four-Seam 92.04 2230.07 209.49 10.52 19.77 InPlay 6.21 1.50 6.87 7.0
Slider 80.75 2164.25 93.39 -8.60 2.27 BallCalled 5.91 3.03 6.47 3.1
Slider 79.87 2270.06 124.97 -5.12 5.10 BallCalled 5.90 2.94 6.52 4.2
Four-Seam 91.00 2270.63 224.38 16.00 17.52 FoulBallNotFieldable 6.21 2.16 6.87 7.5
Sinker 89.90 2183.97 221.90 16.11 19.07 BallCalled 6.38 2.19 6.83 7.4
Four-Seam 89.43 2188.19 218.43 15.32 20.48 BallCalled 6.25 1.99 7.03 7.3
# Calculate the total number of pitches
total_pitches <- nrow(detailed_pitch_table)

# Create a summary table
pitch_summary <- detailed_pitch_table %>%
  group_by(AutoPitchType) %>%
  summarise(
    TotalPitches = n(),
    Usage = sprintf("%.2f%%", n() / total_pitches * 100),
    Balls = sum(PitchCall == "BallCalled"),
    Strikes = sum(PitchCall != "BallCalled"), # Count everything not a ball as a strike
    BallPercentage = sprintf('%.2f%%', Balls / TotalPitches * 100),
    StrikePercentage = sprintf('%.2f%%', Strikes / TotalPitches * 100),
    AvgVelocity = round(mean(ReleaseSpeed, na.rm = TRUE), 2),
    AvgSpinRate = round(mean(SpinRate, na.rm = TRUE), 2),
    AvgInducedVertBreak = round(mean(InducedVerticalBreak, na.rm = TRUE), 2),
    AvgHorzBreak = round(mean(HorizontalBreak, na.rm = TRUE), 2),
    AvgTilt = round(mean(Tilt, na.rm = TRUE), 2),
    AvgClockTilt = round(mean(ClockTilt, na.rm = TRUE), 1), # Clock face interpretation
    AvgReleaseHeight = round(mean(ReleaseHeight, na.rm = TRUE), 2),
    AvgReleaseSide = round(mean(ReleaseSide, na.rm = TRUE), 2),
    AvgExtension = round(mean(Extension, na.rm = TRUE), 2)
  ) %>%
  select(AutoPitchType, Usage, everything())

# Display the total number of pitches
cat("Total number of pitches thrown: ", total_pitches, "\n")
## Total number of pitches thrown:  46
# Display the summary table
knitr::kable(pitch_summary, caption = "Summary Pitch Table for Dennis Helwig")
Summary Pitch Table for Dennis Helwig
AutoPitchType Usage TotalPitches Balls Strikes BallPercentage StrikePercentage AvgVelocity AvgSpinRate AvgInducedVertBreak AvgHorzBreak AvgTilt AvgClockTilt AvgReleaseHeight AvgReleaseSide AvgExtension
Changeup 2.17% 1 1 0 100.00% 0.00% 85.86 1368.53 9.17 14.03 240.62 8.0 5.75 2.72 6.75
Curveball 2.17% 1 0 1 0.00% 100.00% 81.78 2308.91 1.84 -17.01 91.29 3.0 5.61 2.24 6.33
Four-Seam 47.83% 22 7 15 31.82% 68.18% 92.56 2239.90 21.11 13.07 213.11 7.1 6.29 1.82 6.90
Sinker 6.52% 3 1 2 33.33% 66.67% 92.18 2215.08 18.15 17.77 226.20 7.5 6.35 1.78 6.85
Slider 39.13% 18 2 16 11.11% 88.89% 81.98 2211.71 4.31 -6.96 117.40 3.9 5.78 2.56 6.43
Splitter 2.17% 1 0 1 0.00% 100.00% 85.57 1207.53 7.68 14.12 245.73 8.2 5.98 1.43 6.68
# Calculate maximum fastball velocity
max_fb_velocity <- detailed_pitch_table %>%
  filter(AutoPitchType %in% c("Four-Seam", "Two-Seam", "Sinker", "Cutter")) %>%
  summarise(MaxFBVelocity = max(ReleaseSpeed, na.rm = TRUE)) %>%
  pull(MaxFBVelocity)

# Display the maximum fastball velocity
cat("Dennis Helwig maximum FB velocity: ", max_fb_velocity, "mph\n")
## Dennis Helwig maximum FB velocity:  94.53 mph
# Prepare data for plotting pitch locations
pitch_location_data <- Dennis_Helwig_data %>%
  select(AutoPitchType, PlateLocHeight, PlateLocSide, PitchCall) %>%
  rename(
    PitchHeight = PlateLocHeight,
    PitchSide = PlateLocSide
  ) %>%
  mutate(
    SwingTake = ifelse(PitchCall %in% c("StrikeSwinging", "FoulBallNonSwinging", "FoulBallFieldable", "FoulBallNotFieldable", "InPlay"), "Swing", "Take"),
    Chase = ifelse(SwingTake == "Swing" & (PitchSide < -0.75 | PitchSide > 0.75 | PitchHeight < 1.5 | PitchHeight > 3.5), "Chase", "Non-Chase")
  )

# Create the scatter plot with specified strike zone boxes
ggplot(pitch_location_data, aes(x = PitchSide, y = PitchHeight, color = SwingTake, shape = Chase)) +
  geom_point(size = 3) +
  geom_rect(aes(xmin = -0.5, xmax = 0.5, ymin = 1.75, ymax = 3.25), fill = NA, color = "red", linetype = "solid", size = 1) + 
  geom_rect(aes(xmin = -0.75, xmax = 0.75, ymin = 1.5, ymax = 3.5), fill = NA, color = "black", linetype = "solid", size = 1) + 
  geom_rect(aes(xmin = -1.25, xmax = 1.25, ymin = 1.25, ymax = 3.75), fill = NA, color = "gray", linetype = "solid", size = 1) +
  scale_x_continuous(limits = c(-2, 2)) +
  scale_y_continuous(limits = c(0, 5)) +
  coord_fixed(ratio = 1) +
  labs(title = "Pitch Locations for Dennis Helwig",
       x = "Horizontal Location (feet)",
       y = "Vertical Location (feet)",
       color = "Swing/Take",
       shape = "Chase") +
  facet_wrap(~ AutoPitchType) +
  theme_minimal() +
  theme(
    legend.position = "right",
    panel.grid.major = element_line(color = "grey80"),
    panel.grid.minor = element_line(color = "grey90"),
    axis.text = element_text(color = "black"),
    axis.title = element_text(color = "black"),
    plot.title = element_text(color = "black"),
    legend.background = element_rect(fill = "white", color = NA),
    legend.key = element_rect(fill = "white", color = NA),
    legend.text = element_text(color = "black"),
    legend.title = element_text(color = "black")
  )

# Create the scatter plot for horizontal and vertical breaks
ggplot(detailed_pitch_table, aes(x = HorizontalBreak, y = InducedVerticalBreak, color = AutoPitchType)) +
  geom_point(size = 3) + # Increase point size
  labs(title = "Pitch Movement for Dennis Helwig",
       x = "Horizontal Break (inches)",
       y = "Induced Vertical Break (inches)",
       color = "Pitch Type") +
  theme_minimal() +
  theme(
    legend.position = "right",
    panel.grid.major = element_line(color = "grey80"),
    panel.grid.minor = element_line(color = "grey90"),
    axis.text = element_text(color = "black"),
    axis.title = element_text(color = "black"),
    plot.title = element_text(color = "black"),
    legend.background = element_rect(fill = "white", color = NA),
    legend.key = element_rect(fill = "white", color = NA),
    legend.text = element_text(color = "black"),
    legend.title = element_text(color = "black")
  )