Team Payroll and the World Series

Michael Friendly


This vignette examines whether there is a relationship between total team salaries (payroll) and World Series success. It was inspired by Nolan & Lang (2015), “Baseball: Exploring Data in a Relational Database”, Chapter 10 in Data Science in R. They use SQL on the raw Lahman files .csv, rather than the Lahman package.

Here, We largely use dplyr for data munging and ggplot2 for plotting. In the process, we discover a few errors in the data sets.

The data files

Start with loading the files we will use here. We do some pre-processing to make them more convenient for the analyses done later.

The Salaries data

The Salaries data.frame contains data on all players’ salaries from 1985-2015 in the latest release, v. 7.0.1, of the Lahman package. We use the sample_n function to display a random sample of observations.

The Teams data

The Teams data.frame contains a lot of information about all teams that have ever played, with a separate observation for each year. Here, we will mainly use this to get the team name (team) from teamID and also to get the information about World Series winners.

We are only going to use the observations from 1985 on, and a few variables, so we filter and select them now. Keep only the levels of teamID in the data.

The SeriesPost data

Post season records go back to 1884. There are 334 observations covering all aspects of post-season play.

For each year, there are number of observations for the various levels of post-season play (Division titles, League titles, etc. A number of these designations have changed over the years, and I don’t know what they all mean.)

We are interested only in the World Series (WS), which was first played in 1903. We filter for the years for which we have salary data, and drop a couple of variables. The league IDs of the winner and loser are factors, so we use droplevels to include only the levels in recent history.

A first look at Salaries

How many players do we have in each year?

## 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 
##  550  738  627  663  711  867  685  769  923  884  986  931  925  998 1006 
## 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 
##  836  860  846  827  831  831  819  842  856  813  830  839  848  815  802 
## 2015 2016 
##  817  853

What is the range of salaries, across all years?

## [1]        0 33000000

And, year by year?

Salaries %>% 
  group_by(yearID) %>% 
## # A tibble: 32 x 3
##    yearID    min     max
##     <int>  <dbl>   <dbl>
##  1   1985  60000 2130300
##  2   1986  60000 2800000
##  3   1987  62500 2127333
##  4   1988  62500 2340000
##  5   1989  62500 2766667
##  6   1990 100000 3200000
##  7   1991 100000 3800000
##  8   1992 109000 6100000
##  9   1993      0 6200000
## 10   1994  50000 6300000
## # ... with 22 more rows

Hmm, there is a salary==0 in 1993, maybe there are others.

## [1]  6180 12008

Who are they? (We could also look up their playerIDs in Lahman::People.)

##       yearID teamID lgID  playerID salary
## 6180    1993    NYA   AL jamesdi01      0
## 12008   1999    PIT   NL martija02      0

These must be errors. Get rid of them. Reminder: Check further; maybe file an issue in the Lahman package!

Salaries <- Salaries %>%
    filter(salary !=0)

Get team payrolls

We want to sum the salary for each team for each year. We might as well make it in millions. All those zeros hurt my eyes.

Merge team names into payroll

It will be more convenient to have the team names included in the payroll data.frame. The Teams data frame also contains the Y/N indicator WSWin for World Series winners, so we might as well include this too.

Note that we could also do this using left_join in the dplyr package. There is probably a more tidy way to subset the variables from the Teams data set than using Teams[, c()], but, hey– this works.

WSWin is a character variable. Convert it to a factor.

Check the values:

There is something wrong here! There shouldn’t be any NAs. We leave this for further study, and another Reminder to file an issue if we figure out what the problem is.

Boxplots of payroll

Let’s look at the distributions of payroll by year. The observations are teams.

boxplot(payroll ~ yearID, data=payroll, ylab="Payroll ($ millions)")

What are the outliers? Are there any teams that crop up repeatedly? car::Boxplot makes this easy, and also returns the labels of the outliers. We don’t load the car package, because car also contains a Salary dataset.

out <- car::Boxplot(payroll ~ yearID, data=payroll,
             ylab="Payroll ($ millions)")

Most of the outliers are the New York Yankees (NYA):

## out
##   1   2   1  11   1   1

Payroll has obviously increased dramatically over time. So has the variability across teams. For any modelling, we would probably want to use \log(payroll). We might also want to look separately at the American and National leagues.

Correcting for inflation

For proper comparisons, we should correct for inflation. Lets do this by scaling salary back to 1985 dollars, The data below gives inflation rates for all subsequent years. It comes from Nolan & Lang, extended to 2015 using (

This is close enough to linear, that we could use the linear regression predicted value as a simple computation of the inflation rate. (A better way, of course, would be to use the actual inflation rate; this would entail merging payroll with inflation.df by year, and doing the computation.)

Scale payroll by dividing by linear prediction of inflation rate, producing payrollStd.

Boxplot again, of inflation-adjusted payroll. The increase after 2000 doesn’t seem so large.

##  [1] "SEA" "CLE" "MON" "PIT" "NYA" "NYA" "NYA" "NYA" "NYA" "NYA" "NYA"
## [12] "NYA" "NYA" "NYA" "LAN" "NYA" "LAN" "NYA" "BOS" "DET" "LAN" "NYA"

Salaries of World Series winning teams

To what extent are the World Series winners those among the highest in payroll? A simple way to look at this is to plot the team payrolls across years, and mark the World Series winner for each year.

This plot shows inflation-adjusted payroll on a log scale to avoid the dominating influence of the most recent years. We jitter the points to avoid overplotting, and use a transparent gray color for the non-winners, red for the winner in each year.

Cols <- ifelse(payroll$WSWin=='Y', "red", gray(.7, alpha=0.5))
with(payroll, {
  plot(payrollStd ~ jitter(yearID, 0.5), 
       ylab = "Payroll (inflation-adjusted $ millions)",
       ylim = c(5,125), log = "y",
       xlab = "Year",
       pch = 19, cex = 0.8, col = Cols)
with(payroll[payroll$WSWin == 'Y',],
     text(y = payrollStd, x = yearID, labels = teamID, pos = 3, cex = 0.8) )

By and large, the World Series winners tend to be in the upper portion of the payrolls for each year.

Further analyses: Your turn

Here are some questions to provoke further analyses of these data sets. If you find something interesting, post it in a Github Gist or forward it to Team Lahman as in a Lahman issue.