Visualizing the 2019 Cy Young Contenders

While baseball’s overarching trends may be leaning the way of the batter, there’s still space for difference-making pitchers to put their own stamp on the game. This year, we’ve been blessed with a plethora of strong seasons from established aces and a number of less-than expected emergences.

Just in the past week, we’ve seen Justin Verlander throw his third career no-hitter, Gerrit Cole follow that up with 14 strikeouts, and Steven Strasburg rack up 14 of his own. Add to that the resurgent years from Lance Lynn and Charlie Morton, typical greatness from Clayton Kershaw and Max Scherzer, and a dominant performance from Hyun-Jin Ryu for most of the season (minus his last few outings), and you get a fantastic, totally impossible-to-guess race for the Cy Young in each league.

Pitch Analysis

Given that all of this year’s Cy Young contenders have had successful seasons by any measure, I thought it would be fun to dig into the pitch repertoires fueling their success and see what the data might show us. My hope, given that Gerrit Cole and Hyun-Jin Ryu couldn’t have more opposite pitching styles, is to show that there are a number of different ways to succeed as a pitcher in the MLB, and that each pitcher employs their arsenal in unique ways.

Pitch Movement

  • Ryu has a number of pitches clustered tightly together (2-Seam, 4-Seam, Cutter, and Changeup) - it has to be difficult to hit when each pitch appears to be similar but moves in a slightly different way
  • Kershaw varies his pitches vertically much more than horizontally, with a large pitch-to-pitch variance in vertical movement
  • Charlie Morton, on the flip side, has a large diffence in horizontal movement between his 2-Seam (runs to the left from the catcher’s POV) and Curveball (runs down and to the right from the catcher’s POC)
  • Strasburg and Ryu seem to dabble with the Slider, but haven’t made it a consistent part of their arsenals

Pitch Distribution

Release Points

  • The more narrow the distribution of dots for a given pitcher, the more consistently they “tunnel” their arm slot and keep different pitches appearing similar through their delivery
  • Note: Kershaw and Ryu appear on the right side of this chart because they are lefties
  • Verland and Morton tend to have the most overhand delivery of the group
  • Scherzer’s low, drop-and-drive delivery keeps his arm slot relatively low and wide relative to the rest of the group

Release Points

Spin Rate and Velocity

Spin Rate

Pitch Frequency

  • Observing pitch frequency as a percentage of total pitches thrown shows how balanced Ryu’s pitch arsenal is - he’s throwing five different pitches at least 10% of the time
  • Kershaw, by contrast, is pretty much a three pitch guy these days, and throws either his 4-Seam or Slider over 80% of the time, with his beautiful curve clocking in at only ~20% of pitches
  • Strasburg has split up his pitches into rough quarters, with the Changeup registering as the best “out” pitch of the bunch

Pitch Frequency

3-D Visualization of Movement and Velocity


Obtaining and Visualizing the Data

Using pybaseball (Python)

Pybaseball is an incredible project which provides a set of super-simple Python functions which allow users to easily access MLB Statcast data. I took advantage of the statcast_pitcher method to pull every pitch thrown by the eight Cy Young contenders this season and power this analysis.

from pybaseball import *
import numpy as np
import pandas as pd

playerid_lookup('scherzer', 'max')
scherzer_stats = statcast_pitcher('2019-03-01', '2019-10-01', 453286)
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style="darkgrid")

sns.relplot(data=scherzer_stats, x="pfx_x", y="pfx_z", hue = "pitch_type");

Creating the 3-D Pitch Plots (R)

While I ended up needing to pull in Shiny to make the 3-D scatterplots interactive and filterable by pitcher, it’s completely possible to make a self-enclosed 3-D scatter for a single pitcher in one quick Plotly command. Here’s a quick example of how to code that:

plot_ly(
    cy_stats %>% filter(player_name == 'Max Scherzer'), 
    x = ~effective_speed, 
    y = ~pfx_x, 
    z = ~pfx_z, 
    color = ~pitch_name, 
    text = ~pitch_name,
    hovertemplate = paste(
      "<b>%{text}</b><br>",
      "Pitch Velocity: %{x}<br>",
      "Horizontal Movement: %{y}<br>",
      "Vertical Movement: %{z}<br>"
      )
  ) %>%
  add_markers() %>%
  layout(title = "Max Scherzer's Pitch Mix",
         scene = list(xaxis = list(title = 'Pitch Velocity'),
                     yaxis = list(title = 'Horizontal Movement'),
                     zaxis = list(title = 'Vertical Movement'))
         )