If you’re reading about my fantasy baseball experience for the first time, welcome! You may be better oriented by reading this first.
In the last three posts, I wrote about my draft strategy using projected WAR, and explored fantasy talent by defensive position. I discovered that WAR (Wins Above Replacement) was not a great way to select players given my league’s scoring categories. I calculated z-scores for my scoring categories instead, and looked at how that would have changed my draft picks. I did some deep reflection on the danger of fandom bias.
Up to this point, we’ve been looking at projections for 2018. Now that the season is over, we can see just how these projections played out. I used the final 2018 stats from Fangraphs, and compared them to the projected stats. I’ve renamed all final stats to include the f_ prefix for clarity, calculated z-scores for each stat, and over the total, and then I merged this dataset to the full batters dataset, to facilitate comparison. I also merged it to the subset of players I drafted to look specifically at my team, Dropped Third Strike.
end_bat_z <- read.csv("../data/post1/batters_final.csv") %>% rename(f_R = R, f_HR = HR, f_RBI = RBI, f_SO = SO, f_SB = SB, f_OPS = OPS, f_WAR = WAR) %>% filter(PA >= 300) %>% mutate(f_R_z = z_score(f_R), f_HR_z = z_score(f_HR), f_RBI_z = z_score(f_RBI), f_SO_z = -z_score(f_SO), f_SB_z = z_score(f_SB), f_OPS_z = z_score(f_OPS), f_tot_z = round((f_R_z + f_HR_z + f_RBI_z + f_SO_z + f_SB_z + f_OPS_z), 3), playerid = as.character(playerid)) %>% select(-Team) all_final <- inner_join(end_bat_z, bat_z, by = c("playerid", "Name")) drafted_final <- inner_join(end_bat_z, drafted, by = c("Name"))
I’m going to start by looking at who I drafted. How did the final z-scores differ from the projections?
drafted_final <- drafted_final %>% mutate(diff = f_tot_z - tot_z, change = case_when( diff < -1 ~ "underperform", diff > 1 ~ "outperform", TRUE ~ "as expected" ))
Let’s start by looking at those who outperformed their projections. Warning for those on mobile: these tables are wide, and you may not see all the relevant columns.
drafted_final %>% filter(change =="outperform") %>% select(Name, draft_order, R, f_R, HR, f_HR, RBI, f_RBI, SO, f_SO, SB, f_SB, OPS, f_OPS, tot_z, f_tot_z) %>% knitr::kable()
Betts blew his projections out of the water, hitting more home runs and increasing his OPS by quite a bit. Simmons showed much better plate discipline, striking out much less often, but his other categories didn’t dramatically improve. Piscotty did dramatically better than his projections in several different categories. Even Peraza, who had a negative z-score in his projections, ended up finishing the season on a high note, scoring more runs and hitting twice as many home runs as projected.
Now let’s look at the underperformers, who performed below their projections.
drafted_final %>% filter(change =="underperform") %>% select(Name, draft_order, R, f_R, HR, f_HR, RBI, f_RBI, SO, f_SO, SB, f_SB, OPS, f_OPS, tot_z, f_tot_z) %>% knitr::kable()
Posey, whom we’ve alreaady determined at this point was drafted too early and was a poor choice based on his projection, added salt to the wound by underperforming. I knew this even without looking at the stats, given his abysmal offensive season, but this confirms it.
Encarnacion scored fewer runs than his projections, but otherwise still provided good offensive numbers. Odor showed better plate discipline (fewer strikeouts), but his offensive output decreased dramatically. Jones also had a poor offensive year. Margot was a bad draft pick, who started out with poor projections and got even worse, as did Crawford. Belt improved his plate discipline, but his offensive numbers also tanked.
Now let’s look at those who performed as expected, whose z-scores changed by 1 or less.
drafted_final %>% filter(change =="as expected") %>% select(Name, draft_order, R, f_R, HR, f_HR, RBI, f_RBI, SO, f_SO, SB, f_SB, OPS, f_OPS, tot_z, f_tot_z) %>% knitr::kable()
Moustakas improved his RBIs, and got a little better at base stealing, but was otherwise pretty close to his projections. Kepler scored a few more runs, and was a bad pick to start with, but was at least consistent. Franco showed better plate discipline (fewer strikeouts), but was otherwise the same.
Not content to sit with the bad decisions I made, I engaged in some ill-advised counterfactual exploration, and looked to see what might have happened had I drafted the players with higher z-scores, rather than drafting based on WAR.
I looked at which players had higher projected z-scores than Encarnacion, and whether they were available at the time I drafted Encarnacion in the fifth round.
all_final %>% filter(position == 'first_base') %>% top_n(., 10, tot_z) %>% arrange(desc(tot_z)) %>% select(Name, R, f_R, HR, f_HR, RBI, f_RBI, SO, f_SO, SB, f_SB, OPS, f_OPS, tot_z, f_tot_z) %>% knitr::kable()
This makes me feel a little better – the first basemen with higher projected total_z scores were drafted prior to my turn in the fifth. Most of them underperformed their projections, but still did well, except for Joey Votto, whose runs scored, home runs, and runs batted in are much lower than projected, leading to his abysmal final z-score this year. Other first basemen who were projected to perform worse than Encarnacion also underperformed their projection. All things considered, Edwin wasn’t a bad draft pick.
I was really happy that I drafted Betts in the first round, given that he had high z-scores to begin with and then subsequently outperformed his projection. I looked at the next outfielder I drafted, who was Jones in the 12th round. Since all outfielders (except Brantley) with higher z-scores were drafted prior to my pick in the 12th round, I’ll exclude them from the table for simplicity’s sake.
all_final %>% filter(position == 'outfield' & tot_z <= 2.550) %>% top_n(., 10, tot_z) %>% arrange(desc(tot_z)) %>% select(Name, R, f_R, HR, f_HR, RBI, f_RBI, SO, f_SO, SB, f_SB, OPS, f_OPS, tot_z, f_tot_z) %>% knitr::kable()
Looking at both Brantley and Jones, Brantley was projected to do a bit better than Jones largely due to his low strikeout and high stolen bases projection. Jones was projected to handily beat Brantley in homeruns, and runs batted in. I probably selected Jones to boost my homerun numbers. But Jones had a pretty bad season, and Brantley outperformed his projections, so now I feel the pangs of regret. Both Mazara and Cain had already been drafted by the time I picked in the 12th round. Rosario had pretty similar projections to Jones, just with more strikeouts, and he also outperformed his projections.
The projections for these players aren’t that different from each other, so I’m kicking myself for not predicting the future, which is not constructive. I think the main message here is that I shouldn’t have waited this long to pick my other two outfielders. My third outfielder, Kepler, didn’t even break the top 30. Given what I know now about outfielders contributing to runs, homeruns, and RBIs, this is a huge shortcoming in my strategy.
I drafted Moustakas late, in round 11. In the table, I filtered out players with higher z-scores who had been selected in earlier rounds of the draft, and took the top five since there are fewer third basemen.
all_final %>% filter(position == 'third_base' & tot_z < 3.2) %>% top_n(., 5, tot_z) %>% arrange(desc(tot_z)) %>% select(Name, R, f_R, HR, f_HR, RBI, f_RBI, SO, f_SO, SB, f_SB, OPS, f_OPS, tot_z, f_tot_z) %>% knitr::kable()
By the time I had a chance to draft Moustakas in the 11th round, Beltre and Gallo were still available. Given how much Beltre underperformed his projection, it looks like I dodged a bullet. Gallo would have been a better option for runs and home runs, but his strikeouts are really quite high (even after a bit of discipline this year, he still had twice as many strikeouts as Moustakas).
Shaw would have been another reasonable option – his z-score was quite low due to his high projected number of strikeouts, but his projected runs, homeruns, and RBIs are comparable to Moustakas’s, and he was also projected to steal more bases.
Ultimately, Moustakas did just fine relative to his projection, and I could have done well with either Gallo or Shaw.
I did also end up drafting Franco in a later round, which seems reasonable given his projected z-score. He underperformed his projection, however, and didn’t contribute much to my offense.
All the players with higher projected total z-scores than Odor were already gone by the time I got picked up Odor in the 8th round.
all_final %>% filter(position == 'second_base' & tot_z < 3.9) %>% top_n(., 5, tot_z) %>% arrange(desc(tot_z)) %>% select(Name, R, f_R, HR, f_HR, RBI, f_RBI, SO, f_SO, SB, f_SB, OPS, f_OPS, tot_z, f_tot_z) %>% knitr::kable()
It’s worth noting here that none of the other second basemen would have been substantially better than Odor, based on the projections. Dee Gordon was projected to steal a lot more bases, but he was also projected to hit considerably fewer home runs, and bat in fewer runs. Additionally, none of these candidates outperformed their projections. Given that, I think Odor was the right choice here.
Posey was the first catcher to be drafted, so every catcher was available to me at the time.
all_final %>% filter(position == 'catcher') %>% top_n(., 10, tot_z) %>% arrange(desc(tot_z)) %>% select(Name, R, f_R, HR, f_HR, RBI, f_RBI, SO, f_SO, SB, f_SB, OPS, f_OPS, tot_z, f_tot_z) %>% knitr::kable()
I’ve spent the past few posts kicking myself for drafting Posey, but I’m not sure the numbers merit drafting any other catcher in round two. The two catchers with higher projected z-scores (Sanchez and Gattis) would have hit more home runs and batted in more runs, but their strikeouts were also nearly twice that of Posey. The clincher is that both also ended up underperforming their projections.
Looking at the top 10 catchers, only a few substantially outpeformed their projections. Given that there isn’t much variation in this group (namely, most of them are pretty bad), this is probably a good reason not to draft catchers in the second round.
As a bonus, I did a little bit of exploration in my own league, and I found that most people don’t draft catchers in the first 10 rounds, because catchers don’t seem to make much of a difference. More reasons not to draft catchers so early.
I drafted Simmons in the fourth round, early enough that most shortstops were still available. I excluded the three that had already been picked (Correa, Turner, and Lindor).
all_final %>% filter(position == 'short' & tot_z < 3) %>% top_n(., 10, tot_z) %>% arrange(desc(tot_z)) %>% select(Name, R, f_R, HR, f_HR, RBI, f_RBI, SO, f_SO, SB, f_SB, OPS, f_OPS, tot_z, f_tot_z) %>% knitr::kable()
I discussed Bogaerts and Andrus in the previous post, so I’ll start with them. Bogaerts was projected to score more runs and hit more home runs than Simmons, and also strike out many more times. In the end, Bogaerts outperfomed his projections, batting in 33% more runs than his projections, and hitting more home runs, compensating for his high strikeouts. Andrus had similar projections, except he was also projected to steal more bases. He underperformed, so in retrospect, I’m glad I dodged that bullet.
Given Simmons’s low projected z-score, there are plenty of shortstops I could have done better with. Story’s projections were great, and he did even better by the end, cutting his strikeouts and stealing plenty more bases. Baez was also projected to do better than Simmons in homeruns and RBIs, and he outperformed his projections too, stealing more bases than expected.
Even though Simmons ended up outperforming his projection, it was as a result of fewer strikeouts, rather than increased runs. I would have done better with any of the others shortstops I mentioned, underscoring how big of a mistake it was to draft Simmons as early as I did. Down with WAR.
Phew! That was a lot of analysis, and I applaud you if you stuck with me through this exploration. For those of you who skipped to the end, here are my top three takeaways from all this:
Next, I’ll be looking at my strategy for drafting pitchers, and breaking it apart in a similar fashion to see what can be improved for next year. I’ll also write about putting this all together to create a cohesive drafting strategy, since we draft pitchers and batters at the same time.
Hopefully this post has provided some food for thought, and sparked some strategies for building your fantasy team. If you have questions or comments, find me on twitter!