Playoff shot quality difference stats for ’23 contenders (top five Vegas odds):

Den - 5/15 (33%) playoff record; positive shot quality difference in 8/15 (53%) games; 8/15 (53%) games can be predicted by shot quality difference.

Bos - 15/29 (52%) playoff record; positive shot quality difference in 20/29 (69%) games; 18/29 (62%) games can be predicted by shot quality difference.

Mil - 23/35 (66%) playoff record; positive shot quality difference in 26/35 (74%) games; 26/35 (74%) games can be predicted by shot quality difference.

Phi - 13/24 (54%) playoff record; positive shot quality difference in 11/24 (46%) games; 20/24 (83%) games can be predicted by shot quality difference.

Phx - 21/35 (60%) playoff record; positive shot quality difference in 12/35 (34%) games; 16/35 (46%) games can be predicted by shot quality difference.

Playoff Shot Quality difference predictive strength by round:

Round 1 -

Round 2 -

Conference Finals -

Finals -

Last two rounds combined -

*Screenshots of work will be provided in email.

library(tidyverse)
library(jsonlite)
library(janitor)
library(dplyr)
library(nbastatR)
library(reactable)
library(reactablefmtr)
library(plotly)

Charts

teams <- c("Denver", "Boston", "Milwaukee", "Philadelphia", "Phoenix")
record <- c(.33, .52, .66, .54, .60)
shot_quality <- c( .53, .69, .74, .46, .34)
predictability<- c(.53, .62, .74, .83, .46)

data <- data.frame(teams, record, shot_quality, predictability)

plot_ly(data, x = ~teams, y = ~record,
        type = "bar",
        marker = list(color = c("lightblue", "green", "tan", "blue", "orange"))) %>%
  layout( title = "Playoff Winning %, '21 & '22")
plot_ly(data, x = ~teams , y = ~shot_quality,
        type = "bar",
        marker = list(color = c("lightblue", "green", "tan", "blue", "orange"))) %>%
  layout( title = "% of playoff games with positive shot quality difference, '21 & '22")
plot_ly(data, x = ~teams, y = ~predictability,
        type = "bar",
        marker = list(color = c("lightblue", "green", "tan", "blue", "orange"))) %>%
  layout( title = "% of playoff games predicted by shot quality difference, '21 & '22")
plot_ly(data, x = ~teams, y = ~record, 
        type = "bar",
        name = "Playoff record") %>%
  add_trace( y = ~shot_quality, name = "% of games with + shot qaulity diff") %>%
  add_trace( y = ~predictability, name = "Predictability strength of shot quality diff") %>%
  layout( title = "Combined chart, '21 & '22 playoffs")
round <- c("Round 1", "Round 2", "Confernce Finals", "Finals", "Final two rounds")
predictive_strength <- c(.63, .63, .70, .44, .62)

data2 <- data.frame(round, predictive_strength)

plot_ly(data2, x = ~round, y = ~predictive_strength,
        type = "bar",
        color = ~round) %>%
  layout(title = "Predictive strength of shot quality difference by round, '21 & '22 playoffs")
bos_opp_url <- "https://api.pbpstats.com/get-game-logs/nba?Season=2021-22,2020-21&SeasonType=Playoffs&EntityId=1610612738&EntityType=Opponent"

bos_opp <- read_json(bos_opp_url)

bos_opp <- bos_opp[["multi_row_table_data"]] %>%
  bind_rows() %>%
  clean_names() 
opp_sq <- bos_opp %>%
  select(date,
         shot_quality_avg) %>%
  mutate(opp_shot_quality_avg = shot_quality_avg) %>%
  select(opp_shot_quality_avg)

rf_opp <- bos_opp %>%
  select(date,
         at_rim_frequency) %>%
  mutate(opp_at_rim_freq = at_rim_frequency) %>%
  select(opp_at_rim_freq)
bos_url <- "https://api.pbpstats.com/get-game-logs/nba?Season=2021-22,2020-21&SeasonType=Playoffs&EntityId=1610612738&EntityType=Team"

bos <- read_json(bos_url)

bos <- bos[["multi_row_table_data"]] %>%
  bind_rows() %>%
  clean_names()
sq <- bos %>%
  select(date,
         shot_quality_avg)

rf <- bos %>%
  select(date,
         at_rim_frequency)
sq_anal <- cbind(sq, opp_sq) %>%
  mutate( shot_qual_diff = (100*shot_quality_avg - 100*opp_shot_quality_avg) ) %>%
  mutate_at(vars(shot_qual_diff), funs(round(.,4)))
bos <- game_logs(
  seasons = c(2022,2021),
  league = "NBA",
  result_types = "team",
  season_types = "Playoffs"
) %>%
  filter(nameTeam == "Boston Celtics")
## Acquiring NBA basic team game logs for the 2020-21 Playoffs
## Acquiring NBA basic team game logs for the 2021-22 Playoffs
bos_wl <- bos %>%
  select(outcomeGame,
         slugOpponent)

Boston ’21 and ’20 playoffs

cbind(sq_anal, bos_wl) %>%
  select(date,
         shot_quality_avg,
         opp_shot_quality_avg,
         shot_qual_diff,
         outcomeGame,
         slugOpponent) %>%
  mutate( 'Team SQ' = shot_quality_avg,
          'Opp SQ' = opp_shot_quality_avg,
          'SQ Diff' = shot_qual_diff,
          Outcome = outcomeGame,
          Opponent = slugOpponent) %>%
  select( date,
          'Team SQ',
          'Opp SQ',
          'SQ Diff',
          Outcome,
          Opponent) %>%
  mutate_at(vars('Team SQ', 'Opp SQ'), funs(round(.,4)))%>%
  reactable( theme = espn(),
             defaultPageSize = 20,
             sortable = TRUE,
             filterable = TRUE,
             searchable = TRUE)
den_opp_url <- "https://api.pbpstats.com/get-game-logs/nba?Season=2021-22,2020-21&SeasonType=Playoffs&EntityId=1610612743&EntityType=Opponent"

den_opp <- read_json(den_opp_url)

den_opp <- den_opp[["multi_row_table_data"]] %>%
  bind_rows() %>%
  clean_names() 
den_opp_sq <- den_opp %>%
  select(date,
         shot_quality_avg) %>%
  mutate(opp_shot_quality_avg = shot_quality_avg) %>%
  select(opp_shot_quality_avg)

den_rf_opp <- den_opp %>%
  select(date,
         at_rim_frequency) %>%
  mutate(opp_at_rim_freq = at_rim_frequency) %>%
  select(opp_at_rim_freq)
den_url <- "https://api.pbpstats.com/get-game-logs/nba?Season=2021-22,2020-21&SeasonType=Playoffs&EntityId=1610612743&EntityType=Team"

den <- read_json(den_url)

den <- den[["multi_row_table_data"]] %>%
  bind_rows() %>%
  clean_names()
den_sq <- den %>%
  select(date,
         shot_quality_avg)

den_rf <- den %>%
  select(date,
         at_rim_frequency)
sq_anal_den <- cbind(den_sq, den_opp_sq) %>%
  mutate( shot_qual_diff = (100*shot_quality_avg - 100*opp_shot_quality_avg) ) %>%
  mutate_at(vars(shot_qual_diff), funs(round(.,4)))
den_pl <- game_logs(
  seasons = c(2022,2021),
  league = "NBA",
  result_types = "team",
  season_types = "Playoffs"
) %>%
  filter(nameTeam == "Denver Nuggets")
## Acquiring NBA basic team game logs for the 2020-21 Playoffs
## Acquiring NBA basic team game logs for the 2021-22 Playoffs
den_wl <- den_pl %>%
  select(outcomeGame,
         slugOpponent)

Denver ’21 and ’22 playoffs

cbind(sq_anal_den, den_wl) %>%
  select(date,
         shot_quality_avg,
         opp_shot_quality_avg,
         shot_qual_diff,
         outcomeGame,
         slugOpponent) %>%
  mutate( 'Team SQ' = shot_quality_avg,
          'Opp SQ' = opp_shot_quality_avg,
          'SQ Diff' = shot_qual_diff,
          Outcome = outcomeGame,
          Opponent = slugOpponent) %>%
  select( date,
          'Team SQ',
          'Opp SQ',
          'SQ Diff',
          Outcome,
          Opponent) %>%
  mutate_at(vars('Team SQ', 'Opp SQ'), funs(round(.,4))) %>%
  reactable( theme = espn(),
             defaultPageSize = 20,
             sortable = TRUE,
             filterable = TRUE,
             searchable = TRUE)
mil_opp_url <- "https://api.pbpstats.com/get-game-logs/nba?Season=2021-22,2020-21&SeasonType=Playoffs&EntityId=1610612749&EntityType=Opponent"

mil_opp <- read_json(mil_opp_url)

mil_opp <- mil_opp[["multi_row_table_data"]] %>%
  bind_rows() %>%
  clean_names() 
mil_opp_sq <- mil_opp %>%
  select(date,
         shot_quality_avg) %>%
  mutate(opp_shot_quality_avg = shot_quality_avg) %>%
  select(opp_shot_quality_avg)

mil_rf_opp <- mil_opp %>%
  select(date,
         at_rim_frequency) %>%
  mutate(opp_at_rim_freq = at_rim_frequency) %>%
  select(opp_at_rim_freq)
mil_url <- "https://api.pbpstats.com/get-game-logs/nba?Season=2021-22,2020-21&SeasonType=Playoffs&EntityId=1610612749&EntityType=Team"

mil <- read_json(mil_url)

mil <- mil[["multi_row_table_data"]] %>%
  bind_rows() %>%
  clean_names()
mil_sq <- mil %>%
  select(date,
         shot_quality_avg)

mil_rf <- mil %>%
  select(date,
         at_rim_frequency)
sq_anal_mil <- cbind(mil_sq, mil_opp_sq) %>%
  mutate( shot_qual_diff = (100*shot_quality_avg - 100*opp_shot_quality_avg) ) %>%
  mutate_at(vars(shot_qual_diff), funs(round(.,4)))
mil_pl <- game_logs(
  seasons = c(2022,2021),
  league = "NBA",
  result_types = "team",
  season_types = "Playoffs"
) %>%
  filter(nameTeam == "Milwaukee Bucks")
## Acquiring NBA basic team game logs for the 2020-21 Playoffs
## Acquiring NBA basic team game logs for the 2021-22 Playoffs
mil_wl <- mil_pl %>%
  select(outcomeGame,
         slugOpponent)

Milwaukee ’21 and ’22 playoffs

cbind(sq_anal_mil, mil_wl) %>%
  select(date,
         shot_quality_avg,
         opp_shot_quality_avg,
         shot_qual_diff,
         outcomeGame,
         slugOpponent) %>%
  mutate( 'Team SQ' = shot_quality_avg,
          'Opp SQ' = opp_shot_quality_avg,
          'SQ Diff' = shot_qual_diff,
          Outcome = outcomeGame,
          Opponent = slugOpponent) %>%
  select( date,
          'Team SQ',
          'Opp SQ',
          'SQ Diff',
          Outcome,
          Opponent) %>%
  mutate_at(vars('Team SQ', 'Opp SQ'), funs(round(.,4))) %>%
  reactable( theme = espn(),
             defaultPageSize = 20,
             sortable = TRUE,
             filterable = TRUE,
             searchable = TRUE)
phi_opp_url <- "https://api.pbpstats.com/get-game-logs/nba?Season=2021-22,2020-21&SeasonType=Playoffs&EntityId=1610612755&EntityType=Opponent"

phi_opp <- read_json(phi_opp_url)

phi_opp <- phi_opp[["multi_row_table_data"]] %>%
  bind_rows() %>%
  clean_names() 
phi_opp_sq <- phi_opp %>%
  select(date,
         shot_quality_avg) %>%
  mutate(opp_shot_quality_avg = shot_quality_avg) %>%
  select(opp_shot_quality_avg)

phi_rf_opp <- phi_opp %>%
  select(date,
         at_rim_frequency) %>%
  mutate(opp_at_rim_freq = at_rim_frequency) %>%
  select(opp_at_rim_freq)
phi_url <- "https://api.pbpstats.com/get-game-logs/nba?Season=2021-22,2020-21&SeasonType=Playoffs&EntityId=1610612755&EntityType=Team"

phi <- read_json(phi_url)

phi <- phi[["multi_row_table_data"]] %>%
  bind_rows() %>%
  clean_names()
phi_sq <- phi %>%
  select(date,
         shot_quality_avg)

phi_rf <- phi %>%
  select(date,
         at_rim_frequency)
phi_sq_anal <- cbind(phi_sq, phi_opp_sq) %>%
  mutate( shot_qual_diff = (100*shot_quality_avg - 100*opp_shot_quality_avg) ) %>%
  mutate_at(vars(shot_qual_diff), funs(round(.,4)))
phi <- game_logs(
  seasons = c(2022,2021),
  league = "NBA",
  result_types = "team",
  season_types = "Playoffs"
) %>%
  filter(nameTeam == "Philadelphia 76ers")
## Acquiring NBA basic team game logs for the 2020-21 Playoffs
## Acquiring NBA basic team game logs for the 2021-22 Playoffs
phi_wl <- phi %>%
  select(outcomeGame,
         slugOpponent)

Philadelphia ’21 and ’20 playoffs

cbind(phi_sq_anal, phi_wl) %>%
  select(date,
         shot_quality_avg,
         opp_shot_quality_avg,
         shot_qual_diff,
         outcomeGame,
         slugOpponent) %>%
  mutate( 'Team SQ' = shot_quality_avg,
          'Opp SQ' = opp_shot_quality_avg,
          'SQ Diff' = shot_qual_diff,
          Outcome = outcomeGame,
          Opponent = slugOpponent) %>%
  select( date,
          'Team SQ',
          'Opp SQ',
          'SQ Diff',
          Outcome,
          Opponent) %>%
  mutate_at(vars('Team SQ', 'Opp SQ'), funs(round(.,4))) %>%
  reactable( theme = espn(),
             defaultPageSize = 20,
             sortable = TRUE,
             filterable = TRUE,
             searchable = TRUE)
phx_opp_url <- "https://api.pbpstats.com/get-game-logs/nba?Season=2021-22,2020-21&SeasonType=Playoffs&EntityId=1610612756&EntityType=Opponent"

phx_opp <- read_json(phx_opp_url)

phx_opp <- phx_opp[["multi_row_table_data"]] %>%
  bind_rows() %>%
  clean_names() 
phx_opp_sq <- phx_opp %>%
  select(date,
         shot_quality_avg) %>%
  mutate(opp_shot_quality_avg = shot_quality_avg) %>%
  select(opp_shot_quality_avg)

phx_rf_opp <- phx_opp %>%
  select(date,
         at_rim_frequency) %>%
  mutate(opp_at_rim_freq = at_rim_frequency) %>%
  select(opp_at_rim_freq)
phx_url <- "https://api.pbpstats.com/get-game-logs/nba?Season=2021-22,2020-21&SeasonType=Playoffs&EntityId=1610612756&EntityType=Team"

phx <- read_json(phx_url)

phx <- phx[["multi_row_table_data"]] %>%
  bind_rows() %>%
  clean_names()
phx_sq <- phx %>%
  select(date,
         shot_quality_avg)

phx_rf <- phx %>%
  select(date,
         at_rim_frequency)
phx_sq_anal <- cbind(phx_sq, phx_opp_sq) %>%
  mutate( shot_qual_diff = (100*shot_quality_avg - 100*opp_shot_quality_avg) ) %>%
  mutate_at(vars(shot_qual_diff), funs(round(.,4)))
phx <- game_logs(
  seasons = c(2022,2021),
  league = "NBA",
  result_types = "team",
  season_types = "Playoffs"
) %>%
  filter(nameTeam == "Phoenix Suns")
## Acquiring NBA basic team game logs for the 2020-21 Playoffs
## Acquiring NBA basic team game logs for the 2021-22 Playoffs
phx_wl <- phx %>%
  select(outcomeGame,
         slugOpponent)

Phoenix ’21 and ’20 playoffs

cbind(phx_sq_anal, phx_wl) %>%
  select(date,
         shot_quality_avg,
         opp_shot_quality_avg,
         shot_qual_diff,
         outcomeGame,
         slugOpponent) %>%
  mutate( 'Team SQ' = shot_quality_avg,
          'Opp SQ' = opp_shot_quality_avg,
          'SQ Diff' = shot_qual_diff,
          Outcome = outcomeGame,
          Opponent = slugOpponent) %>%
  select( date,
          'Team SQ',
          'Opp SQ',
          'SQ Diff',
          Outcome,
          Opponent) %>%
  mutate_at(vars('Team SQ', 'Opp SQ'), funs(round(.,4))) %>%
  reactable( theme = espn(),
             defaultPageSize = 20,
             sortable = TRUE,
             filterable = TRUE,
             searchable = TRUE)