Ryan Skikun NBA Scoring Trends Since 2010

Introduction to the Data

Hello everyone! I’m Ryan Skikun, a Senior BAIS & Marketing major from Metro Detroit, MI. With the NBA playoffs in full swing, I wanted to take a deep dive into the numbers that define the modern game.

Using a comprehensive 16-year dataset from Sports-Reference, I analyzed team-level statistics to identify the persistent scoring trends reshaping the league. My goal was to answer the ultimate question: In an era of high-octane offense, what actually separates playoff contenders from those heading home early?

Research Prompt:

Have scoring trends changed since 2010, and if so what are the metrics that induce success?

Data Setup

To access the necessary information, I used sportsreference.com and ethically web scraped their data tables, while adhering to their terms and services that come from accessing their information. The means of this information is strictly educational and seeks no financial benefit.

library(tidyverse)
library(rvest)
library(xml2)
library(stringr)
library(readr)
nba_df <- read_csv("~/Desktop/OneDrive - Xavier University/BAIS 462 Spring 2026/nba_df.csv")
nba_df <- 
  nba_df %>%
  mutate(playoffs = if_else(str_detect(Team, "\\*"), 1, 0))

write_csv(nba_df, "nba_df.csv")
zip_path <- "archive (6).zip"

unzip(zip_path)
playoffs_games <- read_csv("nba_playoffs_games.csv")

Three Point % of Playoff vs Non Playoff teams

nba_df %>%
  mutate(`3P%` = as.numeric(str_remove(`3P%`, "%"))) %>%
  group_by(season, playoffs) %>%
  summarise(
    avg_3p = mean(`3P%`, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = season, y = avg_3p, color = factor(playoffs))) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 1) +
  labs(
    title = "3P% Over Time: Playoff vs Non-Playoff Teams (2010–2016)",
    x = "Season",
    y = "Average 3P%",
    color = "Playoffs (1 = Yes, 0 = No)"
  ) +
  theme_minimal()

Historically, three-point proficiency has been a primary indicator of playoff eligibility. However, since 2024, this performance gap has tightened significantly. This shift reflects an evolution in tactical playstyles; as perimeter shooting stabilizes across the league, the competitive edge is shifting toward teams that can successfully disrupt defensive schemes, induce foul trouble, and capitalize on high-percentage opportunities at the charity stripe.

League Average Scoring & Shooting Summary

nba_df %>%
  filter(season >= 2010, season <= 2026) %>%
  filter(str_detect(Team, regex("League", ignore_case = TRUE))) %>%
  mutate(`3P%` = as.numeric(str_remove(`3P%`, "%"))) %>%
  select(season, `3P%`, `3PA`, PTS) %>%
  pivot_longer(cols = -season, names_to = "metric", values_to = "value") %>%
  ggplot(aes(x = season, y = value)) +
  geom_line(linewidth = 1.2, color = "steelblue") +
  geom_point(size = 2, color = "steelblue") +
  facet_wrap(~metric, scales = "free_y", ncol = 1) +
  labs(
    title = "NBA League Averages (2010–2026)",
    x = "Season",
    y = "Value"
  ) +
  theme_minimal()

The visualization highlights a significant evolution in NBA scoring architecture. While shooting percentages remain subject to seasonal variance, the volume of three-point attempts and overall PPG show a synchronized upward trend. This reflects a modern tactical shift where the increased frequency of high-value perimeter shots offsets traditional scoring methods, effectively raising the league’s offensive ceiling.

League Average Points Per game

nba_df %>%
  filter(str_detect(Team, regex("League", ignore_case = TRUE))) %>%
  ggplot(aes(x = season, y = PTS)) +
  geom_line(linewidth = 1.2, color = "black") +
  geom_point(size = 2, color = "red") +
  labs(
    title = "NBA League Average Points Per Game Over Time",
    x = "Season",
    y = "Points Per Game (PPG)"
  ) +
  theme_minimal()

This scaled-out view allows us to identify visual discrepancies that a tighter margin might hide. The scoring declines in 2012–2013 and 2022–2023 act as historical markers for the birth of new offensive eras. During these transitions, defensive adjustments often temporarily outpace offensive innovation. However, as the ‘growing pains’ subside and teams refine their modern scoring strategies, we see a consistent return to the upward trajectory of the league’s offensive ceiling.

nba_df %>%
  group_by(season, playoffs) %>%
  summarise(
    avg_3PA = mean(`3PA`, na.rm = TRUE),
    .groups = "drop") %>%
  ggplot(aes(x = factor(season), y = avg_3PA)) +
  geom_col(fill = "orange") +
  facet_wrap(~playoffs, labeller = labeller(
    playoffs = c("0" = "Non-Playoff Teams", "1" = "Playoff Teams")
  )) +
  labs(
    title = "3PT Attempts Over Time: Playoff vs Non-Playoff Teams",
    x = "Season",
    y = "Average 3PA"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Interestingly, the data shows virtually no gap in three-point volume between the teams that make the playoffs and those that head to early vacation. This parity in attempts leads to two potential conclusions regarding the ‘Modern NBA’ blueprint: either efficiency is the true arbiter of a franchise’s fate, or the missing piece of the puzzle lies in external variables like free throw frequency. This suggests that simply ‘playing the modern way’ isn’t enough; teams must either execute at an elite level or find ways to create scoring leverage beyond the arc.

Free throw % by Playoff vs Non playoff teams

nba_df %>%
  group_by(season, playoffs) %>%
  summarise(
    avg_FTP = mean(`FT%`, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = factor(season), y = avg_FTP)) +
  geom_col(fill = "darkgreen") +
  coord_cartesian(ylim = c(0.7, 0.8)) +
  facet_wrap(~playoffs, labeller = labeller(
    playoffs = c("0" = "Non-Playoff Teams", "1" = "Playoff Teams")
  )) +
  labs(
    title = "Free Throw Percentage Over Time: Playoff vs Non-Playoff Teams",
    x = "Season",
    y = "Free Throw %"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

As we break down the data between the teams that extend their season and those that head home, a clear narrative emerges. Both groups follow the same upward trajectory in playstyle, but scoring efficiency remains the ultimate arbiter of success. In today’s NBA, where the difference between a playoff seed and a lottery pick can come down to a handful of possessions, efficiency isn’t just a stat—it’s the deciding factor between postseason life and death.

Post Season Scoring by Location

playoffs_games <- read_csv("nba_playoffs_games.csv")
Rows: 66 Columns: 9
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (2): home_team, away_team
dbl (7): series_id, home_points, away_points, round, game, score_home, score...

ℹ 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.
playoffs_games %>%
  summarise(
    `Home Points` = mean(home_points, na.rm = TRUE),
    `Away Points` = mean(away_points, na.rm = TRUE)) %>%
  pivot_longer(cols = everything(), names_to = "Location", values_to = "PTS") %>%
  # 3. Create the Bar Chart with the specific 105-110 window
  ggplot(aes(x = Location, y = PTS, fill = Location)) +
  geom_col(width = 0.5, color = "black") +
  # Tighten the margins to your requested range
  coord_cartesian(ylim = c(105, 110)) + 
  scale_fill_manual(values = c(`Home Points` = "#1a3a5c", `Away Points` = "#c0392b")) +
  labs(
    title = "NBA Playoff Average Points: Home vs. Away",
    subtitle = "Zoomed perspective: 105 to 110 PPG",
    x = "Game Location",
    y = "Points Per Game (PPG)"
  ) +
  theme_minimal()

In the high-stakes environment of the NBA playoffs, games naturally become tighter, and the data from 2020–2021 illustrates this perfectly. Compared to the regular-season league averages analyzed earlier, playoff scoring dips by roughly 3 points per game for both Home and Away teams. This scoring ‘cooldown’ is likely a multifactorial result of elite-level scouting, higher defensive engagement, and the tactical slowing of pace that often defines championship-level basketball.

Conclusion

The evidence gathered over the past sixteen seasons confirms that the NBA has moved into an era of unprecedented offensive optimization. While the data highlights significant increases in three-point conversion rates, these trends must be contextualized within broader qualitative changes, such as shifting defensive philosophies and officiating standards that favor offensive flow. The phasing out of traditional post-centric roles in favor of hybrid, multi-dimensional players reflects a league-wide directive toward versatility. Consequently, the ‘Modern 5’ has become the definitive symbol of this era, bridging the gap between historical physical dominance and contemporary technical skill.