Analyzing Baseball Data with R

Some information about the book Analyzing Baseball Data With R, 2nd edition by Max Marchi, Jim Albert, and Ben Baumer:

Some useful links for the book.


61 responses

  1. […] my foray into R with baseball is a neat graphic based on a recent post from the authors of Analyzing Baseball With R.  They use the R statistical programming language to go through the copious amount of baseball […]

  2. Question 7 Chapter 3, p. 85 asks you to pull Pete Rose’s info, but from what I can tell, the function “getinfo” doesn’t work for two players with the exact same name (junior), or am I wrong?

  3. Yeah, I’ll try to fix this and then make the new function available — thanks.

  4. Errata link seems to be broken

    1. Aaron — thanks for noticing that — it should work now.

  5. Sergio Marrero Marrero | Reply

    Hello ! In regard to Chapter 9. I would like to have more information (further reading) about these topics:

    1) How to build a transition matrix to simulate a complete game between two teams, taking into account all offensive(batters/runners) and defensive (pitchers/fielding) strength.
    2) The method used in section 9.2.6 to estimate the transition matrix. In the book is written: “The description of this methodology is beyond the level of this book…” but no further reading or reference is given

    I feel lot of interest on these topics and I appreciate to have some references to continue my research.

    Thanks in advance. Sergio.

  6. Hello !! In regard to the Bradley-Terry model (chapter 9).

    1) Section 9.4: Further reading. The reference of “Chapter 9 of Albert and Bennet (2003)” seems to be wrong as in my book copy the Bradle Terry model is developed in the “Chapter 12, Did the best team win?”. Maybe I have a different edition.

    2) After examine the “Chapter 9 of Anaylzing Baseball Data With R” I jumped to the “Chapter 12, Did the best team win?. Curve Ball” with the hope of finding how to calculate the “Talent(t)” of teams. I did not find anyhing about it. The only way I know is “log5 model by Bill James”. I have thought on maximize the likelihood to find the “teams talent (t)”, but I would like to ask for some reading before jump on my own developing.

    So, is there any other approach to calculate the talents? Can anyone helpy me with further readings about it?

    Lot of thanks in advance !


  7. Hello !!

    In regard to the Bradley-Terry model (chapter 9).

    1) Section 9.4: Further reading. The reference of “Chapter 9 of Albert and Bennet (2003)” seems to be wrong as in my book copy the Bradle Terry model is developed in the “Chapter 12, Did the best team win?”. Maybe I have a different edition.

    2) After examine the “Chapter 9 of Anaylzing Baseball Data With R” I jumped to the “Chapter 12, Did the best team win?. Curve Ball” with the hope of finding how to calculate the “Talent(t)” of teams. I did not find anyhing about it. The only way I know is “log5 model by Bill James”. I have thought on maximize the likelihood to find the “teams talent (t)”, but I would like to ask for some reading before jump on my own developing.

    So, is there any other approach to calculate the talents? Can anyone helpy me with further readings about it?

    Lot of thanks in advance !


    1. Sergio: As I recall, I used a value of the standard deviation of the Bradley Terry talent distribution so that predicted w/l records of the simulated data resembled the observed w/l records. One could formally fit this B-T model and estimate the standard deviation, but I believe I used this empirical approach to estimate the standard deviation. This type of B-T model is typically used in paired comparison models and there are some Bayesian papers on this.

  8. Jim –

    In chapter 1, you guys state that “In 2011, hitters compiled a .253 batting average on plate appearances where they fell behind 0-2. Conversely they hit .479 after going ahead 2-0.” I’m trying to replicate those numbers and even using the pbp11rc.csv file, I can’t even come close. Instead of batter average, did you mean OBP?

  9. I’m trying to add an “Age” column in the Lahman batting.csv file. My idea is that I can use a combination of getinfo and the sapply function. I’m comfortable using the getinfo function for individual players. I’ve attempted to adapt the function to do this but I’m struggling. Any suggestions?

    Much appreciated. I’m really enjoying this book so far!

  10. Just got the new version of the book. In section 2.7 (p52), I’m getting an error with “object ‘crcblue’ not found.” Is that a color for the chart? I’ve tried uninstalling and reinstalling tidyverse and retyping the code and I can’t get through it. Any advice?

    1. Sorry, we neglected to define that color code in our code — just add the code
      crcblue <- "#2905a1".


      1. Can you share what the entire snippet of code should look like? I was having same error until I tried this suggestion but no luck. I have following:

        crcblue <- "#2905a1" followed by:
        ggplot(ws2, aes=(x = wins + losses)) + geom_bar(fill = crcblue) + labs(x= "Number of Games", y= "Frequency")

        After which I receive this mesage:

        "Error: stat_count() requires an x or y aesthetic.
        Run `rlang::last_error()` to see where the error occurred."

      2. I figured it out. One d*mn character bites you every time.

  11. I hate that I’m stumped on the first question I came across, and it seems straightforward, but for the average number of home runs per game recorded in each decade the answer says that the first two decades had 0.3 HRs per game. In the 1870 I get 356 HRs over 4,062 games for 0.09 per game, and in the 1880s 3,773 HRs over 17,484 games for 0.22 per game. What am I missing?

    1. It seems that you double-counted games. When you sum the games variable, it will be twice as large as the actual number of games since 2 teams are in each game. With this correction, you will get reasonable looking values.

      1. Dumb mistake on my part, I should have realized that error. Thank you very much for the response. I am enjoying the book a great deal!

  12. Hi Jim,

    In Chapter 7, when modeling called strike percentage, the book limits the PITCHf/x data to type == “S”, but from the description variable in the first column, it looks like “S” includes strikes of any kind. Rather than “S” and “X” being “called strikes” and “strikes” respectively as noted in the book, I’m pretty sure they stand for “strikes” and “balls in play.”


    1. Judah, I think you’re right — I’ll check with Ben. Thanks for mentioning the issue.

  13. On page 84 the code goes as follows:
    HRdata %
    mutate(Age = yearID – birthyear) %>%
    select(Player, Age, HR) %>%
    mutate(CHR = cumsum(HR))

    The graph however seems to perform the cumulative Sums for every players HR totals. Is there a way to unpack the Cumsum function for the individual players? Thank you for the awesome book.

    1. Christian:

      To do this for the individual plays, you just add a group_by(Player) line after the first mutate() line.

    2. To get these summaries for individual players, you just insert a group_by(Player) before the mutate operation.

  14. Hi guys. What a great book. Not sure if this is a great forum for questions but I’ll give it a shot. In chapter 6 it uses a “Cabrera” data set that has PitchFx data plus some batted ball location data. For these pre-loaded data sets, I’ve found it fun to see if I can recreate the same data from the source. I was able to do this for the PitchFx data, but I can’t find the batted ball data anywhere? Is that publicly available? It looks like Baseball Savant might have this data, but just in summary form and not in a database you can download. Thanks again for the great book. This is from the 1st edition if it matters

    1. Tim, you can load pitch-by-pitch data from Statcast using the baseballr package. This gives you all of the pitch information together with the batted ball measurements.

      1. Neat, can’t wait to look at that. But I am having trouble installing. I seem to be having the same issue described here which was never answered:

        Although for me the error is “cannot remove prior installation of package ‘rlang’”

  15. Hello,

    Where can I find the pbp2016rc data? I do not see it in the file.

    1. Herb, that file is from the 1st edition and can be found at


    2. That is showing only the 2011 file not the 2016, unless I am missing something?

      1. Nick: Can you clarify — what edition and what data file are you looking for? Thanks. Jim

  16. Hi, Jim. I’m fairly new to using R and have little coding background. I bought the book, downloaded R and the csv. files so far. I was able to download the Lahman package as well, but I can’t seem to get R to read the csv files? The error I get is Error in source(“/Users/caseystevens/Desktop/baseball_R-master/baseballdatabank-master/core/People.csv”) :
    /Users/caseystevens/Desktop/baseball_R-master/baseballdatabank-master/core/People.csv:1:9: unexpected ‘,’
    1: playerID,

    I can open it in Numbers just fine, but when I select a csv file and ‘Open With’ the data looks messy. What can I do to get this fixed?

    1. Casey, if the csv files are in the working directory, then you read them into R by statements like
      data <- read.csv("filename.csv") It appears that you were trying to use a source() function that is used to read in R functions. I would try some of the sample scripts from the chapters to get started. Jim

  17. The GitHub page for the 2nd edition of the book only has exercise solutions for 6 of the 14 chapters. Is there anywhere the rest of the solutions can be found?

    1. Daniel, sorry but I think that’s all we have currently available in terms of solutions. Jim

  18. When running the following code from the top of page 173 in the 2nd edition:

    mod_a <- glmer(type == "S" ~ strike_prob + (1|fielder_2),
    data = sc_taken, family = binomial)

    mod_a is being created as . I’m typing the code exactly as it is written in the text so I’m not sure why this would be occurring. Any help on obtaining the proper code would be much appreciated.

    1. Thomas: Here is similar more concise code that seems to work — hope this is helpful.

      # load several packages


      # read in the 2017 Statcast data

      sc_2017 <- read_csv("../data/statcast2017.csv")

      # only consider the called pitches

      taken <- filter(sc_2017, type != "X")

      # model the probability of a strike

      strike_mod <- gam(type == "S" ~ s(plate_x, plate_z),
      family = binomial,
      data = sample_n(taken, size = 100000))

      # work with a sample of 10,000 pitches

      sample_taken <- sample_n(taken, size = 10000)

      # define new variable that predicts the probability
      # of a strike

      sample_taken <- mutate(sample_taken,
      strike_prob = predict(strike_mod,
      newdata = sample_taken,
      type = "response"))

      # fits random effects model using strike_prob as
      # covariate and catcher random effects

      mod_a <- glmer(type == "S" ~ strike_prob +
      data = sample_taken, family = binomial)

  19. For the chapters with no solutions, is there a way to check my solutions? Thank you. By the way, I bought your book, “Teaching Statistics with Baseball” and wonder if there are solutions for it as well.

    1. Stan, solutions to the exercises for some chapters of ABDR are available at
      For my teaching stats using baseball book, just send me an email at, explaining that you are not a student in a class and I can send you solutions.

  20. Thanks for the book. I have the 2nd edition. I have been stuck for several days on Section 11.7 on the line “This assumes that user has a MySQL option file preconfigured to connect to a database retrosheet”. I suppose this is for “src_mysq_cnf(“retrosheet”)” to run. I’m similarly stuck in 12.2.1 using statcastr on the line src_mysql_cnf(“statcast”). I have My_SQL on my machine and use it often. Any references you can point me to help me set up a “MySQL option file preconfigured to connect to a database retrosheet”.

    1. Yes! Here is the official documentation:

      Basically, the idea is that you keep your credentials in a file that is private to you, so that you can show how to connect to databases publicly without revealing your credentials.

      1. I ran mysql_config_editor and now have a mylogin.cnf file that contains
        user = localuser
        password = *****
        host = localhost
        I can now connect with src_mysq_cnf(“retrosheet”,groups=”local”)
        Do you know how I can put DBI connection in the mylogin.cnf file so I could connect with just
        Thank you so much for your time

  21. Statcastr is not working for some 2019 months, however is working for others.
    I was able to run etl_extract(year=2019, month=6) & etl_extract(year=2019, month=7) just fine.
    All other 2019 months give errors. For example…
    etl_extract(year=2019, month=3) gives
    Error: Column `game_date` can’t be converted from character to Date
    etl_extract(year=2019, month=4) gives
    Error: Column `fielder_2…42` can’t be converted from numeric to character
    I believe the error is cause by “empty” dates, dates with not statcast output.
    Is there a way to refine the etl_extract() input so it skips over these dates, or give it a specific date, or specific date range?
    Thanks again!

  22. I’m still pretty new to R so I may be simply overlooking this, but at the beginning of Chapter 5 I’m having trouble getting the “data2016” csv file.

    The line I’m struggling with is on page 112, “data <- read_csv("data/all2016.csv", col_names = pull(fields, Header), na = character())

    I'm getting the error message "Error: 'data/all2016.csv' does not exist in current working directory ", but I made sure every file related to this is in the right place.

    Again, I've looked through all of my downloaded files to see if it could be misplaced and I don't see it anywhere on Github. I've also tried re-downloading the files and still no luck Any suggestions? Thanks!

    1. Zack, the reason why that all2016.csv file wasn’t there was that it was too large for Github. But since things have changed, I was successful in getting that file in the data folder. Download the files from Github and try it again. Jim

      1. I am also trying this code and get this error.

        See spec(…) for full column specifications.
        |===========================================================================| 100% 77 MB
        Warning: 182 parsing failures.
        row col expected actual file
        3347 REMOVED_FOR_PR_RUN2_ID 1/0/T/F/TRUE/FALSE navad002 ‘all2016.csv’
        5180 REMOVED_FOR_PR_RUN2_ID 1/0/T/F/TRUE/FALSE cronc002 ‘all2016.csv’
        5185 REMOVED_FOR_PR_RUN3_ID 1/0/T/F/TRUE/FALSE mazan001 ‘all2016.csv’
        6233 REMOVED_FOR_PR_RUN2_ID 1/0/T/F/TRUE/FALSE calhk001 ‘all2016.csv’
        7319 REMOVED_FOR_PR_RUN2_ID 1/0/T/F/TRUE/FALSE hollm001 ‘all2016.csv’
        …. …………………. ……………… …….. ………….
        See problems(…) for more details.

        Based on some research, I think I have to fix this. Is this error reproducible on your end sir? Thanks.

      2. Stan:

        I got similar parsing errors, but I don’t think this is a problem for most of what you want to do with this data.


      3. Thank you sir.

  23. Dear Authors,
    I have the 2nd edition. Could you check the matrix in Ch9 p209 entered as “RE” with first entry 0.47. I don’t recognize this as a matrix from Ch 5. The first entry in the expectancy matrix in Ch5 in 0.50. Is this a typo?

  24. R newbie struggling with this error message when using mutate:

    Batting %>%
    + mutate(decade=10*floor(yearID/10))%>%
    + split(pull(.,decade))%>%
    + map_df(hr_leader, .id=”decade”)

    Get error message: “Error in mutate(decade = 10 * floor(yearID/10)) :
    object ‘yearID’ not found”

    Is there a package I’ve not installed?

    1. Rob, I just checked this code and it seems to work. You need to have the Lahman and dplyr packages installed. Maybe that is the issue. Good luck. Jim

      1. Something is amiss. I have Lahman, dplyr, magrittr, purrr all installed.

      2. Sometimes R gets confused with some dplyr commands — if you replace mutate with dplyr::mutate it may work.

      3. Like this?:
        Batting %>%
        + + + dplyr::mutate(decade=10*floor(yearID/10)) %>%
        + + + split(pull(.,decade)) %>%
        + + + map_df(hr_leader,.id=”decade”)

        Unfortunately, this returned same result.

      4. Rob: Are you copying and pasting directly from the book? Those “+ + +” signs shouldn’t appear. Jim

  25. I got it. I reinstalled all 4 of those packages, did the library() for each of them, and then reentered each of the snippets of code that defined each of the variables and function leading up to the troublesome step and it worked. Shout out to Jim for his time and patience. Thanks.

    1. Rob, I know it can be frustrating getting started. Glad it worked. Jim

  26. […] 4 of Analyzing Baseball Data with R doesn’t introduce a lot of new functions. Instead, it shows how one can synthesize what they’ve […]

  27. […] bizarre happened to me this week while reading Chapter 5 of Analyzing Baseball Data with R. After reading and re-reading a paragraph explaining how to set up some lines of code, I thought to […]

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: