Problem/Question: Is Bruce Brown a threat from 3pt range?

Results: While he did shoot 40% from 3pt range last year, it was largely due to a extremely hot streak from the right corner. Post All-Star break he shot 76% from the right corner compared to 30% pre All-Star break and 19% in the ’21 season. He also shot better from above the arc but shot worse from the left corner and mid range, suggesting that the uptick is not due to an internal factor like an adjustment to mechanics or mental. There was also only a slight uptick in number of attempts (17 post all star, 10 pre and 21 the year prior).

All the evidence suggests that the increase in 3pt% was due to variance magnified by a small sample size.

library(ggplot2)
library(tidyverse)
library(nbastatR)
library(devtools)
library(extrafont)
library(cowplot)
library(dplyr)
library(plotly)
library(lubridate)
library(reactable)
library(reactablefmtr)
library(rpubs)

Sys.setenv("VROOM_CONNECTION_SIZE" = 131072 * 2)
#court
circle_points = function(center = c(0, 0), radius = 1, npoints = 360) {
  angles = seq(0, 2 * pi, length.out = npoints)
  return(data_frame(x = center[1] + radius * cos(angles),
                    y = center[2] + radius * sin(angles)))
}

width = 50
height = 94 / 2
key_height = 19
inner_key_width = 12
outer_key_width = 16
backboard_width = 6
backboard_offset = 4
neck_length = 0.5
hoop_radius = 0.75
hoop_center_y = backboard_offset + neck_length + hoop_radius
three_point_radius = 23.75
three_point_side_radius = 22
three_point_side_height = 14

court_themes = list(
  light = list(
    court = 'floralwhite',
    lines = '#999999',
    text = '#222222',
    made = '#00bfc4',
    missed = '#f8766d',
    hex_border_size = 1,
    hex_border_color = "#000000"
  ),
  dark = list(
    court = '#000004',
    lines = '#999999',
    text = '#f0f0f0',
    made = '#00bfc4',
    missed = '#f8766d',
    hex_border_size = 0,
    hex_border_color = "#000000"
  )
)


plot_court = function(court_theme = court_themes$light, use_short_three = FALSE) {
  if (use_short_three) {
    three_point_radius = 22
    three_point_side_height = 0
  }
  
  court_points = data_frame(
    x = c(width / 2, width / 2, -width / 2, -width / 2, width / 2),
    y = c(height, 0, 0, height, height),
    desc = "perimeter"
  )
  
  court_points = bind_rows(court_points , data_frame(
    x = c(outer_key_width / 2, outer_key_width / 2, -outer_key_width / 2, -outer_key_width / 2),
    y = c(0, key_height, key_height, 0),
    desc = "outer_key"
  ))
  
  court_points = bind_rows(court_points , data_frame(
    x = c(-backboard_width / 2, backboard_width / 2),
    y = c(backboard_offset, backboard_offset),
    desc = "backboard"
  ))
  
  court_points = bind_rows(court_points , data_frame(
    x = c(0, 0), y = c(backboard_offset, backboard_offset + neck_length), desc = "neck"
  ))
  
  foul_circle = circle_points(center = c(0, key_height), radius = inner_key_width / 2)
  
  foul_circle_top = filter(foul_circle, y > key_height) %>%
    mutate(desc = "foul_circle_top")
  
  foul_circle_bottom = filter(foul_circle, y < key_height) %>%
    mutate(
      angle = atan((y - key_height) / x) * 180 / pi,
      angle_group = floor((angle - 5.625) / 11.25),
      desc = paste0("foul_circle_bottom_", angle_group)
    ) %>%
    filter(angle_group %% 2 == 0) %>%
    select(x, y, desc)
  
  hoop = circle_points(center = c(0, hoop_center_y), radius = hoop_radius) %>%
    mutate(desc = "hoop")
  
  restricted = circle_points(center = c(0, hoop_center_y), radius = 4) %>%
    filter(y >= hoop_center_y) %>%
    mutate(desc = "restricted")
  
  three_point_circle = circle_points(center = c(0, hoop_center_y), radius = three_point_radius) %>%
    filter(y >= three_point_side_height, y >= hoop_center_y)
  
  three_point_line = data_frame(
    x = c(three_point_side_radius, three_point_side_radius, three_point_circle$x, -three_point_side_radius, -three_point_side_radius),
    y = c(0, three_point_side_height, three_point_circle$y, three_point_side_height, 0),
    desc = "three_point_line"
  )
  
  court_points = bind_rows(
    court_points,
    foul_circle_top,
    foul_circle_bottom,
    hoop,
    restricted,
    three_point_line
  )
  
  
  court_points <- court_points
  
  ggplot() +
    geom_path(
      data = court_points,
      aes(x = x, y = y, group = desc),
      color = court_theme$lines
    ) +
    coord_fixed(ylim = c(0, 45), xlim = c(-25, 25)) +
    theme_minimal(base_size = 22) +
    theme(
      text = element_text(color = court_theme$text),
      plot.background = element_rect(fill = 'floralwhite', color = 'floralwhite'),
      panel.background = element_rect(fill = court_theme$court, color = court_theme$court),
      panel.grid = element_blank(),
      panel.border = element_blank(),
      axis.text = element_blank(),
      axis.title = element_blank(),
      axis.ticks = element_blank(),
      legend.background = element_rect(fill = court_theme$court, color = court_theme$court),
      legend.position = "bottom",
      legend.key = element_blank(),
      legend.text = element_text(size = rel(1.0))
    )
}
team <- "Brooklyn Nets"
player <- "Bruce Brown"
#pull data
df <- teams_shots(teams = team, seasons = 2022) %>%
  filter(namePlayer == player) %>%
  mutate( x = as.numeric(as.character(locationX))/10, y = as.numeric(as.character(locationY))/10 + hoop_center_y)
## Brooklyn Nets 2021-22 shot data
df$x <- df$x * -1

df$dateGame <- ymd(df$dateGame)
df_pre <- df %>%
  filter( dateGame < '2022-2-20')

#Pre All Star break shot chart and table

shot_chart_1 <- plot_court() + 
  geom_point( data = df_pre, aes( x = x , y = y,
                                    color = isShotMade,
                                    fill = isShotMade),
              size = 2, shape = 21, stroke = .2) +
  scale_color_manual( values = c("green4", "red3"), 
                      aesthetics = "color",
                      breaks = c("TRUE", "FALSE"),
                      labels = c("Made", "Missed")) +
  scale_fill_manual( values = c("green2", "grey20"),
                     aesthetics = "fill",
                     breaks = c("TRUE", "FALSE"),
                     labels = c("Made", "Missed")) +
  scale_x_continuous( limits = c(-27.5, 27.5)) +
  scale_y_continuous( limits = c(0,45)) +
  theme( legend.title = element_blank()) +
  ggtitle( label = "Bruce Brown: Pre All Star Break") 
ggplotly(shot_chart_1) %>% 
  highlight( selectize = TRUE) %>%
  hide_legend()
df_pre %>% 
  mutate( make_miss = case_when(
    typeEvent == "Made Shot" ~ 1,
    TRUE ~ 0
  )) %>%
  group_by(zoneBasic) %>%
  summarise(shooting = mean(make_miss)) %>%
  mutate_at(vars(shooting), funs(round(.,4))) %>%
  reactable() %>%
  add_title("Pre All Star Break Shooting by Zone")

Pre All Star Break Shooting by Zone

df_pre_clean <- df_pre %>% 
  mutate( make_miss = case_when(
    typeEvent == "Made Shot" ~ 1,
    TRUE ~ 0
  )) %>%
  group_by(zoneBasic) %>%
  summarise(shooting = mean(make_miss)) %>%
  mutate_at(vars(shooting), funs(round(.,4))) %>%
  as.data.frame() %>%
  rename( Pre_all_star = shooting)
df_post <- df %>%
  filter( dateGame > '2022-2-20')

#Post All Star Break shot chart and table

shot_chart_2 <- plot_court() + 
  geom_point( data = df_post, aes( x = x , y = y,
                                    color = isShotMade,
                                    fill = isShotMade),
              size = 2, shape = 21, stroke = .2) +
  scale_color_manual( values = c("green4", "red3"), 
                      aesthetics = "color",
                      breaks = c("TRUE", "FALSE"),
                      labels = c("Made", "Missed")) +
  scale_fill_manual( values = c("green2", "grey20"),
                     aesthetics = "fill",
                     breaks = c("TRUE", "FALSE"),
                     labels = c("Made", "Missed")) +
  scale_x_continuous( limits = c(-27.5, 27.5)) +
  scale_y_continuous( limits = c(0,45)) +
  theme( legend.title = element_blank()) +
  ggtitle( label = "Bruce Brown: Post All Star Break") 
ggplotly(shot_chart_2) %>% 
  highlight( selectize = TRUE) %>%
  hide_legend()
df_post %>% 
  mutate( make_miss = case_when(
    typeEvent == "Made Shot" ~ 1,
    TRUE ~ 0
  )) %>%
  group_by(zoneBasic) %>%
  summarise(shooting = mean(make_miss)) %>%
  mutate_at(vars(shooting), funs(round(.,4))) %>%
  reactable() %>%
  add_title("Post All Star Break Shooting by Zone")

Post All Star Break Shooting by Zone

df_post_clean <- df_post %>% 
  mutate( make_miss = case_when(
    typeEvent == "Made Shot" ~ 1,
    TRUE ~ 0
  )) %>%
  group_by(zoneBasic) %>%
  summarise(shooting = mean(make_miss)) %>%
  mutate_at(vars(shooting), funs(round(.,4))) %>%
  as.data.frame() %>%
  rename( Post_all_star = shooting)
df_21 <- teams_shots(teams = team, seasons = 2021) %>%
  filter(namePlayer == player) %>%
  mutate( x = as.numeric(as.character(locationX))/10, y = as.numeric(as.character(locationY))/10 + hoop_center_y)
## Brooklyn Nets 2020-21 shot data
df_21$x <- df_21$x * -1
df_21_clean <- df_21 %>% 
  mutate( make_miss = case_when(
    typeEvent == "Made Shot" ~ 1,
    TRUE ~ 0
  )) %>%
  group_by(zoneBasic) %>%
  summarise(shooting = mean(make_miss)) %>%
  mutate_at(vars(shooting), funs(round(.,4))) %>% 
  as.data.frame() %>%
  rename( '21 Shooting' = shooting)

#Shooitng compared to ’21 season

right_join(df_21_clean, df_pre_clean) %>%
  right_join(df_post_clean) %>%
  reactable() %>%
  add_title("Tracking Shooting")

Tracking Shooting

Right Corner 3 attempts in ’21

df_21 %>% 
  filter(zoneBasic == "Right Corner 3") %>%
  summarise(n_distinct(idEvent)) 
## # A tibble: 1 × 1
##   `n_distinct(idEvent)`
##                   <int>
## 1                    21

Right Corner 3 attempts pre all star break

df_pre %>% 
  filter(zoneBasic == "Right Corner 3") %>%
  summarise(n_distinct(idEvent)) 
## # A tibble: 1 × 1
##   `n_distinct(idEvent)`
##                   <int>
## 1                    10

Right Corner 3 attempts post all star break

df_post %>% 
  filter(zoneBasic == "Right Corner 3") %>%
  summarise(n_distinct(idEvent)) 
## # A tibble: 1 × 1
##   `n_distinct(idEvent)`
##                   <int>
## 1                    17
sessionInfo()
## R version 4.2.1 (2022-06-23)
## 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.2/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.2/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] rpubs_0.2.2         reactablefmtr_2.0.0 reactable_0.3.0    
##  [4] lubridate_1.8.0     plotly_4.10.0       cowplot_1.1.1      
##  [7] extrafont_0.18      devtools_2.4.4      usethis_2.1.6      
## [10] nbastatR_0.1.151    forcats_0.5.2       stringr_1.4.1      
## [13] dplyr_1.0.10        purrr_0.3.4         readr_2.1.3        
## [16] tidyr_1.2.1         tibble_3.1.8        tidyverse_1.3.2    
## [19] ggplot2_3.3.6      
## 
## loaded via a namespace (and not attached):
##  [1] googledrive_2.0.0   colorspace_2.0-3    ellipsis_0.3.2     
##  [4] fs_1.5.2            rstudioapi_0.14     listenv_0.8.0      
##  [7] furrr_0.3.1         remotes_2.4.2       bit64_4.0.5        
## [10] fansi_1.0.3         xml2_1.3.3          codetools_0.2-18   
## [13] cachem_1.0.6        knitr_1.40          pkgload_1.3.0      
## [16] jsonlite_1.8.2      broom_1.0.1         Rttf2pt1_1.3.10    
## [19] dbplyr_2.2.1        shiny_1.7.2         compiler_4.2.1     
## [22] httr_1.4.4          backports_1.4.1     assertthat_0.2.1   
## [25] fastmap_1.1.0       lazyeval_0.2.2      gargle_1.2.1       
## [28] cli_3.4.1           later_1.3.0         htmltools_0.5.3    
## [31] prettyunits_1.1.1   tools_4.2.1         gtable_0.3.1       
## [34] glue_1.6.2          Rcpp_1.0.9          cellranger_1.1.0   
## [37] jquerylib_0.1.4     vctrs_0.4.2         extrafontdb_1.0    
## [40] crosstalk_1.2.0     xfun_0.33           globals_0.16.1     
## [43] ps_1.7.1            rvest_1.0.3         mime_0.12          
## [46] miniUI_0.1.1.1      lifecycle_1.0.2     googlesheets4_1.0.1
## [49] future_1.28.0       scales_1.2.1        vroom_1.6.0        
## [52] hms_1.1.2           promises_1.2.0.1    parallel_4.2.1     
## [55] yaml_2.3.5          curl_4.3.2          memoise_2.0.1      
## [58] sass_0.4.2          stringi_1.7.8       pkgbuild_1.3.1     
## [61] rlang_1.0.6         pkgconfig_2.0.3     evaluate_0.16      
## [64] htmlwidgets_1.5.4   labeling_0.4.2      bit_4.0.4          
## [67] processx_3.7.0      tidyselect_1.1.2    parallelly_1.32.1  
## [70] magrittr_2.0.3      R6_2.5.1            generics_0.1.3     
## [73] profvis_0.3.7       DBI_1.1.3           pillar_1.8.1       
## [76] haven_2.5.1         withr_2.5.0         modelr_0.1.9       
## [79] crayon_1.5.2        utf8_1.2.2          tzdb_0.3.0         
## [82] rmarkdown_2.16      urlchecker_1.0.1    grid_4.2.1         
## [85] readxl_1.4.1        data.table_1.14.2   reactR_0.4.4       
## [88] callr_3.7.2         reprex_2.0.2        digest_0.6.29      
## [91] xtable_1.8-4        httpuv_1.6.6        munsell_0.5.0      
## [94] viridisLite_0.4.1   bslib_0.4.0         sessioninfo_1.2.2