Building A Post-Game Pitching Report In Shiny R Using Trackman Data

Mason McRae
5 min readJun 10, 2023

About a year ago I started learning how to code at Tread. None of this would be possible without the brains of Cam (@k_camden) and Rylan (@RylanDomingues). Making this blog forced me to go back and clean up the code, but some of it is still a mess since it’s from the end of the summer when I started working on it. I’ve removed most of the code that involves stuff +, but I might’ve left some in. You’ll have to remove that.

To create this, you will obviously need TrackMan and the CSVs created through it. To access the code for this, go to go to my GitHub account.

We’ll use Cory Wall’s final outing of the year as an example. This is what it looks like. In the upper left, you have two options for filtering the data. The first is by choosing one pitcher, or by selecting all pitchers. The other is setting a date range.

When I first made this, you could only select a single date and player. The ‘all’ option lets you look at the entire staff for a weekend, a month, the season, or the entire fall, preseason, and season.

To create your own shiny r script, you’ll follow the two pictures below.

Once you create it, you can simply copy and paste my code from the github into it. You’ll need to make sure all of the files you need are in the same folder as your shiny r script, as shown below.

Now, I’ll briefly explain some of the code and what it does.

At the top, this is where all of my CSVs are brought in. There are three, the first is the game file from TrackMan. The second is a dataset with xwOBAcon by EV and LA so that you can get those values. Lastly, a dataset with the called strike probability based on the vertical and horizontal pitch location. Since this is just for pitching, I’m filtering the main CSV to only include my team’s pitchers, to remove that just delete the line. If you want to change it your team, replace the values I set to your own teams.

To access the files, go to my GitHub and download the files there.

There are two ways to do this, you can either create case_when’s and if_else’s like I did. The alternative is just doing this in the summarize section of the data tables we’ll be going over later. What this does is create columns with true or false or 0/1 value’s. We’ll use this to create the stats like whiff, zone, and strike rate.

This is where we take the data from our two files and combine it so we’re able to get an xwOBAcon value for batted balls and a called strike probability value for every pitcher. Whenever a ball is hit with a 90 EV and 20 LA, the value from the BACON dataset will be inputed.

The UI is where we present the data. If I want to put every single table in it’s own page, we’d put each one on a different tabPanel(). Since some of them are just three rows, I stack multiple onto one page.

The first picture in this article shows the top tapPanel. This one:

This is the beginning of the server. It’s where we put the inputs for our date range and pitcher selection. You could play around with it and add other filters.

The code for all of the tables you’ll see in the shiny app are similar to this with a few tweaks. You can see what this one looks like below, it’s the bottom table. In the group_by portion, we choose what main variable we want to summarize our data with. That could be based on the handedness. It could by pitch type, count, inning, outs, etc.

Next up, we’ve got heat maps.

You could also group_by multiple variables here. Similarly to what we did about with pitch type against batter side. You could look at heat maps by side and type. Why haven’t I done this already? No idea.

Lastly, here are two basic ggplots. One is movement, the other is release.

The code for these is short and simple.

You’re able to change the color of the points in the aesthetic (aes) section and the sizes of them in the geom_point row. To make it so every plot is the same, you set the geom_segment values. For example, with movement I want it to have -25 and 25 as the farthest horizontal and vertical values we can see.

That’s a brief overview of the entire report. If you want to create your own report, go into my GitHub and copy paste the code into your own shiny r.