Monthly Archives: December, 2021

Linking Pitch Location, Spray Chart and Launch Variables

Introduction

In last week’s post, we illustrated the use of Retrosheet’s BATTEDBALL_LOC_TX variable that categorizes the batted ball in one of the 32 regions of the playing field. Statcast data provides the actual location of the batted ball and assuming this is accurate, gives more precise information than the Retrosheet code. Reflecting on this location data, there are three relevant types of information in understanding hitting performance of a ball in play.

  • the pitch location
  • the quality of the ball off the bat as measured by the launch variables (specifically launch angle and exit velocity)
  • the batted ball location on the field

These three types of information are clearly associated. For example, balls thrown inside to a right-handed batter tend to be pulled to the left side of the field. Hard-hit balls, say over 105 mph, with an launch angle about 30 degrees tend to be home runs. So it seems natural to construct multiple scatterplot displays of pitch location, launch variables, and batted ball locations where the displays are linked by brushing. By using the interactive brushing capability built into Shiny apps, it is straightforward to construct an app where one is able to brush a region on one display and see the location of the brushed points on the other displays. I will first illustrate how this app can be used and then provide some information about the process of writing the Shiny code.

The Display

This Shiny app (currently live at https://bayesball.shinyapps.io/statcast_app_3/ ) is easy to use. One selects a 2021 hitter from the dropdown menu (here, Bryce Harper) and one sees three graphs. The left graph is a scatterplot of the launch angle and launch speed measurements, and the right side shows the zone locations (top) and the field locations for all balls put into play (bottom). One explores the relationships between these three displays by selecting (brushing) a region in any one of the displays and looking at the corresponding highlighted batted balls in the other graphs. The lower-left region will show summary statistics for batted balls in the selected region.

Brushing Zone Location

Let’s illustrate the use of this app with the National League MVP Bryce Harper. What are the hitting characteristics of Harper balls in play hit in the lower portion of the zone? I select this particular region in the Zone Locations graph. From the Launch Variables graph, we see these low zone pitches result in a variety of types of batted balls with different launch angles and exit velocities. From the Batted Ball Locations graph, we see that most of the short landing balls are pulled (to the right side) and the balls landing in the outfield are hit to all fields. Looking at the table, the mean launch speed of these low zone pitches is 92.922 mph and the mean launch angle is 11.425 degrees. The hit rate of these pitches is 37 / 120 = 0.308.

If we move our region to the middle of the zone, we see a general increase in both exit velocity and launch angle — the average values have increased to 95.576 mph and 16.865 degrees respectively. Also the hit rate has increased to 51 / 104 = 0.490. Comparing “low zone” balls with “middle of the zone” balls, we conclude Harper is more productive on pitches in the middle of the zone.

Brushing Batted Ball Location

Let’s instead look at the Batted Ball Locations graph and select the balls that were hit where the location_y variable exceeds 400 ft. From the Zone Locations graph, we see these deep balls were hit in the middle-inside portion of the zone. From the Launch Variables graph, we see that all of these balls were hit over 100 mph with launch angles between 20 and 40 degrees.

Brushing Batted Ball Type

There are three vertical lines on the Launch Variables plot dividing the batted balls into ground ball (launch angle less than 10 degrees), line drive (launch angle between 10 and 25 degrees), fly ball (launch angle between 25 and 50 degrees) and popup (launch angle greater than 50 degrees) groups. By selecting the popups below, we see from the Batted Ball Locations graph that these balls in play are relatively shallow and hit towards the opposite side (left field for a left-handed batter).

The Shiny App – Code Details

The live version of this Shiny app can be found on https://bayesball.shinyapps.io/statcast_app_3/ You are welcome to play with this app choosing any 2021 batter of interest (among the 257 batters with at least 200 balls in play) from the dropdown menu. You should discover some interesting findings — for example, you should be able to find your batter’s sweet spot in the zone and see the field locations for hard-hit line drives.

All of the code can be found as a single file app.R here. (This app is included in my ShinyBaseball package.) To run this app on your laptop, just download the single app.R file and place it in a separate folder. If the folder is the current working directory, just type

runApp() 

from the Console window in RStudio to run the app. (You should make sure that you have first installed the packages listed at the top of the app.R file.)

If you look at the code in app.R, you’ll see that the data (the Statcast and Chadwick files) are downloaded from from my Github site and I have put all of the graphing work in separate functions. For example, the function plot_locations() constructs the Batted Ball Locations display. If you look at the ui() function defining the user interface, you see the use of the selectInput() function to set up the dropdown menu and the functions plotOutput() and tableOutput() set up the output regions for plotting and displaying the summary table. There are special arguments in plotOutput() for adding the brushing capability — the brush = "plot_brush" argument adds a brush called “plot_brush". In the plotting functions, you’ll see that I use

geom_point(data = brushedPoints(new_data, input$plot_brush)

This indicates that I will use a subset of the points defined by the plot_brush input. The same brushedPoints() function is used in the renderTable() function creating the summary data frame.

I have tried to make the user interface and server functions relatively succinct so the interested reader can use and hopefully improve what I have done here.

Closing Remarks

  • Using this App. Pitchers are interested in learning the in-play tendencies of batters that they will face. If they are pitching “to contact”, then they want to pitch to locations for a particular batter where the launch speed is low with low launch angles. This type of graphical exploration should be helpful in figuring out these good pitching locations. Also hitters might be interested in finding regions of the zone where the expected hit rate is high.
  • See Relationships. This brushing exercise is potentially useful for understanding relationships between these variables, especially those that are not easily described by straight lines.
  • Improving the App. It would be interesting to look at these graphs for particular situations such as facing pitch types that are either off-speed or fastballs. Or one could look for differences in these relationships for neutral, batter-friendly or pitcher-friendly counts. It is straightforward to modify the code for this Shiny app to provide these extensions.
  • Other Brushing Examples? In a post earlier this year, I demonstrated the use of two Shiny apps that allowed one to compute batting and pitching measures over brushed regions of the zone.