R Markdown

This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.

When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:

Purpose of This Analysis

The purpose of this analysis is to evaluate player performance in the 2021–2022 NBA Playoffs through a General Manager (GM) lens, focusing on efficiency, impact, and role value rather than raw box score totals.


Data Used

This project uses player-level statistics from the 2021–2022 NBA Playoffs, including scoring, assists, rebounds, minutes played, shooting, and defensive metrics. Playoff data was chosen because it reflects performance under high-pressure, reduced-rotation conditions, where efficiency and decision-making are most important.


What This Analysis Examines


GM Decisions This Analysis Supports

This analysis helps a GM: - Identify undervalued rotation players who produce efficiently in limited minutes
- Spot high-minute, low-impact players who may be playoff liabilities
- Optimize playoff rotations and minute allocation
- Make trade, contract, and lineup decisions based on efficiency rather than reputation
- Understand how playoff basketball shifts player value

summary(cars)
##      speed           dist       
##  Min.   : 4.0   Min.   :  2.00  
##  1st Qu.:12.0   1st Qu.: 26.00  
##  Median :15.0   Median : 36.00  
##  Mean   :15.4   Mean   : 42.98  
##  3rd Qu.:19.0   3rd Qu.: 56.00  
##  Max.   :25.0   Max.   :120.00

Including Plots

You can also embed plots, for example:

Note that the echo = FALSE parameter was added to the code chunk to prevent printing of the R code that generated the plot.

nba_playoffs <- read.csv(
  "~/Downloads/2021-2022 NBA Player Stats - Playoffs.csv",
  sep = ";",
  fileEncoding = "latin1"
)

knitr::kable(head(nba_playoffs, 6))
Rk Player Pos Age Tm G GS MP FG FGA FG. X3P X3PA X3P. X2P X2PA X2P. eFG. FT FTA FT. ORB DRB TRB AST STL BLK TOV PF PTS
1 Precious Achiuwa C 22 TOR 6 1 27.8 4.2 8.7 0.481 0.8 2.7 0.313 3.3 6.0 0.556 0.529 1.0 1.7 0.600 1.3 3.5 4.8 1.0 0.2 0.8 1.5 2.3 10.2
2 Steven Adams C 28 MEM 7 5 16.3 1.3 3.0 0.429 0.0 0.0 0.000 1.3 3.0 0.429 0.429 0.9 1.6 0.545 2.1 4.3 6.4 2.1 0.1 0.1 0.6 1.7 3.4
3 Bam Adebayo C 24 MIA 18 18 34.1 5.8 9.7 0.594 0.0 0.1 0.000 5.8 9.7 0.598 0.594 3.2 4.2 0.763 2.1 5.9 8.0 2.7 1.0 0.7 2.1 3.1 14.8
4 Nickeil Alexander-Walker SG 23 UTA 1 0 5.0 2.0 2.0 1.000 0.0 0.0 0.000 2.0 2.0 1.000 1.000 1.0 1.0 1.000 0.0 1.0 1.0 1.0 1.0 0.0 0.0 0.0 5.0
5 Grayson Allen SG 26 MIL 12 5 25.4 3.1 6.8 0.451 1.6 4.0 0.396 1.5 2.8 0.529 0.567 0.6 0.9 0.636 0.4 2.5 2.9 1.3 0.7 0.3 0.8 1.8 8.3
6 Jose Alvarado PG 23 NOP 6 0 19.5 2.7 5.5 0.485 1.0 2.7 0.375 1.7 2.8 0.588 0.576 1.7 2.2 0.769 0.3 1.0 1.3 1.5 1.2 0.2 1.2 3.0 8.0
str(nba_playoffs)
## 'data.frame':    217 obs. of  30 variables:
##  $ Rk    : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Player: chr  "Precious Achiuwa" "Steven Adams" "Bam Adebayo" "Nickeil Alexander-Walker" ...
##  $ Pos   : chr  "C" "C" "C" "SG" ...
##  $ Age   : int  22 28 24 23 26 23 28 27 29 24 ...
##  $ Tm    : chr  "TOR" "MEM" "MIA" "UTA" ...
##  $ G     : int  6 7 18 1 12 6 12 12 8 6 ...
##  $ GS    : int  1 5 18 0 5 0 1 12 0 6 ...
##  $ MP    : num  27.8 16.3 34.1 5 25.4 19.5 18.4 37.3 2.5 36.2 ...
##  $ FG    : num  4.2 1.3 5.8 2 3.1 2.7 2.4 11.7 0.3 6.5 ...
##  $ FGA   : num  8.7 3 9.7 2 6.8 5.5 4.3 23.8 0.4 13.7 ...
##  $ FG.   : num  0.481 0.429 0.594 1 0.451 0.485 0.569 0.491 0.667 0.476 ...
##  $ X3P   : num  0.8 0 0 0 1.6 1 0.3 0.8 0 2.3 ...
##  $ X3PA  : num  2.7 0 0.1 0 4 2.7 1 3.4 0 6.8 ...
##  $ X3P.  : num  0.313 0 0 0 0.396 0.375 0.25 0.22 0 0.341 ...
##  $ X2P   : num  3.3 1.3 5.8 2 1.5 1.7 2.2 10.9 0.3 4.2 ...
##  $ X2PA  : num  6 3 9.7 2 2.8 2.8 3.3 20.3 0.4 6.8 ...
##  $ X2P.  : num  0.556 0.429 0.598 1 0.529 0.588 0.667 0.537 0.667 0.61 ...
##  $ eFG.  : num  0.529 0.429 0.594 1 0.567 0.576 0.598 0.507 0.667 0.561 ...
##  $ FT    : num  1 0.9 3.2 1 0.6 1.7 0.9 7.6 0.1 2 ...
##  $ FTA   : num  1.7 1.6 4.2 1 0.9 2.2 1.5 11.2 0.4 2.7 ...
##  $ FT.   : num  0.6 0.545 0.763 1 0.636 0.769 0.611 0.679 0.333 0.75 ...
##  $ ORB   : num  1.3 2.1 2.1 0 0.4 0.3 0.8 2.2 0.1 1.2 ...
##  $ DRB   : num  3.5 4.3 5.9 1 2.5 1 3.5 12 0.4 2.8 ...
##  $ TRB   : num  4.8 6.4 8 1 2.9 1.3 4.3 14.2 0.5 4 ...
##  $ AST   : num  1 2.1 2.7 1 1.3 1.5 1.8 6.8 0.1 2.5 ...
##  $ STL   : num  0.2 0.1 1 1 0.7 1.2 0.9 0.7 0.1 1 ...
##  $ BLK   : num  0.8 0.1 0.7 0 0.3 0.2 0.6 1.3 0 0.2 ...
##  $ TOV   : num  1.5 0.6 2.1 0 0.8 1.2 0.8 4.5 0 1.7 ...
##  $ PF    : num  2.3 1.7 3.1 0 1.8 3 1.8 3.6 0.5 3 ...
##  $ PTS   : num  10.2 3.4 14.8 5 8.3 8 6 31.7 0.6 17.3 ...
names(nba_playoffs)
##  [1] "Rk"     "Player" "Pos"    "Age"    "Tm"     "G"      "GS"     "MP"    
##  [9] "FG"     "FGA"    "FG."    "X3P"    "X3PA"   "X3P."   "X2P"    "X2PA"  
## [17] "X2P."   "eFG."   "FT"     "FTA"    "FT."    "ORB"    "DRB"    "TRB"   
## [25] "AST"    "STL"    "BLK"    "TOV"    "PF"     "PTS"
summary(nba_playoffs)
##        Rk         Player              Pos                 Age       
##  Min.   :  1   Length:217         Length:217         Min.   :19.00  
##  1st Qu.: 55   Class :character   Class :character   1st Qu.:23.00  
##  Median :109   Mode  :character   Mode  :character   Median :26.00  
##  Mean   :109                                         Mean   :26.59  
##  3rd Qu.:163                                         3rd Qu.:29.00  
##  Max.   :217                                         Max.   :38.00  
##       Tm                  G                GS               MP       
##  Length:217         Min.   : 1.000   Min.   : 0.000   Min.   : 0.00  
##  Class :character   1st Qu.: 5.000   1st Qu.: 0.000   1st Qu.: 7.60  
##  Mode  :character   Median : 6.000   Median : 0.000   Median :18.40  
##                     Mean   : 8.714   Mean   : 4.009   Mean   :19.43  
##                     3rd Qu.:12.000   3rd Qu.: 6.000   3rd Qu.:31.20  
##                     Max.   :24.000   Max.   :24.000   Max.   :44.00  
##        FG              FGA              FG.              X3P        
##  Min.   : 0.000   Min.   : 0.000   Min.   :0.0000   Min.   :0.0000  
##  1st Qu.: 1.000   1st Qu.: 2.000   1st Qu.:0.3700   1st Qu.:0.0000  
##  Median : 2.300   Median : 4.700   Median :0.4390   Median :0.7000  
##  Mean   : 3.045   Mean   : 6.738   Mean   :0.4375   Mean   :0.9346  
##  3rd Qu.: 4.500   3rd Qu.:10.000   3rd Qu.:0.5000   3rd Qu.:1.5000  
##  Max.   :12.200   Max.   :23.800   Max.   :1.0000   Max.   :4.1000  
##       X3PA           X3P.             X2P              X2PA       
##  Min.   : 0.0   Min.   :0.0000   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.: 0.5   1st Qu.:0.0000   1st Qu.: 0.500   1st Qu.: 1.100  
##  Median : 2.2   Median :0.3310   Median : 1.300   Median : 2.600  
##  Mean   : 2.7   Mean   :0.2733   Mean   : 2.112   Mean   : 4.038  
##  3rd Qu.: 4.4   3rd Qu.:0.3930   3rd Qu.: 3.000   3rd Qu.: 5.800  
##  Max.   :10.4   Max.   :1.0000   Max.   :11.200   Max.   :20.300  
##       X2P.            eFG.              FT             FTA        
##  Min.   :0.000   Min.   :0.0000   Min.   :0.000   Min.   : 0.000  
##  1st Qu.:0.410   1st Qu.:0.4320   1st Qu.:0.200   1st Qu.: 0.300  
##  Median :0.500   Median :0.5210   Median :0.800   Median : 1.000  
##  Mean   :0.495   Mean   :0.5044   Mean   :1.442   Mean   : 1.833  
##  3rd Qu.:0.622   3rd Qu.:0.5960   3rd Qu.:2.000   3rd Qu.: 2.500  
##  Max.   :1.000   Max.   :1.5000   Max.   :8.500   Max.   :11.200  
##       FT.              ORB              DRB              TRB        
##  Min.   :0.0000   Min.   :0.0000   Min.   : 0.000   Min.   : 0.000  
##  1st Qu.:0.5000   1st Qu.:0.2000   1st Qu.: 1.000   1st Qu.: 1.200  
##  Median :0.7500   Median :0.5000   Median : 2.000   Median : 2.800  
##  Mean   :0.6232   Mean   :0.7737   Mean   : 2.627   Mean   : 3.404  
##  3rd Qu.:0.8570   3rd Qu.:1.0000   3rd Qu.: 3.700   3rd Qu.: 4.800  
##  Max.   :1.0000   Max.   :5.5000   Max.   :12.000   Max.   :14.300  
##       AST             STL              BLK              TOV       
##  Min.   :0.000   Min.   :0.0000   Min.   :0.0000   Min.   :0.000  
##  1st Qu.:0.400   1st Qu.:0.2000   1st Qu.:0.0000   1st Qu.:0.300  
##  Median :1.000   Median :0.5000   Median :0.2000   Median :0.800  
##  Mean   :1.829   Mean   :0.5848   Mean   :0.3613   Mean   :1.086  
##  3rd Qu.:2.700   3rd Qu.:0.9000   3rd Qu.:0.5000   3rd Qu.:1.500  
##  Max.   :9.800   Max.   :2.1000   Max.   :2.5000   Max.   :6.200  
##        PF             PTS        
##  Min.   :0.000   Min.   : 0.000  
##  1st Qu.:0.800   1st Qu.: 2.100  
##  Median :1.800   Median : 6.000  
##  Mean   :1.784   Mean   : 8.457  
##  3rd Qu.:2.800   3rd Qu.:12.600  
##  Max.   :4.700   Max.   :31.700
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.1     ✔ stringr   1.5.2
## ✔ ggplot2   4.0.0     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
# See your exact NBA column names
names(nba_playoffs)
##  [1] "Rk"     "Player" "Pos"    "Age"    "Tm"     "G"      "GS"     "MP"    
##  [9] "FG"     "FGA"    "FG."    "X3P"    "X3PA"   "X3P."   "X2P"    "X2PA"  
## [17] "X2P."   "eFG."   "FT"     "FTA"    "FT."    "ORB"    "DRB"    "TRB"   
## [25] "AST"    "STL"    "BLK"    "TOV"    "PF"     "PTS"
# Summary stats for every numeric column in your NBA dataset
nba_playoffs %>%
  select(where(is.numeric)) %>%
  pivot_longer(everything(), names_to = "Variable", values_to = "Value") %>%
  group_by(Variable) %>%
  summarise(
    N      = sum(!is.na(Value)),
    Mean   = mean(Value, na.rm = TRUE),
    Median = median(Value, na.rm = TRUE),
    SD     = sd(Value, na.rm = TRUE),
    Min    = min(Value, na.rm = TRUE),
    Max    = max(Value, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(Mean))
## # A tibble: 27 × 7
##    Variable     N   Mean Median    SD   Min   Max
##    <chr>    <int>  <dbl>  <dbl> <dbl> <dbl> <dbl>
##  1 Rk         217 109     109   62.8      1 217  
##  2 Age        217  26.6    26    4.24    19  38  
##  3 MP         217  19.4    18.4 12.9      0  44  
##  4 G          217   8.71    6    5.80     1  24  
##  5 PTS        217   8.46    6    7.63     0  31.7
##  6 FGA        217   6.74    4.7  5.87     0  23.8
##  7 X2PA       217   4.04    2.6  4.09     0  20.3
##  8 GS         217   4.01    0    5.94     0  24  
##  9 TRB        217   3.40    2.8  2.84     0  14.3
## 10 FG         217   3.05    2.3  2.70     0  12.2
## # ℹ 17 more rows
library(tidyverse)

ggplot(nba_playoffs, aes(x = PTS)) +
  geom_histogram(bins = 30, fill = "steelblue", color = "white") +
  labs(
    title = "Distribution of Points (Playoffs)",
    x = "Points",
    y = "Frequency"
  ) +
  theme_minimal()

library(tidyverse)

ggplot(nba_playoffs, aes(x = AST)) +
  geom_histogram(bins = 30, fill = "steelblue", color = "white") +
  labs(
    title = "Distribution of Points (Playoffs)",
    x = "Points",
    y = "Frequency"
  ) +
  theme_minimal()

library(tidyverse)

ggplot(nba_playoffs, aes(x = FG)) +
  geom_histogram(bins = 30, fill = "steelblue", color = "white") +
  labs(
    title = "Distribution of Field Goals (Playoffs)",
    x = "Field Goals",
    y = "Frequency"
  ) +
  theme_minimal()

library(tidyverse)

ggplot(nba_playoffs, aes(x = ORB)) +
  geom_histogram(bins = 30, fill = "steelblue", color = "white") +
  labs(
    title = "Distribution of Offensive Rebounds (Playoffs)",
    x = "Offensive Rebounds",
    y = "Frequency"
  ) +
  theme_minimal()

library(tidyverse)

ggplot(nba_playoffs, aes(x = DRB)) +
  geom_histogram(bins = 30, fill = "steelblue", color = "white") +
  labs(
    title = "Distribution of Defensive Rebounds (Playoffs)",
    x = "Defensive Rebounds",
    y = "Frequency"
  ) +
  theme_minimal()

Histograms for MULTIPLE NBA stats (GM-style)


``` r
library(tidyverse)

nba_playoffs %>%
  select(where(is.numeric)) %>%          # only numeric NBA stats
  pivot_longer(
    cols = everything(),
    names_to = "Stat",
    values_to = "Value"
  ) %>%
  ggplot(aes(x = Value)) +
  geom_histogram(bins = 30, fill = "darkorange", color = "white") +
  facet_wrap(~ Stat, scales = "free") +
  labs(
    title = "Distributions of NBA Playoff Statistics",
    x = "Value",
    y = "Frequency"
  ) +
  theme_minimal()


``` r
library(tidyverse)

nba_playoffs %>%
  select(PTS, AST, TRB, MP) %>%   # edit to match your dataset names
  pivot_longer(everything(), names_to = "Stat", values_to = "Value") %>%
  ggplot(aes(Value)) +
  geom_histogram(bins = 25, fill = "forestgreen", color = "white") +
  facet_wrap(~ Stat, scales = "free") +
  theme_minimal() +
  labs(
    title = "Key Playoff Performance Distributions",
    x = "Value",
    y = "Count"
  )

library(tidyverse)

names(nba_playoffs)
##  [1] "Rk"     "Player" "Pos"    "Age"    "Tm"     "G"      "GS"     "MP"    
##  [9] "FG"     "FGA"    "FG."    "X3P"    "X3PA"   "X3P."   "X2P"    "X2PA"  
## [17] "X2P."   "eFG."   "FT"     "FTA"    "FT."    "ORB"    "DRB"    "TRB"   
## [25] "AST"    "STL"    "BLK"    "TOV"    "PF"     "PTS"
# Look for common "position" column names
possible_pos <- c("Pos","POS","Position","position","pos")
intersect(possible_pos, names(nba_playoffs))
## [1] "Pos"

2) Position breakdown (summary stats table)

✅ If your position column is Pos (most common)


``` r
library(tidyverse)

nba_playoffs %>%
  group_by(Pos) %>%
  summarise(
    Players = n_distinct(Player),
    Avg_PTS = mean(PTS, na.rm = TRUE),
    Avg_AST = mean(AST, na.rm = TRUE),
    Avg_TRB = mean(TRB, na.rm = TRUE),
    Avg_MP  = mean(MP,  na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(Avg_PTS))
## # A tibble: 5 × 6
##   Pos   Players Avg_PTS Avg_AST Avg_TRB Avg_MP
##   <chr>   <int>   <dbl>   <dbl>   <dbl>  <dbl>
## 1 PG         39   10.3     3.31    2.8    21.8
## 2 SF         39    8.91    1.66    2.89   19.7
## 3 PF         50    7.9     1.52    3.74   19.5
## 4 C          40    7.82    1.08    5.38   18.0
## 5 SG         49    7.76    1.70    2.33   18.4
library(tidyverse)

# Pick the first matching column that exists in your dataset
pos_col   <- intersect(names(nba_playoffs), c("Pos", "Position", "pos", "position"))[1]
pts_col   <- intersect(names(nba_playoffs), c("PTS", "Pts", "points"))[1]
player_col<- intersect(names(nba_playoffs), c("Player", "player", "PlayerName", "Name"))[1]
team_col  <- intersect(names(nba_playoffs), c("Team", "Tm", "team", "tm"))[1]

# Stop with a clear message if something critical is missing
if (is.na(pos_col)) stop("No position column found. Check names(nba_playoffs).")
if (is.na(pts_col)) stop("No points column found (PTS/Pts/points). Check names(nba_playoffs).")

nba_playoffs %>%
  group_by(.data[[pos_col]]) %>%
  arrange(desc(.data[[pts_col]])) %>%
  slice_head(n = 10) %>%
  select(any_of(c(player_col, team_col, pos_col, pts_col, "AST", "TRB", "REB", "MP", "MPG"))) %>%
  ungroup()
## # A tibble: 50 × 7
##    Player             Tm    Pos     PTS   AST   TRB    MP
##    <chr>              <chr> <chr> <dbl> <dbl> <dbl> <dbl>
##  1 Nikola Joki?       DEN   C      31     5.8  13.2  34.2
##  2 Joel Embiid        PHI   C      23.6   2.1  10.7  38.5
##  3 Karl-Anthony Towns MIN   C      21.8   2.2  10.8  37  
##  4 Nikola Vu?evi?     CHI   C      19.4   3.2  12.4  36.2
##  5 Deandre Ayton      PHO   C      17.9   1.7   8.9  30.5
##  6 Bam Adebayo        MIA   C      14.8   2.7   8    34.1
##  7 Jonas Valan?i?nas  NOP   C      14.5   3    14.3  29.2
##  8 Rudy Gobert        UTA   C      12     0.5  13.2  32.8
##  9 Al Horford         BOS   C      12     3.3   9.3  35.4
## 10 DeMarcus Cousins   DEN   C      10.6   1.2   3.4  11.4
## # ℹ 40 more rows

5) Quick visualization: points per game by position


``` r
library(tidyverse)

nba_pg %>%
  ggplot(aes(x = Pos, y = PTS_PG)) +
  geom_boxplot() +
  labs(
    title = "Points per Game by Position (Playoffs)",
    x = "Position",
    y = "PTS per Game"
  ) +
  theme_minimal()


Paste the output of:

names(nba_playoffs)

```r

``` r
# Helper function to find first matching column
find_col <- function(options) intersect(names(nba_playoffs), options)[1]

player_col <- find_col(c("Player", "PlayerName", "Name"))
team_col   <- find_col(c("Tm", "Team"))
pos_col    <- find_col(c("Pos", "Position"))

pts_col <- find_col(c("PTS", "Pts", "points"))
ast_col <- find_col(c("AST", "Ast", "assists"))
trb_col <- find_col(c("TRB", "REB", "rebounds"))
stl_col <- find_col(c("STL", "Stl", "steals"))
blk_col <- find_col(c("BLK", "Blk", "blocks"))
pf_col  <- find_col(c("PF", "Fouls"))
fg_col  <- find_col(c("FG", "FGM", "FG."))

ARCHETYPE 1: Scorers

High points + efficiency proxy (FG) + usage proxy (PTS share)


``` r
nba_playoffs %>%
  mutate(
    FG_eff = if (!is.na(fg_col)) .data[[fg_col]] else NA,
    Scoring_Index = scale(.data[[pts_col]]) +
                     0.5 * scale(FG_eff)
  ) %>%
  arrange(desc(Scoring_Index)) %>%
  select(any_of(c(player_col, team_col, pos_col, pts_col, fg_col))) %>%
  head(15)
##                   Player  Tm Pos  PTS   FG
## 1           Nikola Joki? DEN   C 31.0 12.2
## 2  Giannis Antetokounmpo MIL  PF 31.7 11.7
## 3            Luka Don?i? DAL  PG 31.7 10.7
## 4           Jimmy Butler MIA  SF 27.4  9.8
## 5          Stephen Curry GSW  PG 27.4  9.2
## 6         Brandon Ingram NOP  SF 27.0  9.3
## 7              Ja Morant MEM  PG 27.1  9.0
## 8       Donovan Mitchell UTA  SG 25.5  8.8
## 9           Kevin Durant BRK  PF 26.3  8.0
## 10          Jayson Tatum BOS  SF 25.6  8.4
## 11       Anthony Edwards MIN  SG 25.2  8.3
## 12         Pascal Siakam TOR  PF 22.8  8.5
## 13          Jaylen Brown BOS  SF 23.1  8.2
## 14           Joel Embiid PHI   C 23.6  7.8
## 15          Devin Booker PHO  SG 23.3  7.9

GM use: identifies offensive engines vs volume-only scorers.


ARCHETYPE 2: Playmakers

High assists + high AST-to-PTS ratio (who creates vs who finishes)


``` r
nba_playoffs %>%
  mutate(
    AST_to_PTS = .data[[ast_col]] / pmax(.data[[pts_col]], 1)
  ) %>%
  arrange(desc(AST_to_PTS), desc(.data[[ast_col]])) %>%
  select(any_of(c(player_col, team_col, pos_col, ast_col, pts_col))) %>%
  head(15)
##                   Player  Tm Pos AST  PTS
## 1         Andre Iguodala GSW  SF 1.7  1.6
## 2           Paul Millsap PHI  PF 1.0  0.0
## 3         Draymond Green GSW  PF 6.3  8.0
## 4          Elfrid Payton PHO  PG 1.5  2.0
## 5            Alex Caruso CHI  SG 4.3  6.3
## 6           Steven Adams MEM   C 2.1  3.4
## 7             Kyle Lowry MIA  PG 4.7  7.8
## 8            George Hill MIL  SG 0.6  1.0
## 9  Juan Toscano-Anderson GSW  SF 0.6  0.8
## 10          Luca Vildoza MIL  PG 0.6  0.7
## 11      D'Angelo Russell MIN  PG 6.7 12.0
## 12           Ayo Dosunmu CHI  SG 2.2  4.0
## 13     Jordan McLaughlin MIN  PG 3.4  6.2
## 14          John Konchar MEM  SG 0.6  1.1
## 15           Mike Conley UTA  PG 4.8  9.2

📌 GM use: finds initiators, secondary creators, system facilitators.


ARCHETYPE 3: Defenders / Hustle Players

Stocks + rebounds – foul penalty


``` r
nba_playoffs %>%
  mutate(
    Defensive_Impact =
      scale(.data[[stl_col]]) +
      scale(.data[[blk_col]]) +
      scale(.data[[trb_col]]) -
      0.5 * scale(.data[[pf_col]])
  ) %>%
  arrange(desc(Defensive_Impact)) %>%
  select(any_of(c(player_col, team_col, pos_col,
                  stl_col, blk_col, trb_col, pf_col))) %>%
  head(15)
##                   Player  Tm Pos STL BLK  TRB  PF
## 1            Nic Claxton BRK   C 1.3 2.3  6.3 1.5
## 2           Nikola Joki? DEN   C 1.6 1.0 13.2 4.0
## 3     Karl-Anthony Towns MIN   C 0.7 2.0 10.8 4.2
## 4  Giannis Antetokounmpo MIL  PF 0.7 1.3 14.2 3.6
## 5      Jaren Jackson Jr. MEM  PF 0.8 2.5  6.8 4.4
## 6        Robert Williams BOS   C 0.7 2.2  6.2 2.0
## 7           Jimmy Butler MIA  SF 2.1 0.6  7.4 1.5
## 8            Luka Don?i? DAL  PG 1.8 0.6  9.8 2.9
## 9           Kyrie Irving BRK  PG 1.8 1.3  5.3 3.3
## 10             Ja Morant MEM  PG 2.0 0.4  8.0 2.4
## 11        Nikola Vu?evi? CHI   C 0.4 1.2 12.4 2.8
## 12            Al Horford BOS   C 0.8 1.3  9.3 2.8
## 13          Jrue Holiday MIL  PG 1.8 0.6  5.6 2.3
## 14           Rudy Gobert UTA   C 0.2 1.0 13.2 3.2
## 15        Andrew Wiggins GSW  SF 1.0 1.0  7.5 2.5

📌 GM use: playoff defenders, switchable wings, rim protectors.


BONUS: Low-Points, High-Impact Players

(Players who don’t score much but HELP WIN)


``` r
nba_playoffs %>%
  mutate(
    NonScoring_Impact =
      scale(.data[[ast_col]]) +
      scale(.data[[stl_col]]) +
      scale(.data[[blk_col]]) +
      scale(.data[[trb_col]])
  ) %>%
  filter(.data[[pts_col]] < median(.data[[pts_col]], na.rm = TRUE)) %>%
  arrange(desc(NonScoring_Impact)) %>%
  select(any_of(c(player_col, team_col, pos_col,
                  pts_col, ast_col, stl_col, blk_col, trb_col))) %>%
  head(15)
##               Player  Tm Pos PTS AST STL BLK TRB
## 1  Jarred Vanderbilt MIN  PF 5.5 0.7 1.2 0.3 7.2
## 2     Andre Drummond BRK   C 3.8 0.8 1.3 0.8 3.0
## 3       Kevon Looney GSW   C 5.8 2.2 0.4 0.5 7.6
## 4     Onyeka Okongwu ATL   C 5.2 0.4 0.8 0.8 5.4
## 5   Hassan Whiteside UTA   C 1.8 0.0 0.3 1.3 5.2
## 6  De'Anthony Melton MEM  SG 5.6 1.6 1.0 0.5 3.1
## 7      Javonte Green CHI  SF 1.4 0.4 1.8 0.0 3.0
## 8       Clint Capela ATL   C 2.0 0.0 0.5 0.5 7.5
## 9    Otto Porter Jr. GSW  PF 5.4 1.8 0.9 0.3 3.4
## 10         Paul Reed PHI   C 3.7 0.8 0.8 0.5 3.8
## 11     Austin Rivers DEN  SG 4.2 1.2 1.4 0.2 0.6
## 12          Naz Reid MIN   C 4.8 0.0 0.2 1.2 2.8
## 13    Thaddeus Young TOR  PF 3.3 1.7 0.8 0.2 3.0
## 14  Matisse Thybulle PHI  SG 3.0 0.4 0.8 0.8 1.0
## 15     Blake Griffin BRK  PF 4.0 2.0 0.5 0.5 2.0

``` r
find_col <- function(options) intersect(names(nba_playoffs), options)[1]

player_col <- find_col(c("Player", "PlayerName", "Name"))
team_col   <- find_col(c("Tm", "Team"))
pos_col    <- find_col(c("Pos", "Position"))

pts_col <- find_col(c("PTS", "Pts", "points"))
fg_col  <- find_col(c("FG", "FGM", "FG."))
fga_col <- find_col(c("FGA", "FieldGoalsAttempted"))
mp_col  <- find_col(c("MP", "Minutes", "MIN"))
ast_col <- find_col(c("AST", "Ast"))
tov_col <- find_col(c("TOV", "TO", "Turnovers"))
trb_col <- find_col(c("TRB", "REB", "Rebounds"))

METRIC 1: FG vs FGA (Shot Efficiency)

Who converts attempts into makes


``` r
nba_playoffs %>%
  mutate(
    FG_Efficiency = .data[[fg_col]] / pmax(.data[[fga_col]], 1)
  ) %>%
  arrange(desc(FG_Efficiency)) %>%
  select(any_of(c(player_col, team_col, pos_col,
                  fg_col, fga_col))) %>%
  head(15)
##                      Player  Tm Pos  FG FGA
## 1  Nickeil Alexander-Walker UTA  SG 2.0 2.0
## 2              Tony Bradley CHI   C 2.5 2.5
## 3            DeAndre Jordan PHI   C 1.7 1.7
## 4               Nic Claxton BRK   C 4.8 6.0
## 5        Xavier Tillman Sr. MEM  PF 2.0 2.8
## 6             Naji Marshall NOP  SF 1.2 1.7
## 7         Jordan McLaughlin MIN  PG 2.4 3.4
## 8              JaVale McGee PHO   C 2.9 4.2
## 9           Robert Williams BOS   C 3.1 4.6
## 10            Vlatko ?an?ar DEN  PF 1.0 1.5
## 11            Elfrid Payton PHO  PG 1.0 1.5
## 12         DeMarcus Cousins DEN   C 3.8 5.8
## 13             Kevon Looney GSW   C 2.6 4.0
## 14           Omer Yurtseven MIA   C 1.3 2.0
## 15           Gary Payton II GSW  PG 2.4 3.7

GM insight: filters out high-volume, low-efficiency shooters.


METRIC 2: Points per Minute (PTS / MP)

True scoring impact independent of minutes


``` r
nba_playoffs %>%
  mutate(
    PTS_per_Min = .data[[pts_col]] / pmax(.data[[mp_col]], 1)
  ) %>%
  arrange(desc(PTS_per_Min)) %>%
  select(any_of(c(player_col, team_col, pos_col,
                  pts_col, mp_col))) %>%
  head(15)
##                      Player  Tm Pos  PTS   MP
## 1                Kevin Knox ATL  SF 11.0  4.5
## 2              Tony Bradley CHI   C  5.0  4.0
## 3             Aaron Holiday PHO  PG  3.5  3.3
## 4  Nickeil Alexander-Walker UTA  SG  5.0  5.0
## 5         Willy Hernangómez NOP   C  2.0  2.0
## 6          DeMarcus Cousins DEN   C 10.6 11.4
## 7              Nikola Joki? DEN   C 31.0 34.2
## 8             Dalano Banton TOR  PG  1.8  2.0
## 9                Trey Burke DAL  PG  3.2  3.7
## 10              Luka Don?i? DAL  PG 31.7 36.8
## 11    Giannis Antetokounmpo MIL  PF 31.7 37.3
## 12            Stephen Curry GSW  PG 27.4 34.7
## 13             Jimmy Butler MIA  SF 27.4 37.0
## 14                Ja Morant MEM  PG 27.1 37.6
## 15           Brandon Ingram NOP  SF 27.0 39.3

GM insight: identifies bench scorers who scale up.


METRIC 3: AST per Turnover (AST / TOV)

Decision-making efficiency


``` r
nba_playoffs %>%
  mutate(
    AST_to_TOV = .data[[ast_col]] / pmax(.data[[tov_col]], 1)
  ) %>%
  arrange(desc(AST_to_TOV)) %>%
  select(any_of(c(player_col, team_col, pos_col,
                  ast_col, tov_col))) %>%
  head(15)
##               Player  Tm Pos AST TOV
## 1      Fred VanVleet TOR  PG 6.3 1.0
## 2        Alex Caruso CHI  SG 4.3 0.3
## 3         Tyus Jones MEM  PG 4.5 1.1
## 4         Chris Paul PHO  PG 8.3 2.4
## 5  Jordan McLaughlin MIN  PG 3.4 0.4
## 6       Monte Morris DEN  PG 5.4 1.6
## 7      Jalen Brunson DAL  PG 3.7 1.1
## 8     Scottie Barnes TOR  PF 4.3 1.3
## 9       Jimmy Butler MIA  SF 4.6 1.5
## 10 Bogdan Bogdanovi? ATL  SG 3.0 1.0
## 11        Seth Curry BRK  SG 3.0 0.8
## 12     Mikal Bridges PHO  SF 2.8 0.8
## 13      Delon Wright ATL  SG 2.8 1.0
## 14         Ja Morant MEM  PG 9.8 3.6
## 15      Bones Hyland DEN  PG 3.2 1.2

GM insight: playoff-safe ball handlers.


METRIC 4: Rebounds per Minute

Hustle + positioning independent of minutes


``` r
nba_playoffs %>%
  mutate(
    TRB_per_Min = .data[[trb_col]] / pmax(.data[[mp_col]], 1)
  ) %>%
  arrange(desc(TRB_per_Min)) %>%
  select(any_of(c(player_col, team_col, pos_col,
                  trb_col, mp_col))) %>%
  head(15)
##                   Player  Tm Pos  TRB   MP
## 1      Willy Hernangómez NOP   C  2.0  2.0
## 2           Tony Bradley CHI   C  2.0  4.0
## 3       Boban Marjanovi? DAL   C  1.0  2.0
## 4         Garrett Temple NOP  SG  1.0  2.0
## 5      Jonas Valan?i?nas NOP   C 14.3 29.2
## 6       Hassan Whiteside UTA   C  5.2 10.8
## 7            Serge Ibaka MIL  PF  1.7  3.7
## 8          Ish Wainright PHO  PF  1.6  3.7
## 9         Charles Bassey PHI  PF  1.7  4.0
## 10          Bobby Portis MIL   C 10.0 24.8
## 11           Rudy Gobert UTA   C 13.2 32.8
## 12          Steven Adams MEM   C  6.4 16.3
## 13          Nikola Joki? DEN   C 13.2 34.2
## 14 Giannis Antetokounmpo MIL  PF 14.2 37.3
## 15          Clint Capela ATL   C  7.5 20.0

Efficiency > Volume Composite Score** This directly answers your last point.


``` r
nba_playoffs %>%
  mutate(
    Efficiency_Score =
      scale(.data[[pts_col]] / pmax(.data[[mp_col]], 1)) +     # scoring efficiency
      scale(.data[[ast_col]] / pmax(.data[[tov_col]], 1)) +    # decision-making
      scale(.data[[trb_col]] / pmax(.data[[mp_col]], 1))       # hustle
  ) %>%
  arrange(desc(Efficiency_Score)) %>%
  select(any_of(c(player_col, team_col, pos_col,
                  pts_col, ast_col, trb_col, mp_col))) %>%
  head(20)
##                      Player  Tm Pos  PTS AST  TRB   MP
## 1         Willy Hernangómez NOP   C  2.0 0.0  2.0  2.0
## 2                Kevin Knox ATL  SF 11.0 0.0  1.0  4.5
## 3              Tony Bradley CHI   C  5.0 0.5  2.0  4.0
## 4             Fred VanVleet TOR  PG 13.8 6.3  3.0 35.0
## 5     Giannis Antetokounmpo MIL  PF 31.7 6.8 14.2 37.3
## 6              Nikola Joki? DEN   C 31.0 5.8 13.2 34.2
## 7              Jimmy Butler MIA  SF 27.4 4.6  7.4 37.0
## 8         Jonas Valan?i?nas NOP   C 14.5 3.0 14.3 29.2
## 9                 Ja Morant MEM  PG 27.1 9.8  8.0 37.6
## 10              Luka Don?i? DAL  PG 31.7 6.4  9.8 36.8
## 11           Scottie Barnes TOR  PF 12.8 4.3  9.0 33.3
## 12               Tyus Jones MEM  PG  9.2 4.5  3.3 21.8
## 13         DeMarcus Cousins DEN   C 10.6 1.2  3.4 11.4
## 14           Nikola Vu?evi? CHI   C 19.4 3.2 12.4 36.2
## 15            Aaron Holiday PHO  PG  3.5 1.5  0.5  3.3
## 16            Jalen Brunson DAL  PG 21.6 3.7  4.6 34.9
## 17         Boban Marjanovi? DAL   C  1.3 0.0  1.0  2.0
## 18            Stephen Curry GSW  PG 27.4 5.9  5.2 34.7
## 19        Bogdan Bogdanovi? ATL  SG 14.3 3.0  4.8 26.8
## 20 Nickeil Alexander-Walker UTA  SG  5.0 1.0  1.0  5.0

Step 1: Detect correct column names (critical)

find_col <- function(options) intersect(names(nba_playoffs), options)[1]

player_col <- find_col(c("Player", "PlayerName", "Name"))
team_col   <- find_col(c("Tm", "Team"))
pos_col    <- find_col(c("Pos", "Position"))

pts_col <- find_col(c("PTS", "Pts", "points"))
ast_col <- find_col(c("AST", "Ast", "assists"))
trb_col <- find_col(c("TRB", "REB", "Rebounds"))
mp_col  <- find_col(c("MP", "Minutes", "MIN"))

SCATTERPLOT 1: Minutes Played vs Points

This shows usage vs scoring return.


``` r
nba_playoffs %>%
  ggplot(aes(
    x = .data[[mp_col]],
    y = .data[[pts_col]]
  )) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "lm", se = FALSE) +
  labs(
    title = "Minutes Played vs Points (Playoffs)",
    x = "Minutes Played",
    y = "Total Points"
  ) +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

GM read - Above trend line → efficient scorers - Below trend line → empty minutes


SCATTERPLOT 2: Minutes vs Total Contributions

Total Contributions = PTS + AST + TRB


``` r
nba_playoffs %>%
  mutate(
    Total_Contrib =
      .data[[pts_col]] +
      .data[[ast_col]] +
      .data[[trb_col]]
  ) %>%
  ggplot(aes(
    x = .data[[mp_col]],
    y = Total_Contrib
  )) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "lm", se = FALSE) +
  labs(
    title = "Minutes Played vs Total Contributions",
    x = "Minutes Played",
    y = "PTS + AST + TRB"
  ) +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'

High Minutes, Low Output → Possible Inefficiency


``` r
nba_playoffs %>%
  mutate(
    Total_Contrib =
      .data[[pts_col]] +
      .data[[ast_col]] +
      .data[[trb_col]]
  ) %>%
  filter(
    .data[[mp_col]] > median(.data[[mp_col]], na.rm = TRUE),
    Total_Contrib < median(Total_Contrib, na.rm = TRUE)
  ) %>%
  arrange(desc(.data[[mp_col]])) %>%
  select(any_of(c(player_col, team_col, pos_col,
                  mp_col, pts_col, ast_col, trb_col)))
##             Player  Tm Pos   MP PTS AST TRB
## 1  Wesley Matthews MIL  SG 28.8 6.2 1.2 3.1
## 2       Jeff Green DEN   C 22.6 3.8 0.4 3.6
## 3    Austin Rivers DEN  SG 21.6 4.2 1.2 0.6
## 4     Clint Capela ATL   C 20.0 2.0 0.0 7.5
## 5  Trey Murphy III NOP  SF 20.0 5.2 0.5 2.5
## 6  Otto Porter Jr. GSW  PF 19.5 5.4 1.8 3.4
## 7 Danuel House Jr. UTA  SF 18.7 4.3 0.7 2.8

Low Minutes, High Output → Rotation Upside


``` r
nba_playoffs %>%
  mutate(
    Total_Contrib =
      .data[[pts_col]] +
      .data[[ast_col]] +
      .data[[trb_col]]
  ) %>%
  filter(
    .data[[mp_col]] < median(.data[[mp_col]], na.rm = TRUE),
    Total_Contrib > median(Total_Contrib, na.rm = TRUE)
  ) %>%
  arrange(desc(Total_Contrib)) %>%
  select(any_of(c(player_col, team_col, pos_col,
                  mp_col, pts_col, ast_col, trb_col)))
##              Player  Tm Pos   MP  PTS AST TRB
## 1  DeMarcus Cousins DEN   C 11.4 10.6 1.2 3.4
## 2      Bones Hyland DEN  PG 17.4  9.2 3.2 2.0
## 3        Kevin Knox ATL  SF  4.5 11.0 0.0 1.0
## 4 Jordan McLaughlin MIN  PG 16.6  6.2 3.4 2.4
## 5      Steven Adams MEM   C 16.3  3.4 2.1 6.4
## 6      JaVale McGee PHO   C 11.1  6.8 0.6 4.0
## 7    Gary Payton II GSW  PG 16.9  6.5 1.3 3.1
nba_pm <- nba_playoffs %>%
  mutate(
    PTS_PM = .data[[pts_col]] / pmax(.data[[mp_col]], 1),
    AST_PM = .data[[ast_col]] / pmax(.data[[mp_col]], 1),
    TRB_PM = .data[[trb_col]] / pmax(.data[[mp_col]], 1)
  )

2) Compare per-minute production by position (TABLE)


``` r
nba_pm %>%
  group_by(.data[[pos_col]]) %>%
  summarise(
    Players = n(),
    Avg_PTS_PM = mean(PTS_PM, na.rm = TRUE),
    Avg_AST_PM = mean(AST_PM, na.rm = TRUE),
    Avg_TRB_PM = mean(TRB_PM, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(Avg_PTS_PM))
## # A tibble: 5 × 5
##   Pos   Players Avg_PTS_PM Avg_AST_PM Avg_TRB_PM
##   <chr>   <int>      <dbl>      <dbl>      <dbl>
## 1 C          40      0.448     0.0558      0.314
## 2 PG         39      0.445     0.151       0.132
## 3 SF         39      0.425     0.0775      0.153
## 4 SG         49      0.363     0.0800      0.144
## 5 PF         50      0.351     0.0627      0.197

3) Visualization: Per-minute production by position

Scoring efficiency (PTS per minute)


``` r
nba_pm %>%
  ggplot(aes(x = .data[[pos_col]], y = PTS_PM)) +
  geom_boxplot() +
  labs(
    title = "Points per Minute by Position (Playoffs)",
    x = "Position",
    y = "PTS per Minute"
  ) +
  theme_minimal()


``` r
nba_pm %>%
  ggplot(aes(x = .data[[pos_col]], y = AST_PM)) +
  geom_boxplot() +
  labs(
    title = "Assists per Minute by Position (Playoffs)",
    x = "Position",
    y = "AST per Minute"
  ) +
  theme_minimal()


``` r
nba_pm %>%
  ggplot(aes(x = .data[[pos_col]], y = TRB_PM)) +
  geom_boxplot() +
  labs(
    title = "Rebounds per Minute by Position (Playoffs)",
    x = "Position",
    y = "TRB per Minute"
  ) +
  theme_minimal()

  1. Rank positions by efficiency composite (GM-style)

``` r
nba_pm %>%
  group_by(.data[[pos_col]]) %>%
  summarise(
    Efficiency_Score =
      mean(scale(PTS_PM), na.rm = TRUE) +
      mean(scale(AST_PM), na.rm = TRUE) +
      mean(scale(TRB_PM), na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(Efficiency_Score))
## # A tibble: 5 × 2
##   Pos   Efficiency_Score
##   <chr>            <dbl>
## 1 PG            1.30e-15
## 2 PF            6.82e-16
## 3 SG            1.22e-16
## 4 SF           -1.30e-16
## 5 C            -1.14e-15
library(tidyverse)

# Build efficiency table (must exist before outlier chunks)
nba_eff <- nba_playoffs %>%
  mutate(
    Total_Contrib = .data[[pts_col]] + .data[[ast_col]] + .data[[trb_col]],
    Contrib_per_Min = Total_Contrib / pmax(.data[[mp_col]], 1)
  )

🌟 GOOD OUTLIERS: Low Minutes, High Efficiency


``` r
nba_eff %>%
  filter(
    .data[[mp_col]] < median(.data[[mp_col]], na.rm = TRUE),
    Contrib_per_Min > median(Contrib_per_Min, na.rm = TRUE)
  ) %>%
  arrange(desc(Contrib_per_Min)) %>%
  select(any_of(c(
    player_col, team_col, pos_col,
    mp_col, pts_col, ast_col, trb_col
  )))
##                      Player  Tm Pos   MP  PTS AST TRB
## 1                Kevin Knox ATL  SF  4.5 11.0 0.0 1.0
## 2         Willy Hernangómez NOP   C  2.0  2.0 0.0 2.0
## 3              Tony Bradley CHI   C  4.0  5.0 0.5 2.0
## 4             Aaron Holiday PHO  PG  3.3  3.5 1.5 0.5
## 5  Nickeil Alexander-Walker UTA  SG  5.0  5.0 1.0 1.0
## 6          DeMarcus Cousins DEN   C 11.4 10.6 1.2 3.4
## 7             Dalano Banton TOR  PG  2.0  1.8 0.3 0.5
## 8          Boban Marjanovi? DAL   C  2.0  1.3 0.0 1.0
## 9                Trey Burke DAL  PG  3.7  3.2 0.4 0.3
## 10             JaVale McGee PHO   C 11.1  6.8 0.6 4.0
## 11              Isaac Bonga TOR  SF  3.0  2.0 0.0 1.0
## 12              Greg Monroe MIN   C  3.5  2.0 0.5 1.0
## 13            Ish Wainright PHO  PF  3.7  1.9 0.1 1.6
## 14           Omer Yurtseven MIA   C  4.2  2.8 0.3 0.8
## 15           Jaden Springer PHI  SG  2.6  1.2 0.4 0.8
## 16             Nik Stauskas BOS  SG  1.8  1.0 0.3 0.3
## 17            Vlatko ?an?ar DEN  PF  4.5  2.5 0.5 1.0
## 18            Elfrid Payton PHO  PG  4.0  2.0 1.5 0.0
## 19              Serge Ibaka MIL  PF  3.7  1.5 0.0 1.7
## 20         Jonathan Kuminga GSW  SF  8.6  5.2 0.5 1.7
## 21           Sterling Brown DAL  SG  2.9  1.2 0.3 0.9
## 22             Bones Hyland DEN  PG 17.4  9.2 3.2 2.0
## 23          Marquese Chriss DAL  PF  3.8  1.8 0.0 1.1
## 24             Steven Adams MEM   C 16.3  3.4 2.1 6.4
## 25           Dewayne Dedmon MIA   C  9.9  3.8 0.4 3.0
## 26        Jordan McLaughlin MIN  PG 16.6  6.2 3.4 2.4
## 27              Luke Kornet BOS   C  2.1  0.8 0.1 0.6
## 28                Paul Reed PHI   C 11.7  3.7 0.8 3.8
## 29           Furkan Korkmaz PHI  SG  6.8  3.1 0.4 1.3
## 30                 Naz Reid MIN   C 10.8  4.8 0.0 2.8
## 31           Taurean Prince MIN  PF 13.0  6.0 1.2 1.6
## 32           Charles Bassey PHI  PF  4.0  0.7 0.3 1.7
## 33           Jarrett Culver MEM  SG  7.3  2.3 0.3 2.3
## 34             Luca Vildoza MIL  PG  2.4  0.7 0.6 0.3
## 35             Daniel Theis BOS   C 12.5  4.3 0.7 3.3
## 36         Hassan Whiteside UTA   C 10.8  1.8 0.0 5.2
## 37           Gary Payton II GSW  PG 16.9  6.5 1.3 3.1
## 38         Payton Pritchard BOS  SG 12.9  4.8 1.6 1.9
## 39            Blake Griffin BRK  PF 12.5  4.0 2.0 2.0

🚨 BAD OUTLIERS: High Minutes, Low Efficiency


``` r
nba_eff %>%
  filter(
    .data[[mp_col]] > median(.data[[mp_col]], na.rm = TRUE),
    Contrib_per_Min < median(Contrib_per_Min, na.rm = TRUE)
  ) %>%
  arrange(.data[[mp_col]]) %>%
  select(any_of(c(
    player_col, team_col, pos_col,
    mp_col, pts_col, ast_col, trb_col
  )))
##                 Player  Tm Pos   MP  PTS AST TRB
## 1     Danuel House Jr. UTA  SF 18.7  4.3 0.7 2.8
## 2        Jose Alvarado NOP  PG 19.5  8.0 1.5 1.3
## 3      Otto Porter Jr. GSW  PF 19.5  5.4 1.8 3.4
## 4        Malik Beasley MIN  SG 19.8  8.5 0.7 3.3
## 5         Clint Capela ATL   C 20.0  2.0 0.0 7.5
## 6      Trey Murphy III NOP  SF 20.0  5.2 0.5 2.5
## 7    Jarred Vanderbilt MIN  PF 21.5  5.5 0.7 7.2
## 8       Onyeka Okongwu ATL   C 21.6  5.2 0.4 5.4
## 9        Austin Rivers DEN  SG 21.6  4.2 1.2 0.6
## 10     Jaden McDaniels MIN  PF 21.7  9.3 0.7 2.8
## 11          Jeff Green DEN   C 22.6  3.8 0.4 3.6
## 12        Gabe Vincent MIA  PG 23.5  8.0 3.2 1.9
## 13        John Collins ATL  PF 24.4  9.4 1.2 4.6
## 14       Grayson Allen MIL  SG 25.4  8.3 1.3 2.9
## 15         Maxi Kleber DAL  PF 25.4  8.7 1.1 4.6
## 16       Derrick White BOS  SG 25.4  8.5 2.7 3.0
## 17     Pat Connaughton MIL  SG 26.5  9.5 0.9 4.3
## 18         Danny Green PHI  SF 26.6  8.6 0.8 3.1
## 19      Grant Williams BOS  PF 27.3  8.6 0.8 3.8
## 20        Delon Wright ATL  SG 27.4  8.2 2.8 4.8
## 21         Brook Lopez MIL   C 27.7 10.6 0.7 5.9
## 22    Precious Achiuwa TOR   C 27.8 10.2 1.0 4.8
## 23         Alex Caruso CHI  SG 28.3  6.3 4.3 2.8
## 24         P.J. Tucker MIA  PF 28.3  7.9 1.8 5.7
## 25     Wesley Matthews MIL  SG 28.8  6.2 1.2 3.1
## 26         Mike Conley UTA  PG 29.0  9.2 4.8 3.2
## 27           Max Strus MIA  SF 29.1 10.9 2.1 4.1
## 28         Jae Crowder PHO  PF 29.5  9.4 2.4 4.7
## 29          Kyle Lowry MIA  PG 29.5  7.8 4.7 3.6
## 30    Patrick Williams CHI  PF 30.6 11.8 0.8 5.4
## 31       Kevin Huerter ATL  SG 30.8  9.2 3.8 3.0
## 32       Royce O'Neale UTA  SF 31.3  6.2 1.5 5.7
## 33    Patrick Beverley MIN  PG 32.3 11.0 4.8 3.2
## 34          Seth Curry BRK  SG 33.0 14.5 3.0 2.5
## 35      Gary Trent Jr. TOR  SG 33.2 15.3 1.3 1.7
## 36         Bruce Brown BRK  SF 34.8 14.0 2.8 4.8
## 37       Herbert Jones NOP  PF 37.7 10.7 1.8 3.3
## 38 Dorian Finney-Smith DAL  PF 38.2 11.7 1.9 5.5
## 39       Mikal Bridges PHO  SF 38.5 13.3 2.8 4.7
## 40      Reggie Bullock DAL  SF 39.3 10.6 1.7 4.6

Good outliers represent players who deliver above-average impact despite limited minutes, suggesting strong efficiency and potential upside if their role expands.

Bad outliers are players receiving heavy minutes but producing below-average impact per minute, indicating possible inefficiency that becomes more exposed in playoff basketball where rotations tighten and possessions matter more.


🏀 Final GM Insight (1 sentence, very strong)

Playoff basketball rewards efficiency over volume, elevating low-usage, high-impact contributors while exposing high-minute players who fail to convert usage into value.

If you want, I can: - Label these players directly on a scatterplot - Rank teams by rotation efficiency - Help you write a final conclusion section