Problem: Coach Malone wants a top 5 defense.

Recommendations: Force teams to increase their average two point distance. This can be done in two ways; increase ratio of mid range to paint field goal attempts and force each individual shot to be taken from further out.

Decreasing opponent Three Point Field Goal Percentage would be another route but that stat is largely luck influenced and Nuggets were top 10 last year already.

Important Notes: To jump from 10th to 5th in Defensive Rating (NBA.com) the Nuggets would have to decrease their rating by 3 points, a feat accomplished by about 5 teams.

Publicly available defensive stats and ratings leave a lot to be desired, so I believe that the development of versatility in coverages and schemes that Malone mentioned is a higher priority than the end of the year ranking in a flawed stat.

Method: Pulled and cleaned data from basketball-reference to measure how teams changed in terms of defensive ratings between the 21 and 22 seasons. Identified the teams that improved the most, and than categorized them in terms of how the organization changed (ie coaching change). Pulled and cleaned data from pbpstats.com to than analyze how those teams made their improvement.

library(tidyverse)
library(rvest)
library(dplyr)
library(reactable)
library(plotly)
library(ggplot2)
library(reactablefmtr)
library(jsonlite)
library(gt)
#'22 def rating table
url_bball <- "https://www.basketball-reference.com/leagues/NBA_2022_ratings.html"
def_rat_22 <- url_bball %>%
  read_html() %>%
  html_elements("table") %>%
  html_table %>%
  .[[1]] 
names(def_rat_22) <- def_rat_22[1,]
def_rat_22 <- def_rat_22[-1,]
def_rat_22 <- def_rat_22 %>%
  select(Team,
         `DRtg/A`) %>%
  rename(Adj_DRate_22 = 'DRtg/A')
#'21 def rating table
url_21 <- "https://www.basketball-reference.com/leagues/NBA_2021_ratings.html"
def_rat_21 <- url_21 %>%
  read_html() %>%
  html_elements("table") %>%
  html_table %>%
  .[[1]] 
names(def_rat_21) <- def_rat_21[1,]
def_rat_21 <- def_rat_21[-1,]
def_rat_21 <- def_rat_21 %>%
  select(Team,
         `DRtg/A`) %>%
  rename(Adj_DRate_21 = 'DRtg/A')
#combining tables

def_rating <- right_join(def_rat_21,def_rat_22)
def_rating$Adj_DRate_21 <- as.numeric(def_rating$Adj_DRate_21)
def_rating$Adj_DRate_22 <- as.numeric(def_rating$Adj_DRate_22)
def_rating <- def_rating %>%
  mutate(difference = Adj_DRate_21 - Adj_DRate_22) %>%
  arrange(-difference) %>%
  mutate( change = case_when(
    difference > 0 ~ 'positive',
    TRUE ~ 'negative'
  )) %>%
  mutate_at(vars(difference), funs(round(.,4)))
def_rating %>% reactable(filterable = TRUE,
                         pagination = FALSE,
                         sortable = TRUE,
                         searchable = TRUE) %>%
  add_title("Defeneive Rating Improvements from '21 to '22")

Defeneive Rating Improvements from '21 to '22

plot_ly(def_rating, x = ~Team, y = ~difference, color = ~change) %>%
  add_bars() %>%
  layout(title = 'Difference in Def Rating 21 to 22')
signif_change <- def_rating %>%
  filter( change == "positive") %>%
  filter( difference > 1.5)
signif_change %>% 
  reactable(sortable = TRUE) %>% 
  add_title("Top Defensive Rating Improvements '21 to '22")

Top Defensive Rating Improvements '21 to '22

Suns

#pulling and cleaning data from pbp for 22 season
pbp_url_suns <- "https://api.pbpstats.com/get-totals/nba?Season=2021-22&SeasonType=Regular%2BSeason&StartType=All&Type=Opponent"
suns_json <- read_json(pbp_url_suns)
suns_22 <- suns_json[["multi_row_table_data"]] %>%
  bind_rows() %>%
  filter( Name == "PHX") %>%
   mutate( Year = 22) %>%
  select(Year,
         Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct) %>%
   mutate_at(vars(Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct), funs(round(.,4)))
#pulling and cleaning data from pbp for 21 season
pbp_url_suns_2 <- "https://api.pbpstats.com/get-totals/nba?Season=2020-21&SeasonType=Regular%2BSeason&StartType=All&Type=Opponent"
suns_json_2 <- read_json(pbp_url_suns_2)
suns_21 <- suns_json_2[["multi_row_table_data"]] %>%
  bind_rows() %>%
  filter( Name == "PHX") %>%
  mutate( Year = 21) %>%
  select(Year,
         Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct) %>%
  mutate( Year = 21) %>%
  mutate_at(vars(Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct), funs(round(.,4)))

#combining dataframes  
suns <- bind_rows(suns_22, suns_21)
reactable(suns,filterable = TRUE,
                         pagination = FALSE,
                         sortable = TRUE,
                         searchable = TRUE) %>%
  add_title("Suns Defensive Metrics for '21 to '22")

Suns Defensive Metrics for '21 to '22

suns[1, ] - suns[2, ] 
##   Year Avg3ptShotDistance Avg2ptShotDistance PenaltyPointsPct
## 1    1             0.0214             0.4367            0.001
##   SecondChancePointsPct   TsPct  EfgPct  Fg2Pct  Fg3Pct
## 1                0.0136 -0.0224 -0.0238 -0.0249 -0.0146

Above is the differences for the Suns in ’21 and ’22 seasons

Timberwolves

#timberwolves
pbp_url_suns <- "https://api.pbpstats.com/get-totals/nba?Season=2021-22&SeasonType=Regular%2BSeason&StartType=All&Type=Opponent"
suns_json <- read_json(pbp_url_suns)
wolves_22 <- suns_json[["multi_row_table_data"]] %>%
  bind_rows() %>%
  filter( Name == "MIN") %>%
   mutate( Year = 22) %>%
  select(Year,
         Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct) %>%
   mutate_at(vars(Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct), funs(round(.,4)))

pbp_url_suns_2 <- "https://api.pbpstats.com/get-totals/nba?Season=2020-21&SeasonType=Regular%2BSeason&StartType=All&Type=Opponent"
suns_json_2 <- read_json(pbp_url_suns_2)
wolves_21 <- suns_json_2[["multi_row_table_data"]] %>%
  bind_rows() %>%
  filter( Name == "MIN") %>%
  mutate( Year = 21) %>%
  select(Year,
         Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct) %>%
  mutate( Year = 21) %>%
  mutate_at(vars(Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct), funs(round(.,4)))
  
wolves <- bind_rows(wolves_22, wolves_21)
reactable(wolves,filterable = TRUE,
                         pagination = FALSE,
                         sortable = TRUE,
                         searchable = TRUE) %>%
  add_title("Wolves Defensive Metrics for '21 to '22")

Wolves Defensive Metrics for '21 to '22

wolves[1, ] - wolves[2, ] 
##   Year Avg3ptShotDistance Avg2ptShotDistance PenaltyPointsPct
## 1    1            -0.1149            -0.2122           0.0273
##   SecondChancePointsPct   TsPct  EfgPct Fg2Pct Fg3Pct
## 1                0.0019 -0.0182 -0.0204 0.0066 -0.042

Above is the differences for the Wolves in ’21 and ’22 seasons

Heat

#heat
pbp_url_suns <- "https://api.pbpstats.com/get-totals/nba?Season=2021-22&SeasonType=Regular%2BSeason&StartType=All&Type=Opponent"
suns_json <- read_json(pbp_url_suns)
heat_22 <- suns_json[["multi_row_table_data"]] %>%
  bind_rows() %>%
  filter( Name == "MIA") %>%
   mutate( Year = 22) %>%
  select(Year,
         Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct) %>%
   mutate_at(vars(Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct), funs(round(.,4)))

pbp_url_suns_2 <- "https://api.pbpstats.com/get-totals/nba?Season=2020-21&SeasonType=Regular%2BSeason&StartType=All&Type=Opponent"
suns_json_2 <- read_json(pbp_url_suns_2)
heat_21 <- suns_json_2[["multi_row_table_data"]] %>%
  bind_rows() %>%
  filter( Name == "MIA") %>%
  mutate( Year = 21) %>%
  select(Year,
         Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct) %>%
  mutate( Year = 21) %>%
  mutate_at(vars(Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct), funs(round(.,4)))
  
heat <- bind_rows(heat_22, heat_21)
reactable(heat,filterable = TRUE,
                         pagination = FALSE,
                         sortable = TRUE,
                         searchable = TRUE) %>%
  add_title("Heat Defensive Metrics for '21 to '22")

Heat Defensive Metrics for '21 to '22

heat[1, ] - heat[2, ] 
##   Year Avg3ptShotDistance Avg2ptShotDistance PenaltyPointsPct
## 1    1             0.0123             0.2841           0.0303
##   SecondChancePointsPct   TsPct  EfgPct Fg2Pct  Fg3Pct
## 1                0.0067 -0.0142 -0.0181 -7e-04 -0.0259

Above is the differences for the Heat in ’21 and ’22 seasons

Kings

#kings
pbp_url_suns <- "https://api.pbpstats.com/get-totals/nba?Season=2021-22&SeasonType=Regular%2BSeason&StartType=All&Type=Opponent"
suns_json <- read_json(pbp_url_suns)
kings_22 <- suns_json[["multi_row_table_data"]] %>%
  bind_rows() %>%
  filter( Name == "SAC") %>%
   mutate( Year = 22) %>%
  select(Year,
         Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct) %>%
   mutate_at(vars(Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct), funs(round(.,4)))

pbp_url_suns_2 <- "https://api.pbpstats.com/get-totals/nba?Season=2020-21&SeasonType=Regular%2BSeason&StartType=All&Type=Opponent"

suns_json_2 <- read_json(pbp_url_suns_2)
kings_21 <- suns_json_2[["multi_row_table_data"]] %>%
  bind_rows() %>%
  filter( Name == "SAC") %>%
  mutate( Year = 21) %>%
  select(Year,
         Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct) %>%
  mutate( Year = 21) %>%
  mutate_at(vars(Avg3ptShotDistance,
         Avg2ptShotDistance,
         PenaltyPointsPct,
         SecondChancePointsPct,
         TsPct,
         EfgPct,
         Fg2Pct,
         Fg3Pct), funs(round(.,4)))
  
kings <- bind_rows(kings_22, kings_21)
reactable(kings,filterable = TRUE,
                         pagination = FALSE,
                         sortable = TRUE,
                         searchable = TRUE) %>%
  add_title("Kings Defensive Metrics for '21 to '22")

Kings Defensive Metrics for '21 to '22

kings[1, ] - kings[2, ] 
##   Year Avg3ptShotDistance Avg2ptShotDistance PenaltyPointsPct
## 1    1             0.0849             0.0668          -0.0181
##   SecondChancePointsPct   TsPct  EfgPct  Fg2Pct  Fg3Pct
## 1                0.0089 -0.0089 -0.0059 -0.0025 -0.0083

Above is the differences for the Kings in ’21 and ’22 seasons

sessionInfo()
## R version 4.1.0 (2021-05-18)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Big Sur 10.16
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRblas.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] gt_0.3.1            jsonlite_1.7.2      reactablefmtr_2.0.0
##  [4] plotly_4.10.0       reactable_0.3.0     rvest_1.0.2        
##  [7] forcats_0.5.1       stringr_1.4.0       dplyr_1.0.7        
## [10] purrr_0.3.4         readr_2.0.1         tidyr_1.2.0        
## [13] tibble_3.1.4        ggplot2_3.3.5       tidyverse_1.3.1    
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_1.0.7         lubridate_1.7.10   assertthat_0.2.1   digest_0.6.27     
##  [5] utf8_1.2.2         reactR_0.4.4       R6_2.5.1           cellranger_1.1.0  
##  [9] backports_1.2.1    reprex_2.0.0       evaluate_0.14      httr_1.4.2        
## [13] pillar_1.6.2       rlang_0.4.11       curl_4.3.2         lazyeval_0.2.2    
## [17] readxl_1.3.1       rstudioapi_0.13    data.table_1.14.0  jquerylib_0.1.4   
## [21] rmarkdown_2.14     selectr_0.4-2      htmlwidgets_1.5.4  munsell_0.5.0     
## [25] broom_0.7.8        compiler_4.1.0     modelr_0.1.8       xfun_0.31         
## [29] pkgconfig_2.0.3    htmltools_0.5.2    tidyselect_1.1.1   fansi_0.5.0       
## [33] viridisLite_0.4.0  crayon_1.4.1       tzdb_0.1.2         dbplyr_2.1.1      
## [37] withr_2.4.2        grid_4.1.0         gtable_0.3.0       lifecycle_1.0.0   
## [41] DBI_1.1.1          magrittr_2.0.1     scales_1.1.1       cli_3.0.1         
## [45] stringi_1.7.4      farver_2.1.0       fs_1.5.0           xml2_1.3.2        
## [49] bslib_0.3.1        ellipsis_0.3.2     generics_0.1.0     vctrs_0.3.8       
## [53] RColorBrewer_1.1-2 tools_4.1.0        glue_1.4.2         crosstalk_1.2.0   
## [57] hms_1.1.0          fastmap_1.1.0      yaml_2.2.1         colorspace_2.0-2  
## [61] knitr_1.33         haven_2.4.1        sass_0.4.0