The purpose of this project was to predict which current NBA players will get their jersey numbers retired in the future. Historic NBA player statistics were analyzed and used to forecast future outcomes. Rather than include excessive amount of predictors, the best subset was chosen via statistical methods.

library(Hmisc)
library(tab)
library(knitr)
library(kableExtra)
library(tidyverse)
library(caret)
library(caTools)
select <- dplyr::select

The Data

There are two separate data sets that are considered throughout the project.

  1. Team Data: If a player played at least 3 seasons with more than one team, the Career Data is split by each of such teams. For instance, Kareem Abdul-Jabbar played for the LA Lakers and Milwaukee Bucks, so he will occupy two rows in the Team Data.
  2. Career Data: Statistics of each player’s entire career. Accordingly, each player occupies only one row.
teamAvgs1 <- read_csv("Team_Avgs")
totalAvgs1 <- read_csv("Total_Avgs")
teamHistory <- read_csv("TeamSummaries.csv")
outliers <- teamAvgs1 %>%
  filter(nSeasons < 3 & Jersey_Retired == 1)
teamAvgs <- teamAvgs1 %>%
  filter(nSeasons > 2) %>% 
  mutate(Total_Titles = replace_na(Total_Titles, 0), Team_Titles = replace_na(Team_Titles, 0)) 
totalAvgs <- totalAvgs1 %>%
  filter(Player %in% teamAvgs1$Player) %>% 
  mutate(Total_Titles = replace_na(Total_Titles, 0))
CareerVarsXy <- c("Total_Titles", "AllStar_Selections", "nSeasons", "GP", "GS", "PPG", "TOV", "PER", "USGp", "WS", "BPM", "VORP", "Jersey_Retired")
TeamVarsXy <- c("Team_Titles", "nSeasons", "GP", "GS", "PPG", "TOV", "PER", "USGp", "WS", "BPM", "VORP", "Jersey_Retired")

Training and Validation Sets

testSetTotal <- totalAvgs %>%
  filter(Seasons %in% grep(pattern = "2017$", x = totalAvgs$Seasons, ignore.case = TRUE, value = T))
testSetTeam <- teamAvgs %>%
  filter(Player %in% testSetTotal$Player)
nonTestTotal <- totalAvgs %>%
  filter(Player %nin% testSetTotal$Player)
nonTestTeam <- teamAvgs %>%
  filter(Player %nin% testSetTeam$Player)
set.seed(9711)
sampleSplit <- sample.split(Y = nonTestTotal$Jersey_Retired, SplitRatio = 0.7)
trainSetTotal <- subset(x = nonTestTotal, sampleSplit == TRUE)
valSetTotal <- subset(x = nonTestTotal, sampleSplit == FALSE)
trainSetTeam <- teamAvgs %>%
  filter(Player %in% trainSetTotal$Player)
valSetTeam <- teamAvgs %>%
  filter(Player %in% valSetTotal$Player)

Best Subset Model (Training Set)

library(leaps)
library(bestglm)
select <- dplyr::select
trainTotal_Xy <- trainSetTotal %>%
  select(all_of(CareerVarsXy))
set.seed(11)
trainTotalBestGLM <- bestglm(Xy = as.data.frame(trainTotal_Xy), family = binomial, IC = "BIC", method = "forward")
trainTeam_Xy <- trainSetTeam %>%
  select(all_of(TeamVarsXy))
set.seed(11)
trainTeamBestGLM <- bestglm(Xy = as.data.frame(trainTeam_Xy), family = binomial, IC = "BIC", method = "forward")

Performance on Training Sets

trainTotalPreds <- ifelse(trainTotalBestGLM$BestModel$fitted.values > 0.5, 1, 0)
trainTeamPreds <- ifelse(trainTeamBestGLM$BestModel$fitted.values > 0.5, 1, 0)
Table1A <-  as_tibble(table(Predictions = trainTotalPreds, `True Outcome` = trainSetTotal$Jersey_Retired, deparse.level = 2)) %>%
  kbl(booktabs = F, align = "c", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>% #, position = "float_left") %>%
  add_header_above(c("Table 1A: Career Model" = 3), bold = T, font_size = 12,  monospace = T, background = "#0047AB", color = "white") %>%
  row_spec(0, bold = T, monospace = T,  background = "cyan") %>%
  row_spec(1:4, bold = T, monospace = T) %>%
  # column_spec(c(1,3), border_left = T, border_right = T) %>%
  footnote(general = paste("Train Set Accuracy = ", round(mean(trainTotalPreds == trainSetTotal$Jersey_Retired), digits = 4) * 100, "%"), general_title = "")
Table1A
Table 1A: Career Model
Predictions True Outcome n
0 0 621
1 0 6
0 1 22
1 1 31
Train Set Accuracy = 95.88 %
Table1B <- as_tibble(table(Predictions = trainTeamPreds, `True Outcome` = trainSetTeam$Jersey_Retired, deparse.level = 2)) %>%
  kbl(booktabs = F, align = "c", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>% #, position = "float_right") %>%
  add_header_above(c("Table 2A: Team Model" = 3), bold = T, font_size = 12,  monospace = T, background = "#0047AB", color = "white") %>%
  row_spec(0, bold = T, monospace = T,  background = "cyan") %>%
  row_spec(1:4, bold = T, monospace = T) %>%
  # column_spec(c(1,3), border_left = T, border_right = T) %>%
  footnote(general = paste("Train Set Accuracy = ", round(mean(trainTeamPreds == trainSetTeam$Jersey_Retired), digits = 4) * 100, "%"), general_title = "")
Table1B
Table 2A: Team Model
Predictions True Outcome n
0 0 954
1 0 9
0 1 32
1 1 22
Train Set Accuracy = 95.97 %

Performance on Validation Sets

valSaveTotalPreds <- predict(trainTotalBestGLM$BestModel, newdata = valSetTotal, type = 'response')
valTotalPreds <- ifelse(valSaveTotalPreds > 0.5, 1, 0)
valSaveTeamPreds <- predict(trainTeamBestGLM$BestModel, newdata = valSetTeam, type = 'response')
valTeamPreds <- ifelse(valSaveTeamPreds > 0.5, 1, 0)
Table2A <- as_tibble(table(Predictions = valTotalPreds, `True Outcome` = valSetTotal$Jersey_Retired, deparse.level = 2)) %>%
  kbl(booktabs = F, align = "c", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"), 
                full_width = F, font_size = 10) %>% #, position = "float_left") %>%
  add_header_above(c("Table 2A: Career Model" = 3), bold = T, font_size = 12,  monospace = T, background = "#0047AB", color = "white") %>%
  row_spec(0, bold = T, monospace = T,  background = "cyan") %>%
  row_spec(1:4, bold = T, monospace = T) %>%
  # column_spec(c(1,3), border_left = T, border_right = T) %>%
  footnote(general = paste("Validation Set Accuracy = ", round(mean(valTotalPreds == valSetTotal$Jersey_Retired), digits = 4) * 100, "%"), general_title = "")
Table2A
Table 2A: Career Model
Predictions True Outcome n
0 0 264
1 0 5
0 1 13
1 1 10
Validation Set Accuracy = 93.84 %
Table2B <- as_tibble(table(Predictions = valTeamPreds, `True Outcome` = valSetTeam$Jersey_Retired, deparse.level = 2)) %>%
  kbl(booktabs = F, align = "c", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>% #, position = "float_right") %>%
  add_header_above(c("Table 2B: Team Model" = 3), bold = T, font_size = 12,  monospace = T, background = "#0047AB", color = "white") %>%
  row_spec(0, bold = T, monospace = T,  background = "cyan") %>%
  row_spec(1:4, bold = T, monospace = T) %>%
  # column_spec(c(1,3), border_left = T, border_right = T) %>%
  footnote(general = paste("Validation Set Accuracy = ", round(mean(valTeamPreds == valSetTeam$Jersey_Retired), digits = 4) * 100, "%"), general_title = "")
Table2B
Table 2B: Team Model
Predictions True Outcome n
0 0 376
1 0 7
0 1 14
1 1 9
Validation Set Accuracy = 94.83 %

Combined (Training + Validation) & Known Test Set

Known Test Set contains all recently retired players who outcomes are known.

RecentPlayers <- read_csv("CurrentPlayers.csv")
KnownCareerTestSet <- testSetTotal %>%
  filter(Player %nin% RecentPlayers$Player)
UnknownCareerTestSet <- testSetTotal %>%
  filter(Player %in% RecentPlayers$Player)
KnownTestCareerXy <- KnownCareerTestSet %>%
  select(all_of(CareerVarsXy))
UnknownTestCareerXy <- UnknownCareerTestSet %>%
  select(all_of(CareerVarsXy))
KnownTeamTestSet <- testSetTeam %>%
  filter(Player %nin% RecentPlayers$Player)
UnknownTeamTestSet <- testSetTeam %>%
  filter(Player %in% RecentPlayers$Player)
KnownTestTeamXy <- KnownTeamTestSet %>%
  select(all_of(TeamVarsXy))
UnknownTestTeamXy <- UnknownTeamTestSet %>%
  select(all_of(TeamVarsXy))
CombinedCareerXy <- nonTestTotal %>%
  select(all_of(CareerVarsXy))

Best Subet (Combined Set)

set.seed(11)
BestOverallCareerGLM <- bestglm(Xy = as.data.frame(CombinedCareerXy), family = binomial, IC = "BIC", method = "forward")
CombinedTeamXy <- nonTestTeam %>%
  select(all_of(TeamVarsXy))
set.seed(11)
BestOverallTeamGLM <- bestglm(Xy = as.data.frame(CombinedTeamXy), family = binomial, IC = "BIC", method = "forward")

Performance on Combined Sets

FinalCareerPreds <- ifelse(BestOverallCareerGLM$BestModel$fitted.values > 0.5, 1, 0)
FullTeamPreds <- ifelse(BestOverallTeamGLM$BestModel$fitted.values > 0.5, 1, 0)
Table3A <- as_tibble(table(Predictions = FinalCareerPreds, `True Outcome` = CombinedCareerXy$Jersey_Retired, deparse.level = 2)) %>%
  kbl(booktabs = F, align = "c", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>% #, position = "float_left") %>%
  add_header_above(c("Table 3A: Career Model" = 3), bold = T, font_size = 12,  monospace = T, background = "#0047AB", color = "white") %>%
  row_spec(0, bold = T, monospace = T,  background = "cyan") %>%
  row_spec(1:4, bold = T, monospace = T) %>%
  # column_spec(1:3, border_left = T, border_right = T) %>%
  footnote(general = paste("Combined Train Set Accuracy = ", round(mean(FinalCareerPreds == CombinedCareerXy$Jersey_Retired), digits = 4) * 100, "%"), general_title = "")
Table3A
Table 3A: Career Model
Predictions True Outcome n
0 0 885
1 0 11
0 1 36
1 1 40
Combined Train Set Accuracy = 95.16 %
Table3B <- as_tibble(table(Predictions = FullTeamPreds, `True Outcome` = CombinedTeamXy$Jersey_Retired, deparse.level = 2)) %>%
  kbl(booktabs = F, align = "c", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>% #, position = "float_right") %>%
  add_header_above(c("Table 3B: Team Model" = 3), bold = T, font_size = 12,  monospace = T, background = "#0047AB", color = "white") %>%
  row_spec(0, bold = T, monospace = T,  background = "cyan") %>%
  row_spec(1:4, bold = T, monospace = T) %>%
  # column_spec(1:3, border_left = T, border_right = T) %>%
  footnote(general = paste("Combined Train Set Accuracy = ", round(mean(FullTeamPreds == CombinedTeamXy$Jersey_Retired), digits = 4) * 100, "%"), general_title = "")
Table3B
Table 3B: Team Model
Predictions True Outcome n
0 0 1334
1 0 12
0 1 46
1 1 31
Combined Train Set Accuracy = 95.92 %

Performance on Known Test Sets

KnownSetSaveCareerPreds <- predict(BestOverallCareerGLM$BestModel, newdata = KnownTestCareerXy, type = 'response')
KnownSetCareerPreds <- ifelse(KnownSetSaveCareerPreds > 0.5, 1, 0)

KnownTestTeamSavePreds <- predict(BestOverallTeamGLM$BestModel, newdata = KnownTestTeamXy, type = 'response')
KnownTestTeamPreds <- ifelse(KnownTestTeamSavePreds > 0.5, 1, 0)
Table4A <- as_tibble(table(Predictions = KnownSetCareerPreds, `True Outcome` = KnownTestCareerXy$Jersey_Retired, deparse.level = 2)) %>%
  kbl(booktabs = F, align = "c", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>% #, position = "float_left") %>%
  add_header_above(c("Table 4A: Career Model" = 3), bold = T, font_size = 12,  monospace = T, background = "#0047AB", color = "white") %>%
  row_spec(0, bold = T, monospace = T,  background = "cyan") %>%
  row_spec(1:4, bold = T, monospace = T) %>%
  # column_spec(1:3, border_left = T, border_right = T) %>%
  footnote(general = paste("Known Test Set Accuracy = ", round(mean(KnownSetCareerPreds == KnownTestCareerXy$Jersey_Retired), digits = 4) * 100, "%"), general_title = "")
Table4A
Table 4A: Career Model
Predictions True Outcome n
0 0 89
1 0 1
0 1 4
1 1 2
Known Test Set Accuracy = 94.79 %
Table4B <- as_tibble(table(Predictions = KnownTestTeamPreds, `True Outcome` = KnownTestTeamXy$Jersey_Retired, deparse.level = 2)) %>%
  kbl(booktabs = F, align = "c", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>% #, position = "float_right") %>%
  add_header_above(c("Table 4B: Team Model" = 3), bold = T, font_size = 12,  monospace = T, background = "#0047AB", color = "white") %>%
  row_spec(0, bold = T, monospace = T,  background = "cyan") %>%
  row_spec(1:4, bold = T, monospace = T) %>%
  # column_spec(1:3, border_left = T, border_right = T) %>%
  footnote(general = paste("Known Test Set Accuracy = ", round(mean(KnownTestTeamPreds == KnownTestTeamXy$Jersey_Retired), digits = 4) * 100, "%"), general_title = "")
Table4B
Table 4B: Team Model
Predictions True Outcome n
0 0 140
1 0 1
0 1 4
1 1 2
Known Test Set Accuracy = 96.6 %

(Outcomes) Unknown Test Set

Note, the test set contains players who are either currently in the NBA or recently retired.

UnknownTestCareerSavePreds <- predict(BestOverallCareerGLM$BestModel, newdata = UnknownTestCareerXy, type = 'response')
UnknownCareerTestSet$Predictions <- ifelse(UnknownTestCareerSavePreds > 0.5, 1, 0)

# BestOverallCareerGLM$BestModel$coefficients
CareerUnknownPredsDF <- UnknownCareerTestSet %>%
  select(Player, AllStar_Selections, WS, Predictions) %>%
  mutate(Predictions = recode_factor(Predictions, `0` = "NO", `1` =  "YES"))
UnknownTestTeamSavePreds <- predict(BestOverallTeamGLM$BestModel, newdata = UnknownTestTeamXy, type = 'response')
UnknownTeamTestSet$Predictions <- ifelse(UnknownTestTeamSavePreds > 0.5, 1, 0)

# BestOverallTeamGLM$BestModel$coefficients
TeamUnknownPredsDF <- UnknownTeamTestSet %>%
  select(Player, Team, nSeasons, WS, Predictions) %>%
  mutate(Predictions = recode_factor(Predictions, `0` = "NO", `1` =  "YES"))

Decision Boundary Plots

CareerUnknownPlot <- ggplot(data = CareerUnknownPredsDF, aes(y = WS, x = AllStar_Selections)) +
  geom_point(aes(color = as_factor(Predictions)), size = 3) +
  ggtitle("Combined Career Model: Unknown Test Set") +
  geom_abline(slope = -0.249 / 0.814, intercept = 6.176 / 0.814) +
  geom_text(label="Decision Boundary", x=11.8, y=3.9, vjust=-1, angle = -15, color = "darkgreen", size = 4.3) +
  ylab("Average Win Shares") + xlab("Number of Times an All-Star") + 
  labs(color = "Jersey Retired") +
  theme_grey() +
  theme(plot.title = element_text(size = 15, hjust = 0.5, color = "darkgreen", face = "bold"),
        axis.title = element_text(size = 14, face = "italic"), 
        axis.text = element_text(size = 13), 
        legend.background = element_rect(colour = "black"),
        # legend.key.height = unit(1, "cm"),
        legend.title = element_text(size=11, face="bold", hjust = 0.5),
        legend.text = element_text(size=10),
        legend.position = c(0.18, 0.84))
CareerUnknownPlot

TeamUnknownPlot <- ggplot(data = TeamUnknownPredsDF, aes(y = WS, x = nSeasons)) +
  geom_point(aes(color = as_factor(Predictions)), size = 3) +
  ggtitle("Combined Team Model: Unknown Test Set") +
  geom_abline(slope = -0.457 / 0.672, intercept = 9.219 / 0.672) +
  geom_text(label="Decision Boundary", x=12.5, y=5.1, vjust=-1, angle = -22, color = "purple", size = 4.3) +
  ylab("Average Win Shares") + xlab("Number of Seasons with Team") + 
  labs(color = "Jersey Retired") +
  theme_grey() +
  theme(plot.title = element_text(size = 15, hjust = 0.5, color = "purple", face = "bold"),
        axis.title = element_text(size = 14, face = "italic"), 
        axis.text = element_text(size = 13), 
        legend.background = element_rect(colour = "black"),
        # legend.key.height = unit(1, "cm"),
        legend.title = element_text(size=11, face="bold", hjust = 0.5),
        legend.text = element_text(size=10),
        legend.position = c(0.86, 0.84))
TeamUnknownPlot

# cowplot::plot_grid(CareerUnknownPlot, TeamUnknownPlot)

Final Predictions (Jersey Retired = Yes)

FutureYesCareer <- CareerUnknownPredsDF %>%
  filter(Predictions == "YES") %>%
  rename("All-Star" = AllStar_Selections) %>%
  arrange(desc(-6.176 + 0.814 * WS + 0.249 * `All-Star`))

FutureYesTeam <- TeamUnknownPredsDF %>%
  filter(Predictions == "YES") %>%
  rename("Seasons" = nSeasons) %>%
  arrange(desc(-9.219 + 0.457 * Seasons + 0.672 * WS))
Table5A <- FutureYesCareer[1:14, 1:3] %>%
  kbl(booktabs = F, align = "l", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>% #, position = "float_left") %>%
  add_header_above(c("Career Model: Unknown Test Set" = 3), bold = T, monospace = T, background = "#0047AB", color = "white", font_size = 12) %>%
  row_spec(0, bold = T, monospace = T,  background = "cyan") %>%
  row_spec(1:14, bold = T, monospace = T) #%>%
  # footnote(general = c("Remaining players not listed: ", toString(FutureYesCareer$Player[15:17]), toString(FutureYesCareer$Player[18:20]), toString(FutureYesCareer$Player[21:23]), toString(FutureYesCareer$Player[24:25]), toString(FutureYesCareer$Player[26:27]), toString(FutureYesCareer$Player[28:29]), toString(FutureYesCareer$Player[30:31])), general_title = "")
Table5A
Career Model: Unknown Test Set
Player All-Star WS
LeBron James 17 14.7
Chris Paul 11 12.9
Kevin Durant 11 12.0
Dirk Nowitzki 14 10.6
James Harden 9 11.4
Stephen Curry 7 10.5
Dwyane Wade 13 8.4
Anthony Davis 8 9.7
Dwight Howard 8 9.4
Russell Westbrook 9 8.9
Damian Lillard 6 9.1
Blake Griffin 6 8.9
Kawhi Leonard 5 9.2
LaMarcus Aldridge 7 7.9
Table5B <- FutureYesTeam[1:4] %>%
  kbl(booktabs = F, align = "l", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>%
  row_spec(1:14, bold = T, monospace = T) %>%
  add_header_above(c("Team Model: Unknown Test Set" = 4), bold = T, monospace = T, background = "#0047AB", color = "white", font_size = 12) %>%
  row_spec(0, bold = T, monospace = T,  background = "cyan") 
Table5B
Team Model: Unknown Test Set
Player Team Seasons WS
Dirk Nowitzki DAL 19 10.6
LeBron James CLE 10 14.0
LeBron James MIA 4 16.3
Kevin Durant OKC 9 12.0
Tony Parker SAS 16 6.8
Dwyane Wade MIA 13 8.8
James Harden HOU 5 14.1
Chris Paul LAC 6 13.0
Chris Paul NOP 6 12.8
Dwight Howard ORL 8 10.9
Stephen Curry GSW 8 10.5
Russell Westbrook OKC 9 8.9
LaMarcus Aldridge POR 9 7.7
Marc Gasol MEM 9 7.7

Final Predictions (Jersey Retired = No)

The tables below only show the players that were closest to the decision boundary. In other words, these players just missed the cutoff point.

FutureNoCareer <- CareerUnknownPredsDF %>%
  filter(Predictions == "NO", AllStar_Selections > 1) %>%
  rename("All-Star" = AllStar_Selections) %>%
  arrange(desc(-6.176 + 0.814 * WS + 0.249 * `All-Star`))

FutureNoTeam <- TeamUnknownPredsDF %>%
  filter(Predictions == "NO") %>%
  rename("Seasons" = nSeasons) %>%
  arrange(desc(-9.219 + 0.457 * Seasons + 0.672 * WS))
Table6A <- FutureNoCareer[1:3] %>%
  kbl(booktabs = F, align = "l", linesep = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>% #, position = "float_left") %>%
  row_spec(0, bold = T, monospace = T, background = "#D73B3E", color = "white") %>%
  row_spec(1:13, bold = T, monospace = T) %>%
  add_header_above(c("Career Model" = 3), bold = T, monospace = T, background = "#800000", color = "white", font_size = 12) #%>%
  # column_spec(c(1,3), border_left = T, border_right = T)
Table6A
Career Model
Player All-Star WS
Draymond Green 3 6.6
John Wall 5 5.8
Isaiah Thomas 2 6.5
DeMar DeRozan 4 5.6
Kemba Walker 4 5.4
Joakim Noah 2 6.0
DeMarcus Cousins 4 4.6
Luol Deng 2 5.2
Rajon Rondo 4 4.4
Bradley Beal 3 4.4
Derrick Rose 3 4.3
Khris Middleton 2 3.6
Victor Oladipo 2 3.4
Table6B <- FutureNoTeam[c(1:12,52), 1:4] %>%
  kbl(booktabs = F, align = "l", linesep = "", midrule = "") %>%
  kable_classic("striped") %>%
  kable_styling(latex_options = c("striped", "HOLD_position"), bootstrap_options = c("condensed", "bordered"),
                full_width = F, font_size = 10) %>%
  row_spec(0, bold = T, monospace = T, background = "#D73B3E", color = "white") %>%
  row_spec(1:13, bold = T, monospace = T) %>%
  add_header_above(c("Team Model" = 4), bold = T, monospace = T, background = "#800000", color = "white", font_size = 12)
Table6B
Team Model
Player Team Seasons WS
Blake Griffin LAC 7 8.9
DeAndre Jordan LAC 9 7.4
Kawhi Leonard SAS 6 9.2
Pau Gasol LAL 7 8.5
Al Horford ATL 9 7.1
Andre Iguodala PHI 8 7.7
Udonis Haslem MIA 14 3.6
Mike Conley MEM 10 6.3
Anthony Davis NOP 5 9.7
Luol Deng CHI 10 5.9
Kyle Lowry TOR 5 9.2
Joakim Noah CHI 9 6.4
Giannis Antetokounmpo MIL 4 6.7