“I’ve always said that LeBron is a physical marvel, but Michael Jordan
is the ultimate competitor.”
– Charles Barkley
“Kobe Bryant had an unmatched work
ethic, but LeBron’s versatility changes the game.” – Shaquille
O’Neal
The debate about who is the greatest basketball player of all time (the “GOAT”) has fueled endless discussions among fans, analysts, and players alike.
This report focuses on a statistical comparison of three legends of the game:
Using season and playoff statistics, this analysis compares their
scoring, efficiency, accolades, and career
trajectories.
The goal is not to provide a definitive answer, but to highlight trends
and achievements that inform the GOAT conversation.
data collected from Basketballreference.com
Lets take a look at the data referenced from Basketballreference.com
## 'data.frame': 15 obs. of 18 variables:
## $ season : chr "1984-85" "1985-86" "1986-87" "1987-88" ...
## $ games_played: int 82 18 82 82 81 82 82 80 78 17 ...
## $ fg_percent : num 0.515 0.457 0.482 0.535 0.538 0.526 0.539 0.519 0.495 0.411 ...
## $ fg_3percent : num 0.173 0.167 0.182 0.132 0.276 0.376 0.312 0.27 0.352 0.5 ...
## $ fg_2percent : num 0.526 0.474 0.491 0.546 0.553 0.548 0.551 0.533 0.514 0.403 ...
## $ e_fg_percent: num 0.518 0.462 0.484 0.537 0.546 0.55 0.547 0.526 0.515 0.431 ...
## $ ft_percent : num 0.845 0.84 0.857 0.841 0.85 0.848 0.851 0.832 0.837 0.801 ...
## $ apg : num 5.9 2.9 4.6 5.9 8 6.3 5.5 6.1 5.5 5.3 ...
## $ rpg : num 6.5 3.6 5.2 5.5 8 6.9 6 6.4 6.7 6.9 ...
## $ spg : num 2.4 2.1 2.9 3.2 2.9 2.8 2.7 2.3 2.8 1.8 ...
## $ bpg : num 0.8 1.2 1.5 1.6 0.8 0.7 1 0.9 0.8 0.8 ...
## $ ppg : num 28.2 22.7 37.1 35 32.5 33.6 31.5 30.1 32.6 26.9 ...
## $ game_high : int 49 33 61 59 53 69 46 51 64 55 ...
## $ ts_percent : num 0.592 0.533 0.562 0.603 0.614 0.606 0.605 0.579 0.564 0.493 ...
## $ per : num 25.8 27.5 29.8 31.7 31.1 31.2 31.6 27.7 29.7 22.1 ...
## $ vorp : num 7.4 1.3 10.6 12.5 11.4 10.6 10.8 9.2 10.2 1.1 ...
## $ ws : num 14 1.5 16.9 21.2 19.8 19 20.3 17.7 17.2 2.3 ...
## $ bpm : num 7.3 9.7 10.8 13 11.9 11.2 12 9.7 11.2 4.2 ...
#Jordan Full Season
## 'data.frame': 13 obs. of 18 variables:
## $ season : chr "1984-85" "1985-86" "1986-87" "1987-88" ...
## $ games_played: int 4 3 3 10 17 16 17 22 19 10 ...
## $ fg_percent : num 0.436 0.505 0.417 0.531 0.51 0.514 0.524 0.499 0.475 0.484 ...
## $ fg_3percent : num 0.125 1 0.4 0.333 0.286 0.32 0.385 0.386 0.389 0.367 ...
## $ fg_2perent : num 0.471 0.5 0.418 0.533 0.532 0.54 0.534 0.508 0.489 0.5 ...
## $ e_fg_percent: num 0.442 0.511 0.429 0.533 0.523 0.533 0.537 0.514 0.502 0.506 ...
## $ ft_percent : num 0.828 0.872 0.897 0.869 0.799 0.836 0.845 0.857 0.805 0.81 ...
## $ apg : num 8.5 5.7 6 4.7 7.6 6.8 8.4 5.8 6 4.5 ...
## $ rpg : num 5.8 6.3 7 7.1 7 7.2 6.4 6.2 6.7 6.5 ...
## $ spg : num 2.8 2.3 2 2.4 2.5 2.8 2.4 2 2.1 2.3 ...
## $ bpg : num 1 1.3 2.3 1.1 0.8 0.9 1.4 0.7 0.9 1.4 ...
## $ ppg : num 29.3 43.7 35.7 36.3 34.8 36.7 31.1 34.5 35.1 31.5 ...
## $ game_high : int 35 63 42 55 50 49 46 56 55 48 ...
## $ ts_percent : num 0.565 0.584 0.529 0.598 0.602 0.592 0.6 0.571 0.553 0.557 ...
## $ per : num 24.7 30.1 28.1 28.4 29.9 31.7 32 27.2 30.1 24.8 ...
## $ vorp : num 0.5 0.4 0.5 1.5 2.5 2.7 2.9 2.8 2.7 1 ...
## $ ws : num 0.7 0.5 0.4 2.1 4 4 4.8 4.1 4.4 1.3 ...
## $ bpm : num 9.5 11.9 12.7 12.2 12.1 13.7 14.6 9.9 11.6 8 ...
#Jordan Full Playoffs
## 'data.frame': 22 obs. of 18 variables:
## $ season : chr "2003-04" "2004-05" "2005-06" "2006-07" ...
## $ games_played : int 79 80 79 78 75 81 76 79 62 76 ...
## $ fg_percent : num 0.417 0.472 0.48 0.476 0.484 0.489 0.503 0.51 0.531 0.565 ...
## $ fg_3percent : num 0.29 0.351 0.335 0.319 0.315 0.344 0.333 0.33 0.362 0.406 ...
## $ fg_2percent : num 0.438 0.499 0.518 0.513 0.531 0.535 0.56 0.552 0.556 0.602 ...
## $ e_fg_perecent: num 0.438 0.504 0.515 0.507 0.518 0.53 0.545 0.541 0.554 0.603 ...
## $ ft_percent : num 0.754 0.75 0.738 0.698 0.712 0.78 0.767 0.759 0.771 0.753 ...
## $ rpg : num 5.5 7.4 7 6.7 7.9 7.6 7.3 7.5 7.9 8 ...
## $ apg : num 5.9 7.2 6.6 6 7.2 7.2 8.6 7 6.2 7.3 ...
## $ spg : num 1.6 2.2 1.6 1.6 1.8 1.7 1.6 1.6 1.9 1.7 ...
## $ bpg : num 0.7 0.7 0.8 0.7 1.1 1.1 1 0.6 0.8 0.9 ...
## $ ppg : num 20.9 27.2 31.4 27.3 30 28.4 29.7 26.7 27.1 26.8 ...
## $ game_high : int 41 56 52 41 51 55 48 51 41 40 ...
## $ per : num 18.3 25.7 28.1 24.5 29.1 31.7 31.1 27.3 30.7 31.6 ...
## $ ts_percent : num 0.488 0.554 0.568 0.552 0.568 0.591 0.604 0.594 0.605 0.64 ...
## $ ws : num 5.1 14.3 16.3 13.7 15.2 20.3 18.5 15.6 14.5 19.3 ...
## $ bpm : num 1.7 8.6 9.1 8.1 10.9 13.2 11.8 8.1 10.9 11.7 ...
## $ vorp : num 2.9 9.1 9.4 8.1 9.8 11.8 10.3 7.8 7.6 9.9 ...
#Lebron Full Season
## 'data.frame': 18 obs. of 18 variables:
## $ season : chr "2005-06" "2006-07" "2007-08" "2008-09" ...
## $ games_played: int 13 20 13 14 11 21 23 23 20 20 ...
## $ fg_percent : num 0.476 0.416 0.411 0.51 0.502 0.466 0.5 0.491 0.565 0.417 ...
## $ fg_3percent : num 0.333 0.28 0.257 0.333 0.4 0.353 0.259 0.375 0.407 0.227 ...
## $ fg_2percent : num 0.512 0.448 0.463 0.571 0.534 0.5 0.549 0.524 0.618 0.465 ...
## $ e_fg_percent: num 0.51 0.442 0.444 0.553 0.55 0.507 0.522 0.532 0.616 0.44 ...
## $ ft_percent : num 0.737 0.755 0.731 0.749 0.733 0.763 0.739 0.777 0.806 0.731 ...
## $ apg : num 5.8 8 7.6 7.3 7.6 5.9 5.6 6.6 4.8 8.5 ...
## $ rpg : num 8.1 8.1 7.8 9.1 9.3 8.4 9.7 8.4 7.1 11.3 ...
## $ spg : num 1.4 1.7 1.8 1.6 1.7 1.7 1.9 1.8 1.8 1.7 ...
## $ bpg : num 0.7 0.5 1.3 0.9 1.8 1.2 0.7 0.8 0.6 1.1 ...
## $ ppg : num 30.8 25.1 28.2 35.3 29.1 23.7 30.3 25.9 27.4 30.1 ...
## $ game_high : int 45 48 45 49 40 35 45 37 49 44 ...
## $ ts_percent : num 0.557 0.516 0.525 0.618 0.607 0.563 0.576 0.585 0.668 0.487 ...
## $ per : num 23.2 23.9 24.3 37.4 28.6 23.7 30.3 28.1 31 25.3 ...
## $ vorp : num 1.4 2.2 1.7 2.9 1.6 2.1 3.1 3 2.4 2.1 ...
## $ ws : num 1.7 3.7 2.2 4.8 2.3 3.8 5.8 5.2 4.3 3 ...
## $ bpm : num 7.5 8.1 10.1 17.5 11.5 7.1 10.5 10.4 10.3 7.9 ...
#Lebron Full Playoffs
## 'data.frame': 20 obs. of 18 variables:
## $ season : chr "1996-97" "1997-98" "1998-99" "1999-00" ...
## $ games_played: int 71 79 50 66 68 80 82 65 66 80 ...
## $ fg_percent : num 0.417 0.428 0.465 0.468 0.464 0.469 0.451 0.438 0.433 0.45 ...
## $ fg_3percent : num 0.375 0.341 0.267 0.319 0.305 0.25 0.383 0.327 0.339 0.347 ...
## $ fg_2percent : num 0.437 0.456 0.494 0.489 0.489 0.489 0.465 0.463 0.472 0.482 ...
## $ e_fg_percent: num 0.477 0.469 0.482 0.488 0.484 0.479 0.483 0.468 0.482 0.491 ...
## $ ft_percent : num 0.819 0.794 0.839 0.821 0.853 0.829 0.843 0.852 0.816 0.85 ...
## $ apg : num 1.3 2.5 3.8 4.9 5 5.5 5.9 5.1 6 4.5 ...
## $ rpg : num 1.9 3.1 5.3 6.3 5.9 5.5 6.9 5.5 5.9 5.3 ...
## $ spg : num 0.7 0.9 1.4 1.6 1.7 1.5 2.2 1.7 1.3 1.8 ...
## $ bpg : num 0.3 0.5 1 0.9 0.6 0.4 0.8 0.4 0.8 0.4 ...
## $ ppg : num 7.6 15.4 19.9 22.5 28.5 25.2 30 24 27.6 35.4 ...
## $ game_high : int 24 33 38 40 51 56 55 45 48 81 ...
## $ ts_percent : num 0.544 0.548 0.549 0.546 0.552 0.544 0.55 0.551 0.563 0.559 ...
## $ per : num 14.4 18.5 18.9 21.7 24.5 23.2 26.2 23.7 23.3 28 ...
## $ vorp : num 0.5 1.8 1.9 4.5 4.7 5.2 7.7 4.7 4.7 8 ...
## $ ws : num 1.8 6.3 5.2 10.6 11.3 12.7 14.9 10.7 8.1 15.3 ...
## $ bpm : num -0.1 1.4 2.1 5.1 4.8 4.6 7.1 5.6 5 7.6 ...
#Kobe Full Season
## 'data.frame': 15 obs. of 18 variables:
## $ season : chr "1996-97" "1997-98" "1998-99" "1999-00" ...
## $ games_played: int 9 11 8 22 16 19 12 22 7 5 ...
## $ fg_percent : num 0.382 0.408 0.43 0.442 0.469 0.434 0.432 0.413 0.497 0.462 ...
## $ fg_3percent : num 0.261 0.214 0.348 0.344 0.324 0.379 0.403 0.247 0.4 0.357 ...
## $ fg_2percent : num 0.469 0.452 0.445 0.461 0.485 0.442 0.439 0.457 0.527 0.49 ...
## $ e_fg_percent: num 0.436 0.428 0.458 0.47 0.485 0.459 0.472 0.439 0.545 0.5 ...
## $ ft_percent : num 0.867 0.689 0.8 0.754 0.821 0.759 0.827 0.813 0.771 0.919 ...
## $ apg : num 1.2 1.5 4.6 4.4 6.1 4.6 5.2 5.5 5.1 4.4 ...
## $ rpg : num 1.2 1.9 6.9 4.5 7.3 5.8 5.1 4.7 6.3 5.2 ...
## $ spg : num 0.3 0.3 1.9 1.5 1.6 1.4 1.2 1.9 1.1 1 ...
## $ bpg : num 0.2 0.7 1.3 1.5 0.8 0.9 0.1 0.3 0.4 0.4 ...
## $ ppg : num 8.2 8.7 19.8 21.1 29.4 26.6 32.1 24.5 27.9 32.8 ...
## $ game_high : int 22 22 28 35 48 36 39 42 50 45 ...
## $ ts_percent : num 0.543 0.501 0.502 0.517 0.555 0.511 0.531 0.506 0.587 0.561 ...
## $ per : num 12.5 12.8 19.1 19.3 25 20.5 22.2 21 19.9 24.1 ...
## $ vorp : num 0 0 0.5 1.4 1.5 1.3 0.8 1.8 0.3 0.4 ...
## $ ws : num 0.1 0.2 0.4 2.1 3.8 2.6 1.5 2.9 0.6 0.5 ...
## $ bpm : num -1.7 -2.2 3.9 4.2 6.5 4.3 3.7 5.3 2.4 6.1 ...
#Kobe Full Playoffs
## 'data.frame': 3 obs. of 7 variables:
## $ player : chr "Michael Jordan" "Kobe Bryant" "LeBron James"
## $ titles_won : int 6 5 4
## $ finals_MVP : int 6 2 4
## $ MVP_award : int 5 1 4
## $ All_NBA_team : int 11 15 19
## $ All_NBA_def_team: int 9 12 6
## $ scoring_titles : int 10 2 1
#Legends Accomplishments
## 'data.frame': 3 obs. of 20 variables:
## $ season : chr "1984-85" "2003-04" "1996-97"
## $ games_played: int 82 79 71
## $ fg_percent : num 0.515 0.417 0.417
## $ fg_3percent : num 0.173 0.29 0.375
## $ fg_2percent : num 0.526 0.438 0.437
## $ e_fg_percent: num 0.518 0.438 0.477
## $ ft_percent : num 0.845 0.754 0.819
## $ apg : num 5.9 5.9 1.3
## $ rpg : num 6.5 5.5 1.9
## $ spg : num 2.4 1.6 0.7
## $ blk : num 0.8 NA NA
## $ ppg : num 28.2 20.9 7.6
## $ game_high : int 49 41 24
## $ ts_percent : num 0.592 0.488 0.544
## $ per : num 25.8 18.3 14.4
## $ vorp : num 7.4 2.9 0.5
## $ ws : num 14 5.1 1.8
## $ bpm : num 7.3 1.7 -0.1
## $ player : chr "Jordan" "LeBron" "Kobe"
## $ bpg : num NA 0.7 0.3
Lets take a minute to take a look at some key metrics and the what they mean. Here is a glossary of the key metrics that we will be using for this analysis.
Terms
| Term | Meaning |
|---|---|
| season | Season |
| games_played | Games Played during season/playoffs |
| fg_percent | Field Goal Percentage |
| fg_3percent | 3-Point Field Goal Percentage |
| fg_2percent | 2-Point Field Goal Percentage |
| e_fg_percent | Effective Field Goal Percentage |
| ft_percent | Free Throw Percentage |
| game_high | Game high during season/playoffs |
| apg | Assists Per Game |
| rpg | Rebounds Per Game. |
| bpg | Blocks Per Game |
| spg | Steals Per Game |
| ppg | Points per Game |
Here are some other advanced terms that we will be using for our analysis;
Advanced Terms
| Term | Meaning | Explanation |
|---|---|---|
| ts_percent | True Shooting Percentage | A measure of shooting efficiency that takes into account 2-point field goals, 3-point field goals, and free throws. |
| per | Player Efficiency Rating | A measure of per-minute production standardized such that the league average is 15 |
| vorp | Value over Replacement Player | A box score estimate of the points per 100 TEAM possessions that a player contributed above a replacement-level. |
| ws | Win Shares | An estimate of the number of wins contributed by a player. |
| bpm | Box Plus/Minus | A box score of the points per 100 possessions a player contributed over a average player, translated to an average team. |
The interactive chart allows us to compare the rookie-season shooting efficiency of Michael Jordan, Kobe Bryant, and LeBron James across four key metrics:
The data for each player is filtered to their rookie season: -
Michael Jordan → 1984–85
- Kobe Bryant → 1996–97
- LeBron James → 2003–04
library(dplyr)
library(tidyr)
library(ggplot2)
library(ggimage)
library(ggiraph)
# Prepare data with player images
rookie_stats_images <- rookie_stats %>%
mutate(player = factor(player, levels = c("Jordan", "Kobe", "LeBron"))) %>%
mutate(image = case_when(
player == "Jordan" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/jordami01.jpg",
player == "Kobe" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/bryanko01.jpg",
player == "LeBron" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/jamesle01.jpg"
))
# Create interactive plot
p <- rookie_stats_images %>%
select(player, image, fg_percent, fg_3percent, ft_percent, ts_percent) %>%
pivot_longer(
cols = c(fg_percent, fg_3percent, ft_percent, ts_percent),
names_to = "metric", values_to = "value"
) %>%
mutate(metric = case_when(
metric == "fg_percent" ~ "FG%",
metric == "fg_3percent" ~ "3P%",
metric == "ft_percent" ~ "FT%",
metric == "ts_percent" ~ "TS%"
)) %>%
ggplot(aes(x = metric, y = value, fill = player)) +
geom_col_interactive(
aes(tooltip = paste0(player, ": ", round(value * 100, 1), "%")),
position = position_dodge(width = 0.8),
width = 0.7
) +
geom_image(
aes(image = image, y = value + 0.06, group = player),
position = position_dodge(width = 0.8),
size = 0.06
) +
scale_y_continuous(expand = expansion(mult = c(0, 0.25))) +
labs(
title = "Rookie Shooting Efficiency",
x = "Metric",
y = "Percentage"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 0, hjust = 0.5),
legend.title = element_blank(),
plot.title = element_text(face = "bold", hjust = 0.5)
)
# Render interactive plot in HTML
girafe(
ggobj = p,
width_svg = 8,
height_svg = 6,
options = list(
opts_hover(css = "fill-opacity:0.7;cursor:pointer;"),
opts_tooltip(css = "background-color:white;color:black;padding:5px;border-radius:5px;")
)
)Michael Jordan stands out with the highest overall efficiency as a rookie. His FG% and TS% are the highest, showing he scored effectively both inside and outside the paint. His 3P% and FT% indicate solid performance from long-range and the free-throw line as well.
Kobe Bryant shows a moderate rookie efficiency. His FG% and TS% are slightly lower than Jordan’s, reflecting a typical adjustment period for a rookie. His 3P% is shorter, but his FT% demonstrates reliable scoring from the line.
LeBron James exhibits strong finishing ability, as reflected by his high FG% and TS%, particularly in close-range shots. His 3P% bar is the lowest among the three, indicating that perimeter shooting was not yet a major strength as a rookie. His FT% is consistent, suggesting steady scoring in controlled situations.
Here we take a look at each players debut season as a Rookie in the NBA. Here are some key per game statistics that we will be comparing;
The data for each player is filtered to their rookie season:
## Rookie Regular Season Per Game Stats
library(dplyr)
library(tidyr)
library(ggplot2)
library(ggimage)
library(ggiraph)
# Extract rookie regular season for each player
jordan_rookie_reg <- jordan_full_season %>%
filter(season == "1984-85") %>% # Jordan's rookie year
mutate(player = "Michael Jordan") %>%
select(player, ppg, rpg, apg, spg, bpg)
kobe_rookie_reg <- kobe_full_season %>%
filter(season == "1996-97") %>% # Kobe's rookie year
mutate(player = "Kobe Bryant") %>%
select(player, ppg, rpg, apg, spg, bpg)
lebron_rookie_reg <- lebron_full_season %>%
filter(season == "2003-04") %>% # LeBron's rookie year
mutate(player = "LeBron James") %>%
select(player, ppg, rpg, apg, spg, bpg)
# Combine
rookie_regular <- bind_rows(jordan_rookie_reg, kobe_rookie_reg, lebron_rookie_reg)
# Add images
rookie_regular <- rookie_regular %>%
mutate(image = case_when(
player == "Michael Jordan" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/jordami01.jpg",
player == "Kobe Bryant" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/bryanko01.jpg",
player == "LeBron James" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/jamesle01.jpg"
))
# Pivot
rookie_regular_long <- rookie_regular %>%
pivot_longer(cols = c(ppg, rpg, apg, spg, bpg),
names_to = "stat", values_to = "value")
# Plot
p_regular <- ggplot(rookie_regular_long, aes(x = stat, y = value, fill = player)) +
geom_col_interactive(
aes(tooltip = paste0(player, ": ", round(value, 1))),
position = position_dodge(width = 0.9), width = 0.9
) +
geom_image(
aes(image = image, y = value + 1.5, group = player),
position = position_dodge(width = 0.9), size = 0.05
) +
scale_y_continuous(expand = expansion(mult = c(0, 0.25))) +
labs(title = "Rookie Regular Season Per-Game Stats",
x = "Statistic", y = "Per-Game Value") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 0, hjust = 0.5),
legend.title = element_blank(),
plot.title = element_text(face = "bold", hjust = 0.5))
# Render interactive plot
girafe(ggobj = p_regular, width_svg = 8, height_svg = 6)Michael Jordan clearly dominated as a rookie. Over the course of the full 82-game season, he averaged nearly 30 points per game, an extraordinary mark for a first-year player. Despite being slightly smaller than LeBron (6’6” vs. 6’8”), Jordan also managed to secure more rebounds per game, showcasing his tenacity and athleticism.
LeBron James, while not matching Jordan’s scoring, still had an outstanding rookie campaign. Averaging 21 points, 6 assists, 5 rebounds, and 2 steals per game, he demonstrated remarkable balance across all facets of the game. His performance was an early indication of the all-around dominance that would define his career.
Kobe Bryant, on the other hand, had a much more modest rookie season. Averaging just 7 points per game, his limited production was largely a result of restricted playing time behind established All-Stars like Shaquille O’Neal and Eddie Jones. Still, flashes of his scoring ability hinted at the superstar he would eventually become.
Now, lets highlight some of their early strengths and playing styles during the playoffs. This visualization compares the rookie playoff performance of Michael Jordan, Kobe Bryant, and LeBron James across key per-game statistics:
The data for each player is filtered to their rookie playoff season:
- Michael Jordan → 1984–85
- Kobe Bryant → 1996–97
- LeBron James → 2005–06
library(dplyr)
library(tidyr)
library(ggplot2)
library(ggiraph)
library(ggimage)
# Extract rookie playoff stats
jordan_rookie <- jordan_full_playoffs %>%
filter(season == "1984-85") %>%
mutate(player = "Michael Jordan") %>%
select(player, ppg, rpg, apg, spg, bpg)
kobe_rookie <- kobe_full_playoffs %>%
filter(season == "1996-97") %>%
mutate(player = "Kobe Bryant") %>%
select(player, ppg, rpg, apg, spg, bpg)
lebron_rookie <- lebron_full_playoffs %>%
filter(season == "2005-06") %>%
mutate(player = "LeBron James") %>%
select(player, ppg, rpg, apg, spg, bpg)
# Combine data
rookie_playoffs <- bind_rows(jordan_rookie, kobe_rookie, lebron_rookie)
# Add player headshots
rookie_playoffs <- rookie_playoffs %>%
mutate(image = case_when(
player == "Michael Jordan" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/jordami01.jpg",
player == "Kobe Bryant" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/bryanko01.jpg",
player == "LeBron James" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/jamesle01.jpg"
))
# Pivot data longer
rookie_playoffs_per_game <- rookie_playoffs %>%
pivot_longer(
cols = c(ppg, rpg, apg, spg, bpg),
names_to = "stat", values_to = "value"
)
# Build interactive plot
p_playoffs_per_game <- ggplot(rookie_playoffs_per_game, aes(x = stat, y = value, fill = player)) +
geom_col_interactive(
aes(tooltip = paste0(player, ": ", round(value, 1))),
position = position_dodge(width = 0.9), width = 0.9
) +
geom_image(
aes(image = image, y = value + 1.5, group = player),
position = position_dodge(width = 0.8),
size = 0.05
) +
scale_y_continuous(expand = expansion(mult = c(0, 0.25))) +
labs(title = "Rookie Playoff Per-Game Stats",
x = "Statistic", y = "Per-Game Value") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 0, hjust = 0.5),
legend.title = element_blank(),
plot.title = element_text(face = "bold", hjust = 0.5))
# Render interactive plot
girafe(ggobj = p_playoffs_per_game, width_svg = 8, height_svg = 6)Michael Jordan exploded onto the playoff stage, averaging nearly 30 PPG, which was nothing short of the standard he would go on to set throughout his career. While his other numbers were solid, it was his scoring contribution that stood out the most.
Kobe Bryant had a more modest rookie playoff performance, averaging just 9 PPG while playing behind All-Stars like Eddie Jones and Shaquille O’Neal. His lower numbers were more a reflection of his limited role and playing time rather than his talent.
LeBron James, unlike Kobe, entered the playoffs as the clear centerpiece for the Cavaliers. With an explosive scoring performance comparable to Jordan’s, LeBron paired his offense with a more balanced stat line across rebounds and assists. He also played the most games of the three, making his rookie playoff run especially impressive.
From this comparison what I can say about this rookie analysis is;
Jordan → pure scorer dominance from the start LeBron → all-around force immediately Kobe → slower start due to playing behind Eddie Jones but later became elite
In this interactive density plot, we examine the distribution of key stats — Points, Rebounds, Assists, Blocks, and Steals — for each player. This visualization allows us to understand the overall trends in performance, highlighting how consistent a player was, where their peak contributions occurred, and how their output varied across seasons. By comparing players, we can identify differences in playing style, such as scoring focus versus all-around contributions. The interactive elements also let us explore exact values for specific seasons and stats.
Here are the key metrics being visualized:
library(ggplot2)
library(dplyr)
library(tidyr)
library(plotly)
# Reshape data to long format
stats_long <- season_ppg %>%
select(player, ppg, apg, rpg, bpg, spg) %>%
rename(Points = ppg,
Assists = apg,
Rebounds = rpg,
Blocks = bpg,
Steals = spg) %>%
pivot_longer(cols = Points:Steals, names_to = "Stat", values_to = "Value")
# Create ggplot with custom hover text
p <- ggplot(stats_long, aes(x = Value, color = player, fill = player,
text = paste("Player:", player,
"<br>Stat:", Stat,
"<br>Value:", round(Value, 2)))) +
geom_density(alpha = 0.3) +
facet_wrap(~ Stat, scales = "free") +
labs(title = "Distribution of Player Stats",
x = "Value",
y = "Density") +
theme_minimal()
# Convert to interactive plotly
ggplotly(p, tooltip = "text")The plot shows which player was most balanced vs. specialized, who was most consistent, and how each contributed across all key stats over their careers.
Jordan: Dominant scorer with strong Peaks in Points; moderate contributions in Rebounds and Assists; smaller impact in defensive stats.
LeBron: All-around contributor; consistently high across Points, Rebounds, and Assists.
Kobe: High scoring focus with more variation in other stats, reflecting volume scoring style.
In this interactive horizontal heatmap, we examine player stats across seasons — Points, Rebounds, Assists, Blocks, Steals, PER, and Win Shares for each player. This visualization allows us to quickly identify trends and patterns in performance over time. We can see which seasons were peak performance years, where players were consistent, and how their contributions varied across different metrics. By comparing players side by side, we can understand differences in playing style, strengths, and overall impact. The interactive elements let us hover over each tile to explore exact values for specific seasons and stats.
Here are the key metrics being visualized:
library(dplyr)
library(tidyr)
library(ggplot2)
library(plotly)
# Step 1: Clean and rename columns explicitly
heatmap_data <- season_ppg %>%
select(player, season, ppg, rpg, apg, bpg, spg, per, ws) %>%
rename(
Points = ppg,
Rebounds = rpg,
Assists = apg,
Blocks = bpg,
Steals = spg,
PER = per,
Win_Shares = ws
) %>%
arrange(player, season)
# Replace NA with 0
heatmap_data[is.na(heatmap_data)] <- 0
# Step 2: Pivot to long format
heatmap_long <- heatmap_data %>%
pivot_longer(
cols = Points:Win_Shares,
names_to = "Stat",
values_to = "Value"
) %>%
mutate(
season = factor(season, levels = sort(unique(season))),
Stat = factor(Stat, levels = c("Points","Rebounds","Assists","Blocks","Steals","PER","Win_Shares"))
)
# Step 3: Create horizontal heatmap
p <- ggplot(heatmap_long, aes(x = Stat, y = season, fill = Value,
text = paste(
"Player:", player,
"<br>Season:", season,
"<br>Stat:", Stat,
"<br>Value:", round(Value,2)
))) +
geom_tile(color = "white") +
facet_wrap(~ player, scales = "free_y") +
scale_fill_viridis_c(option = "plasma") +
labs(title = "Player Stats Heatmap by Season (Horizontal Layout)",
x = "Statistic",
y = "Season") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, hjust = 1),
strip.text = element_text(size = 12))
# Step 4: Make interactive
ggplotly(p, tooltip = "text")This interactive heatmap shows player stats across seasons:
Darker tiles indicate higher values in a particular stat, highlighting seasons where a player excelled (e.g., Jordan’s scoring peaks, LeBron’s all-around contributions).
Lighter, consistent shades indicate steady performance, while variability shows seasons with fluctuations in output.
What the Heatmap tells us: Jordan: Dominant in Points and scoring stats, with some defensive contributions. LeBron: Balanced across multiple metrics, consistently strong in Points, Rebounds, and Assists. Kobe: High scoring peaks with moderate contributions in other stats, reflecting a volume scoring style.
Lets dive into the defensive metric comparison of the three players.
library(dplyr)
library(tidyr)
library(ggplot2)
library(ggimage)
# Player colors
player_info <- data.frame(
Player = c("Jordan", "LeBron", "Kobe"),
Color = c("#1f77b4", "#ff7f0e", "#2ca02c")
)
# Metrics for X-axis
metrics <- c("Steals", "Blocks", "Rebounds") # replace with your actual metrics
# Image parameters
image_size <- 0.5
image_offset <- 0.1
ggplot(defense_long, aes(x = X_Pos, y = Value, fill = Player)) +
geom_col(width = 0.25, position = position_dodge(width = 0.4)) +
geom_image(
aes(image = Image, y = Value + 1.5), # move offset inside aes()
size = 0.1,
position = position_dodge(width = 0.4)
) +
scale_fill_manual(values = setNames(player_info$Color, player_info$Player)) +
scale_x_continuous(breaks = 1:3, labels = metrics) +
scale_y_continuous(limits = c(0, 14)) +
labs(
title = "Defensive Metrics Comparison: Jordan vs LeBron vs Kobe",
x = "Metric",
y = "Average per Game"
) +
theme_minimal(base_size = 14) +
theme(plot.title = element_text(face = "bold", hjust = 0.5))## Warning: `position_dodge()` requires non-overlapping x intervals.
From the defensive metrics comparison of Jordan, LeBron, and Kobe:
Michael Jordan clearly stands out as the most effective overall defensive player, leading in steals (SPG) and blocks (BPG). This confirms his reputation as a dominant perimeter defender and disruptor.
LeBron James, while slightly behind Jordan in steals and blocks, excels in rebounds (RPG), showcasing his strength in controlling the glass and impacting the game inside.
Kobe Bryant performs solidly across all defensive metrics, but slightly below Jordan and LeBron, reinforcing that while he was a strong defender, he was not as statistically dominant as Jordan.
Overall, Jordan’s defensive versatility and disruptive ability make him the superior defensive player, whereas LeBron’s rebounding prowess highlights his strength in interior defense and overall court coverage.
VORP stands for Value Over Replacement Player. It’s an advanced basketball statistic used to estimate how much value a player contributes to their team compared to a “replacement-level” player — basically, a bench or freely available player who could easily be substituted in.
It’s kind of like saying: > “How much better is LeBron (or any player) than an average backup if he were replaced?”
In this interactive line chart, we examine VORP (Value Over Replacement Player) across seasons for each player, separated by Regular Season and Playoffs. This visualization allows us to track how each player’s overall impact evolved over their career and compare performance between different contexts. By observing the trends, we can identify peak seasons, periods of consistency, and how each player performed under playoff pressure. The interactive elements let us hover over each point to explore exact VORP values for specific seasons and contexts.
Key metric being visualized: VORP (Value Over Replacement Player)
library(dplyr)
library(ggplot2)
library(plotly)
# Step 1: Add player and type labels, then combine datasets
jordan_season <- jordan_full_season %>%
mutate(player = "Jordan", type = "RegularSeason") %>%
select(player, season, type, vorp)
jordan_playoffs <- jordan_full_playoffs %>%
mutate(player = "Jordan", type = "Playoffs") %>%
select(player, season, type, vorp)
lebron_season <- lebron_full_season %>%
mutate(player = "LeBron", type = "RegularSeason") %>%
select(player, season, type, vorp)
lebron_playoffs <- lebron_full_playoffs %>%
mutate(player = "LeBron", type = "Playoffs") %>%
select(player, season, type, vorp)
kobe_season <- kobe_full_season %>%
mutate(player = "Kobe", type = "RegularSeason") %>%
select(player, season, type, vorp)
kobe_playoffs <- kobe_full_playoff %>%
mutate(player = "Kobe", type = "Playoffs") %>%
select(player, season, type, vorp)
# Combine all data
vorp_data <- bind_rows(jordan_season, jordan_playoffs,
lebron_season, lebron_playoffs,
kobe_season, kobe_playoffs) %>%
mutate(season = factor(season, levels = sort(unique(season))),
type = factor(type, levels = c("RegularSeason", "Playoffs")))
# Step 2: Create interactive line chart (showing every other year on x-axis)
p <- ggplot(vorp_data, aes(x = season, y = vorp, color = type, group = type,
text = paste(
"Player:", player,
"<br>Season:", season,
"<br>Type:", type,
"<br>VORP:", round(vorp,2)
))) +
geom_line(size = 1) +
geom_point(size = 2) +
facet_wrap(~ player, scales = "free_x") +
scale_x_discrete(breaks = levels(vorp_data$season)[c(TRUE, FALSE)]) + # skip every other year
labs(title = "VORP by Season: Regular Season vs Playoffs",
x = "Season",
y = "VORP") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, hjust = 1),
legend.position = "bottom")
# Step 3: Make interactive
ggplotly(p, tooltip = "text")| VORP Value | Meaning |
|---|---|
| 0.0 | Replacement-level player (average bench player) |
| 1.0–2.0 | Solid rotation player |
| 3.0–5.0 | All-Star level |
| 6.0+ | MVP-level season |
So, for example:
Let’s examine VORP from a different perspective. Using a stacked bar plot, we can more clearly compare the contributions of each season to a player’s total VORP. Unlike a line chart, which emphasizes trends over time, the stacked bars allow us to directly see the magnitude of each season’s impact and make side-by-side comparisons between players.
library(dplyr)
library(ggplot2)
library(plotly)
# Step 1: Prepare data
vorp_all <- vorp_data %>%
mutate(
label_text = paste0(
"Player: ", player,
"<br>Season: ", season,
"<br>Type: ", type,
"<br>VORP: ", round(vorp, 2)
),
season = factor(season, levels = sort(unique(season))) # ensures proper order
)
# Step 2: Horizontal bar chart
p <- ggplot(vorp_all, aes(x = vorp, y = season, fill = player, text = label_text)) +
geom_col(position = "dodge", color = "black") +
facet_wrap(~ type, scales = "free_y") +
labs(
title = "VORP by Season for Each Player",
x = "VORP",
y = "Season"
) +
theme_minimal() +
theme(
axis.text.y = element_text(size = 8),
legend.position = "bottom"
)
# Step 3: Make interactive
ggplotly(p, tooltip = "text")Examining VORP across the full careers of these players reveals distinct career trajectories:
In this interactive bar chart, we examine the Value Over Replacement Player (VORP) for the first four seasons of Michael Jordan, LeBron James, and Kobe Bryant. This visualization allows us to track how each player’s early-career impact evolved, highlighting which seasons contributed most relative to a replacement-level player. By observing the bars, we can compare early-career performance across players and identify patterns in their contributions. The interactive elements let us hover over each bar to explore exact VORP values for specific seasons, while the headshots on top of each bar provide a visual reference for the player.
library(dplyr)
library(readr)
library(ggplot2)
library(ggimage)
library(ggiraph)
# Load data (adjust file paths if needed)
jordan <- read_csv("jordan_full_season.csv") %>% mutate(player = "Michael Jordan")## Rows: 15 Columns: 18
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): season
## dbl (17): games_played, fg_percent, fg_3percent, fg_2percent, e_fg_percent, ...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 22 Columns: 18
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): season
## dbl (17): games_played, fg_percent, fg_3percent, fg_2percent, e_fg_perecent,...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 20 Columns: 18
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): season
## dbl (17): games_played, fg_percent, fg_3percent, fg_2percent, e_fg_percent, ...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Combine & keep first 4 years
vorp_data <- bind_rows(jordan, lebron, kobe) %>%
select(player, season, vorp) %>%
group_by(player) %>%
slice_head(n = 4) %>%
ungroup()
# Add headshot images
vorp_data <- vorp_data %>%
mutate(image = case_when(
player == "Michael Jordan" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/jordami01.jpg",
player == "LeBron James" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/jamesle01.jpg",
player == "Kobe Bryant" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/bryanko01.jpg"
))
# Build interactive chart
p <- ggplot(vorp_data, aes(x = season, y = vorp, fill = player)) +
geom_col_interactive(aes(
tooltip = paste0(player, " (", season, "): VORP ", vorp)
), width = 0.7) +
geom_image(aes(image = image, y = vorp + 1), size = 0.08) + # 👈 on top of bars
facet_wrap(~player, nrow = 1, scales = "free_x") +
theme_minimal(base_size = 15) +
labs(
title = "First 4 Seasons: VORP Comparison",
y = "VORP",
x = "Season"
) +
theme(
legend.position = "none",
axis.text.x = element_text(angle = 45, hjust = 1)
)
girafe(ggobj = p)Why the First Four Years of VORP
We focus on the first four seasons to examine each player’s early-career impact. Michael Jordan was immediately dominant, LeBron James shows steady growth, reflecting his development as a versatile scorer and playmaker, and Kobe Bryant started lower but quickly increased his contributions. This period highlights how each player adapted to the NBA and established themselves as key contributors.
Early-Career VORP Analysis
In this interactive line chart, we examine the Points-to-Assists (P/A) ratio across seasons for each player. This visualization allows us to track how scoring and playmaking balance evolved throughout their careers. By observing the trends, we can identify scoring-focused seasons, periods of more balanced play, and differences in playing style between players. The interactive elements let us hover over each point to explore exact points, assists, and P/A ratio for specific seasons.
Here is the key metric being visualized: Points-to-Assists Ratio (PPG / APG)
library(dplyr)
library(ggplot2)
library(plotly)
# Step 1: Calculate Points-to-Assists ratio
pa_data <- season_ppg %>%
mutate(
PA_ratio = ppg / apg,
label_text = paste0(
"Player: ", player,
"<br>Season: ", season,
"<br>Points: ", ppg,
"<br>Assists: ", apg,
"<br>P/A Ratio: ", round(PA_ratio,2)
)
)
# Step 2: Line chart
p <- ggplot(pa_data, aes(x = season, y = PA_ratio, color = player, group = player, text = label_text)) +
geom_line(size = 1) +
geom_point(size = 2) +
labs(
title = "Points-to-Assists Ratio by Season",
x = "Season",
y = "Points per Assist"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
# Step 3: Make interactive
ggplotly(p, tooltip = "text")Player Comparisons
*Michael Jordan Jordan’s P/A is generally high**, reflecting his role as a primary scorer. While he was capable of facilitating, his unmatched value came from his scoring ability.
*LeBron James LeBron shows the most balanced P/A ratio of the three, consistently scoring at a high level while also being a strong playmaker. His ratio demonstrates his dual-threat ability** to create offense both for himself and for others.
Kobe Bryant Kobe’s P/A ratio trends closer to Jordan’s — scoring-focused. His passing improved later in his career, but overall he leaned toward being a primary scorer first.
Spotlight: Michael Jordan, 1994–95 Season
In the 1994–95 season, after returning mid-season from his first retirement, Jordan’s Points-to-Assists ratio spiked.
This means for every assist Jordan recorded, he scored about 7.5 points.
This unusually high ratio highlights that upon his return, Jordan was focused heavily on scoring while still contributing some playmaking.
library(ggplot2)
library(ggimage)
library(plotly)
library(ggiraph)
legends_accomplishments %>%
distinct(player)## player
## 1 Michael Jordan
## 2 Kobe Bryant
## 3 LeBron James
legend_accomplishments_images <- legends_accomplishments %>%
filter(player %in% c("Michael Jordan", "LeBron James", "Kobe Bryant")) %>%
mutate(image = case_when(
player == "Michael Jordan" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/jordami01.jpg",
player == "LeBron James" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/jamesle01.jpg",
player == "Kobe Bryant" ~ "https://www.basketball-reference.com/req/202106291/images/headshots/bryanko01.jpg"
))
p <- ggplot(legend_accomplishments_images,
aes(x = player, y = titles_won, fill = player)) +
# interactive bars
geom_col_interactive(aes(tooltip = paste0(player, ": ", titles_won, " titles")),
width = 0.7) +
# static images (no interactivity here)
geom_image(aes(image = image, y = titles_won + 1.5), size = 0.16) +
labs(x = "Players", y = "Titles", title = "NBA Titles Won by Legends") +
expand_limits(y = max(legend_accomplishments_images$titles_won) + 4) +
theme_minimal()
girafe(ggobj = p)Through our analysis of Michael Jordan, Kobe Bryant, and LeBron James, we’ve gained a deeper appreciation of what makes each player legendary. By examining advanced metrics like VORP, offensive balance through Points-to-Assists ratios, and defensive impact via Steals and Blocks, we’ve uncovered the different paths each took to greatness.
Michael Jordan redefined dominance with his explosive scoring, relentless competitiveness, and ability to immediately elevate his team. His legacy rests on peak performance and an unmatched will to win.
Kobe Bryant embodied growth, dedication, and mastery. From a gradual rise to superstardom, his career tells the story of perseverance, relentless work ethic, and the mentality of a pure scorer.
LeBron James stands as the model of consistency and versatility, maintaining elite production across two decades. His rare ability to both score and facilitate makes him a complete all-around player.
What these visualizations ultimately show is that greatness in basketball cannot be defined by a single metric. Each of these legends brought a unique style, strength, and impact to the game: Jordan as the ultimate competitor, Kobe as the relentless scorer, and LeBron as the all-around force.
Together, they represent three different versions of basketball excellence, and their legacies will continue to shape the game for generations to come.
I appreciate your time and consideration, and I welcome any feedback or recommendations for improvement. Im looking to make more imrovements as i go along my journey. Please, any feedback is apprecaited, Thank You!
Wickham, H. (2017). tidyverse: Easily install and load the ‘tidyverse’ (R package version 1.2.1). Retrieved from https://CRAN.R-project.org/package=tidyverse
Kassambara, A. (2018). ggpubr: ‘ggplot2’ based publication ready plots (R package version 0.2). Retrieved from https://CRAN.R-project.org/package=ggpubr
Ooms, J. (2018). magick: Advanced graphics and image-processing in R (R package version 1.9). Retrieved from https://CRAN.R-project.org/package=magick
R Core Team. (2018). R: A language and environment for statistical computing. Vienna, Austria: R Foundation for Statistical Computing. Retrieved from https://www.R-project.org/
Xie, Y. (2018). knitr: A general-purpose package for dynamic report generation in R (R package version 1.20).
Xie, Y. (2015). Dynamic documents with R and knitr (2nd ed.). Chapman and Hall/CRC. ISBN 978-1498716963
Xie, Y. (2014). knitr: A comprehensive tool for reproducible research. In V. Stodden, F. Leisch, & R. D. Peng (Eds.), Implementing reproducible computational research. Chapman and Hall/CRC. ISBN 978-1466561595