MLB Batting Statistics

In this problem set, you will practice using the pipe %>% and grouped calculations with batting statistics taken from the Lahman dataset. We will compute the same statistics as we did in Problem Set 2 and also standardize these statistics within year, year and league, and also historical era.

To load the batting data into R, we can do the following:

library(tidyverse)
library(Lahman)
batting <- as_tibble(Batting)

Unfortunately, some statistics like hit-by-pitch (HBP) were not recorded in the earlier decades of baseball. In the tbl, these missing values are designated NA. To see some of these, we can run the following code:

batting %>% 
  select(playerID, yearID, teamID, AB, BB, HBP, SH, SF, IBB, GIDP)
## # A tibble: 110,495 × 10
##    playerID  yearID teamID    AB    BB   HBP    SH    SF   IBB  GIDP
##    <chr>      <int> <fct>  <int> <int> <int> <int> <int> <int> <int>
##  1 abercda01   1871 TRO        4     0    NA    NA    NA    NA     0
##  2 addybo01    1871 RC1      118     4    NA    NA    NA    NA     0
##  3 allisar01   1871 CL1      137     2    NA    NA    NA    NA     1
##  4 allisdo01   1871 WS3      133     0    NA    NA    NA    NA     0
##  5 ansonca01   1871 RC1      120     2    NA    NA    NA    NA     0
##  6 armstbo01   1871 FW1       49     0    NA    NA    NA    NA     0
##  7 barkeal01   1871 RC1        4     1    NA    NA    NA    NA     0
##  8 barnero01   1871 BS1      157    13    NA    NA    NA    NA     1
##  9 barrebi01   1871 FW1        5     0    NA    NA    NA    NA     0
## 10 barrofr01   1871 BS1       86     0    NA    NA    NA    NA     0
## # … with 110,485 more rows

A common convention for dealing with the missing values when computing \(\text{PA}\) is to replace the NA with a 0. To do this, we can use the function replace_na() within a pipe as follows.

batting <- 
  batting %>% 
  replace_na(list(IBB = 0, HBP = 0, SH = 0, SF = 0, GIDP = 0))
batting %>% select(playerID, yearID, teamID, AB, BB, HBP, SH, SF, IBB)
## # A tibble: 110,495 × 9
##    playerID  yearID teamID    AB    BB   HBP    SH    SF   IBB
##    <chr>      <int> <fct>  <int> <int> <int> <int> <int> <int>
##  1 abercda01   1871 TRO        4     0     0     0     0     0
##  2 addybo01    1871 RC1      118     4     0     0     0     0
##  3 allisar01   1871 CL1      137     2     0     0     0     0
##  4 allisdo01   1871 WS3      133     0     0     0     0     0
##  5 ansonca01   1871 RC1      120     2     0     0     0     0
##  6 armstbo01   1871 FW1       49     0     0     0     0     0
##  7 barkeal01   1871 RC1        4     1     0     0     0     0
##  8 barnero01   1871 BS1      157    13     0     0     0     0
##  9 barrebi01   1871 FW1        5     0     0     0     0     0
## 10 barrofr01   1871 BS1       86     0     0     0     0     0
## # … with 110,485 more rows

The syntax for replace_na() is a bit involved but the basic idea is you have to specify the value you want to replace each NA. When using replace_na() it is very important to remember to include the list(...) bit.

  1. Load the Lahman data and run the above code to create the tbl Batting, which has replaced all of the NA’ in the columns IBB, HBP, SH, SF and GIDP`.

  2. Using the pipe %>%, mutate(), filter(), and select(), create a tbl batting by:

    • Adding columns (with mutate()) for plate appearances (PA), unintentional walks (uBB), singles (X1B), batting average (BA), on-base percentage (OBP), on-base plus slugging (OPS), and weighted On-Base Average (wOBA). Note that the formula for plate appearances is \(\text{PA} = \text{AB} + \text{BB} + \text{HBP} + \text{SH} + \text{SF}.\) Formulae for the remaining statistics are given in Problem Set 2.
    • Pulling out only those rows of players with at least 502 plate appearances and who played in either the AL or NL with filter()
    • Select the following columns: playerID, yearID, lgID, teamID, PA, BA, OBP, OPS, wOBA.

  3. Standardize each of BA, OBP, OPS, and wOBA using data from all of the years. Name the columns containing these new standardized values zBA_all, zOPB_all, etc. Remember, you must re-define the standardize() function we wrote in Lecture 3 Who were the best and worst batters according to these four metrics?

  4. Group batting by year and compute the standardized BA, OBP, OPS, and wOBA within each year. Name the columns containing these new standardized values zBA_year, zOBP_year, etc. Now who are the best and worst batters according to the four measures?

  5. Remove the grouping by year and instead group by year and league. Once again, standardize OBP, OPS, and wOBA within each league-year combination, and name the columns containing these new standardized values zBA_year_lg, zOBP_year_lg, etc. Are the best and worst batters still the same?

  6. Remove the grouping you created in Problem 4. Bill James divided baseball history into several eras as follows:

    • Pioneer Era: 1871 – 1892
    • Spitball Era: 1893 – 1919
    • Landis Era: 1920 – 1946
    • Baby Boomer Era: 1947 – 1968
    • Artifical Turf Era: 1969 – 1992
    • Camden Yards Era: 1993 – present

    Use mutate() and case_when() (just like we did in Lecture 2) to add a column called Hist_era to batting that records the historical era.

  7. Group batting by Hist_era and standardize BA, OBP, OPS, and wOBA within historical era. Who are the best and worst batters now? Name the columns containing these new standardized values zBA_hist, zOBP_hist, etc.

  8. Remove the grouping you added in Problem 6.

MLB Payroll and Winnings

Recall from Problem Set 2, we plotted the relative payroll of MLB teams against their winning percentage. In that problem set, we read in a file that had included the relative payroll for each team as a separate column. To get some additional practice with dplyr, we will read in a different dataset and re-compute these relative payrolls.

  1. Remember the function we wrote in Lecture 3 to standardize various statistics? It is reproduced below:
standardize <- function(x){
  mu <- mean(x, na.rm = TRUE)
  sigma <- sd(x, na.rm = TRUE)
  return( (x - mu)/sigma)
}
  1. Read in the MLB Payroll Data and load it into a tibble called mlb_payrolls.

  2. Using the pipe %>%, group_by(), and mutate(), add a column to mlb_payrolls that contains the relative payroll for each team.

  3. Make a scatterplot of winning percentage against relative payrolls. Comment on the relationship. Your scatterplot should be identical to one you made in Problem Set 2.

  4. Using the summarize() function, compute the average team payroll and relative payroll for each year. Save these results in a new tbl called payroll_avg.

  5. Make a scatterplot that shows how team payrolls have evolved over the year. Similar to what we did in Lecture 3, add a line to this scatterplot that shows the average team payroll. Do the same thing for relative payroll. What do you notice about the average team payroll and relative payroll?

  6. As you will see in coming lectures, correlation is a measure of the strength of the linear relationship between two variables. The closer to +1 or -1 the correlation between two variables is, the more predictable they are of each other. We can compute it using the cor() function. Using summary() and cor(), compute the correlation between relative payroll and winning percentage within each year. What do you notice about how the relationship between winning percentage and relative payroll changes year to year?

A Challenge Question

Without running the code, work with your teammates to see if you can figure out what the code below is doing.

batting_2014_2015 <-
  batting %>%
  filter(yearID %in% c(2014, 2015)) %>%
  group_by(playerID) %>%
  filter(n() == 2) %>% 
  select(playerID, yearID, BA) %>%
  arrange(playerID)

Now, run the code above and save the tbl batting_2014_2015.RData to the file “data/batting_2014_2015.RData” using the save() function. We will return to this dataset in Lecture 4.