Court Region Analysis
Court Region Analysis in Basketball
Court region analysis has revolutionized basketball strategy by quantifying the value of shots taken from different areas of the floor. Understanding spatial dynamics, shot efficiency by zone, and the evolution of regional shot selection provides critical insights for both offensive schemes and defensive strategies. Modern analytics have fundamentally reshaped how teams approach shot selection, leading to the "three-point revolution" and the dramatic decline of mid-range attempts.
NBA Court Zones and Classifications
Standard Court Regions
The NBA court is typically divided into several key zones for analytical purposes:
- Restricted Area (0-4 feet): The area directly under the basket within the 4-foot arc, representing the highest-value shots in basketball
- Paint (Non-RA, 4-8 feet): The painted area excluding the restricted zone, including short-range hooks and floaters
- Short Mid-Range (8-16 feet): The zone between the paint and the free-throw line extended
- Long Mid-Range (16 feet to 3PT line): The area from the free-throw line extended to the three-point arc
- Corner Three (0-2 feet from sideline): Three-point shots from the corners, the shortest three-point distance at 22 feet
- Above-the-Break Three (Center): Three-point shots from the top of the arc, 23.75 feet from the basket
- Wing Three: Three-point shots between corner and top-of-key regions
- Backcourt: Shots beyond half-court (rare, typically end-of-quarter attempts)
Shot Value by Region (2023-24 NBA Season)
| Region | FG% | Points Per Attempt | Frequency | Value Rank |
|---|---|---|---|---|
| Restricted Area | 64.5% | 1.29 | 30.2% | 1 |
| Corner Three | 39.1% | 1.17 | 8.7% | 2 |
| Above-Break Three | 36.2% | 1.09 | 30.5% | 3 |
| Paint (Non-RA) | 45.8% | 0.92 | 11.3% | 4 |
| Short Mid-Range | 42.1% | 0.84 | 9.2% | 5 |
| Long Mid-Range | 39.8% | 0.80 | 10.1% | 6 |
Key Insight: The dramatic value gap between three-point shots (1.09-1.17 PPA) and mid-range attempts (0.80-0.84 PPA) has driven the modern NBA's emphasis on rim attacks and three-point shooting.
Python Analysis: Zone Efficiency Using nba_api
Analyzing Shot Distribution and Efficiency by Court Region
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from nba_api.stats.endpoints import (
LeaguePlayerOnDetails,
ShotChartDetail,
LeagueDashTeamStats
)
from nba_api.stats.static import teams
from matplotlib.patches import Circle, Rectangle, Arc, Polygon
import warnings
warnings.filterwarnings('ignore')
def get_team_shot_chart(team_id, season='2023-24'):
"""
Retrieve shot chart data for a team by zone
"""
shot_chart = ShotChartDetail(
team_id=team_id,
player_id=0, # 0 for entire team
season_nullable=season,
season_type_all_star='Regular Season',
context_measure_simple='FGA'
)
shots_df = shot_chart.get_data_frames()[0]
return shots_df
def classify_shot_zone(row):
"""
Classify shots into analytical zones based on coordinates
"""
x, y = row['LOC_X'], row['LOC_Y']
distance = np.sqrt(x**2 + y**2) / 10 # Convert to feet
# Restricted Area
if distance <= 4:
return 'Restricted Area'
# Paint (Non-RA)
elif distance <= 8:
return 'Paint (Non-RA)'
# Three-point shots
elif row['SHOT_TYPE'] == '3PT Field Goal':
# Corner three (within 2 feet of sideline)
if abs(x) > 220: # 22 feet from center
return 'Corner Three'
else:
return 'Above-Break Three'
# Mid-range
else:
if distance < 16:
return 'Short Mid-Range'
else:
return 'Long Mid-Range'
def analyze_zone_efficiency(shots_df):
"""
Calculate efficiency metrics by zone
"""
# Classify each shot
shots_df['ZONE'] = shots_df.apply(classify_shot_zone, axis=1)
# Calculate zone statistics
zone_stats = shots_df.groupby('ZONE').agg({
'SHOT_MADE_FLAG': ['sum', 'count', 'mean'],
'SHOT_VALUE': 'first'
}).reset_index()
zone_stats.columns = ['Zone', 'Made', 'Attempts', 'FG_PCT', 'Shot_Value']
zone_stats['Points_Per_Attempt'] = (
zone_stats['Made'] * zone_stats['Shot_Value']
) / zone_stats['Attempts']
zone_stats['Frequency'] = (
zone_stats['Attempts'] / zone_stats['Attempts'].sum() * 100
)
return zone_stats.sort_values('Points_Per_Attempt', ascending=False)
def compare_team_zone_profiles(team_ids, season='2023-24'):
"""
Compare shot distribution across teams
"""
team_profiles = []
for team_id in team_ids:
shots = get_team_shot_chart(team_id, season)
shots['ZONE'] = shots.apply(classify_shot_zone, axis=1)
zone_dist = shots.groupby('ZONE').size()
zone_pct = (zone_dist / zone_dist.sum() * 100).to_dict()
zone_pct['team_id'] = team_id
team_profiles.append(zone_pct)
return pd.DataFrame(team_profiles)
def plot_zone_heatmap(shots_df):
"""
Create a court heatmap showing shot frequency by region
"""
fig, ax = plt.subplots(figsize=(12, 11))
# Draw court
draw_court(ax)
# Create hexbin heatmap
hexbin = ax.hexbin(
shots_df['LOC_X'],
shots_df['LOC_Y'],
gridsize=25,
cmap='YlOrRd',
alpha=0.8,
edgecolors='none'
)
plt.colorbar(hexbin, ax=ax, label='Shot Frequency')
ax.set_xlim(-250, 250)
ax.set_ylim(-50, 400)
ax.set_aspect('equal')
ax.set_title('Shot Distribution by Court Region', fontsize=16, fontweight='bold')
plt.tight_layout()
return fig
def draw_court(ax, color='black'):
"""
Draw NBA court lines
"""
# Hoop
hoop = Circle((0, 0), radius=7.5, linewidth=2, color=color, fill=False)
# Backboard
backboard = Rectangle((-30, -7.5), 60, -1, linewidth=2, color=color)
# Paint
outer_box = Rectangle((-80, -47.5), 160, 190, linewidth=2, color=color, fill=False)
inner_box = Rectangle((-60, -47.5), 120, 190, linewidth=2, color=color, fill=False)
# Free throw top arc
top_free_throw = Arc((0, 142.5), 120, 120, theta1=0, theta2=180,
linewidth=2, color=color, fill=False)
bottom_free_throw = Arc((0, 142.5), 120, 120, theta1=180, theta2=0,
linewidth=2, color=color, linestyle='dashed')
# Restricted area
restricted = Arc((0, 0), 80, 80, theta1=0, theta2=180,
linewidth=2, color=color)
# Three point line
corner_three_a = Rectangle((-220, -47.5), 0, 140, linewidth=2, color=color)
corner_three_b = Rectangle((220, -47.5), 0, 140, linewidth=2, color=color)
three_arc = Arc((0, 0), 475, 475, theta1=22, theta2=158,
linewidth=2, color=color)
# Center court
center_outer_arc = Arc((0, 422.5), 120, 120, theta1=180, theta2=0,
linewidth=2, color=color)
# Add elements to plot
court_elements = [
hoop, backboard, outer_box, inner_box,
top_free_throw, bottom_free_throw, restricted,
corner_three_a, corner_three_b, three_arc,
center_outer_arc
]
for element in court_elements:
ax.add_patch(element)
return ax
def analyze_zone_evolution(team_id, seasons):
"""
Track how a team's shot selection has evolved across seasons
"""
evolution_data = []
for season in seasons:
try:
shots = get_team_shot_chart(team_id, season)
shots['ZONE'] = shots.apply(classify_shot_zone, axis=1)
zone_dist = shots.groupby('ZONE').size()
zone_pct = (zone_dist / zone_dist.sum() * 100).to_dict()
zone_pct['Season'] = season
evolution_data.append(zone_pct)
except:
print(f"Data not available for {season}")
evolution_df = pd.DataFrame(evolution_data)
return evolution_df
def plot_zone_evolution(evolution_df):
"""
Visualize the evolution of shot selection by zone
"""
fig, ax = plt.subplots(figsize=(14, 8))
zones_to_plot = [
'Restricted Area', 'Above-Break Three',
'Corner Three', 'Long Mid-Range', 'Short Mid-Range'
]
for zone in zones_to_plot:
if zone in evolution_df.columns:
ax.plot(
evolution_df['Season'],
evolution_df[zone],
marker='o',
linewidth=2.5,
label=zone
)
ax.set_xlabel('Season', fontsize=12, fontweight='bold')
ax.set_ylabel('Frequency (%)', fontsize=12, fontweight='bold')
ax.set_title('Evolution of Shot Selection by Court Region',
fontsize=16, fontweight='bold')
ax.legend(loc='best', frameon=True, shadow=True)
ax.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
return fig
# Example usage
if __name__ == "__main__":
# Get Boston Celtics data (team_id = 1610612738)
celtics_shots = get_team_shot_chart(1610612738, '2023-24')
# Analyze zone efficiency
print("Zone Efficiency Analysis:")
zone_stats = analyze_zone_efficiency(celtics_shots)
print(zone_stats)
print("\n")
# Track evolution over multiple seasons
seasons = ['2018-19', '2019-20', '2020-21', '2021-22', '2022-23', '2023-24']
evolution = analyze_zone_evolution(1610612738, seasons)
print("Shot Selection Evolution:")
print(evolution)
# Create visualizations
heatmap_fig = plot_zone_heatmap(celtics_shots)
heatmap_fig.savefig('celtics_shot_heatmap.png', dpi=300, bbox_inches='tight')
evolution_fig = plot_zone_evolution(evolution)
evolution_fig.savefig('celtics_zone_evolution.png', dpi=300, bbox_inches='tight')
plt.show()
Analysis Capabilities:
- Shot Classification: Automatically categorizes shots into analytical zones based on coordinates
- Zone Efficiency: Calculates FG%, points per attempt, and frequency for each region
- Visual Heatmaps: Creates court visualizations showing shot density by location
- Temporal Analysis: Tracks evolution of shot selection across multiple seasons
- Comparative Analysis: Compares zone profiles across different teams
R Analysis: Regional Shooting Data with hoopR
Shot Quality and Expected Value by Region
library(hoopR)
library(tidyverse)
library(ggplot2)
library(viridis)
library(scales)
library(patchwork)
# Load NBA play-by-play data
load_nba_pbp <- function(seasons) {
pbp_data <- map_dfr(seasons, ~{
hoopR::load_nba_pbp(season = .x)
})
return(pbp_data)
}
# Classify shot zones from play-by-play data
classify_shot_region <- function(shot_data) {
shot_data %>%
mutate(
region = case_when(
str_detect(text, "Driving Layup|Dunk|Layup") ~ "Restricted Area",
str_detect(text, "Hook Shot|Floating Jump Shot") &
coordinate_x <= 8 ~ "Paint (Non-RA)",
shot_type == "3PT" & abs(coordinate_x) > 22 ~ "Corner Three",
shot_type == "3PT" ~ "Above-Break Three",
!shot_type == "3PT" & coordinate_y < 16 ~ "Short Mid-Range",
!shot_type == "3PT" & coordinate_y >= 16 ~ "Long Mid-Range",
TRUE ~ "Other"
),
shot_value = if_else(shot_type == "3PT", 3, 2),
points_scored = if_else(scoring_play == TRUE, shot_value, 0)
)
}
# Analyze zone efficiency with confidence intervals
analyze_regional_efficiency <- function(pbp_data) {
shot_data <- pbp_data %>%
filter(type_text == "Field Goal Made" | type_text == "Field Goal Missed") %>%
filter(!is.na(coordinate_x) & !is.na(coordinate_y))
shot_data <- classify_shot_region(shot_data)
regional_stats <- shot_data %>%
group_by(region) %>%
summarise(
attempts = n(),
makes = sum(scoring_play),
fg_pct = mean(scoring_play),
points_per_attempt = mean(points_scored),
se = sqrt(fg_pct * (1 - fg_pct) / attempts),
ci_lower = fg_pct - 1.96 * se,
ci_upper = fg_pct + 1.96 * se,
frequency = attempts / sum(shot_data$region != "Other") * 100
) %>%
arrange(desc(points_per_attempt))
return(regional_stats)
}
# Calculate expected points added by region
calculate_epa_by_region <- function(shot_data) {
league_avg_ppa <- mean(shot_data$points_scored)
epa_stats <- shot_data %>%
group_by(region, athlete_id, athlete_display_name) %>%
summarise(
attempts = n(),
actual_ppa = mean(points_scored),
.groups = 'drop'
) %>%
filter(attempts >= 50) %>% # Minimum threshold
mutate(
epa = actual_ppa - league_avg_ppa,
total_epa = epa * attempts
) %>%
arrange(desc(total_epa))
return(epa_stats)
}
# Analyze team zone profiles
team_zone_profile <- function(pbp_data, team_id) {
shot_data <- pbp_data %>%
filter(type_text == "Field Goal Made" | type_text == "Field Goal Missed") %>%
filter(team_id == !!team_id) %>%
filter(!is.na(coordinate_x) & !is.na(coordinate_y))
shot_data <- classify_shot_region(shot_data)
team_profile <- shot_data %>%
group_by(region) %>%
summarise(
attempts = n(),
fg_pct = mean(scoring_play),
ppa = mean(points_scored),
frequency = attempts / sum(shot_data$region != "Other") * 100
)
return(team_profile)
}
# Visualize zone efficiency with error bars
plot_zone_efficiency <- function(regional_stats) {
p <- ggplot(regional_stats, aes(x = reorder(region, points_per_attempt),
y = points_per_attempt)) +
geom_bar(stat = "identity", aes(fill = points_per_attempt),
alpha = 0.8, width = 0.7) +
geom_errorbar(aes(ymin = ci_lower * if_else(region == "Corner Three" |
region == "Above-Break Three", 3, 2),
ymax = ci_upper * if_else(region == "Corner Three" |
region == "Above-Break Three", 3, 2)),
width = 0.2, alpha = 0.6) +
scale_fill_viridis(option = "plasma", direction = -1) +
coord_flip() +
labs(
title = "Points Per Attempt by Court Region",
subtitle = "NBA 2023-24 Season | Error bars show 95% confidence intervals",
x = "Court Region",
y = "Points Per Attempt",
fill = "PPA"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", size = 16),
plot.subtitle = element_text(color = "gray40"),
legend.position = "right",
panel.grid.major.y = element_blank()
)
return(p)
}
# Create shot frequency distribution
plot_zone_frequency <- function(regional_stats) {
p <- ggplot(regional_stats, aes(x = reorder(region, frequency),
y = frequency)) +
geom_bar(stat = "identity", aes(fill = frequency),
alpha = 0.8, width = 0.7) +
geom_text(aes(label = sprintf("%.1f%%", frequency)),
hjust = -0.2, size = 4, fontface = "bold") +
scale_fill_viridis(option = "mako", direction = -1) +
coord_flip() +
ylim(0, max(regional_stats$frequency) * 1.15) +
labs(
title = "Shot Distribution by Court Region",
subtitle = "Frequency of attempts from each zone",
x = "Court Region",
y = "Frequency (%)",
fill = "Frequency"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", size = 16),
plot.subtitle = element_text(color = "gray40"),
legend.position = "none",
panel.grid.major.y = element_blank()
)
return(p)
}
# Compare teams by zone preference
compare_team_zones <- function(pbp_data, team_ids, team_names) {
team_comparisons <- map2_dfr(team_ids, team_names, ~{
profile <- team_zone_profile(pbp_data, .x)
profile$team <- .y
return(profile)
})
p <- ggplot(team_comparisons,
aes(x = region, y = frequency, fill = team)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.8) +
scale_fill_brewer(palette = "Set2") +
coord_flip() +
labs(
title = "Team Shot Distribution Comparison",
subtitle = "Zone preferences across different teams",
x = "Court Region",
y = "Frequency (%)",
fill = "Team"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", size = 16),
plot.subtitle = element_text(color = "gray40"),
legend.position = "bottom"
)
return(p)
}
# Analyze zone efficiency evolution over time
analyze_temporal_zones <- function(seasons) {
temporal_data <- map_dfr(seasons, ~{
pbp <- load_nba_pbp(season = .x)
shot_data <- pbp %>%
filter(type_text == "Field Goal Made" | type_text == "Field Goal Missed") %>%
filter(!is.na(coordinate_x) & !is.na(coordinate_y))
shot_data <- classify_shot_region(shot_data)
season_stats <- shot_data %>%
group_by(region) %>%
summarise(
frequency = n() / sum(shot_data$region != "Other") * 100,
ppa = mean(points_scored),
.groups = 'drop'
) %>%
mutate(season = .x)
return(season_stats)
})
return(temporal_data)
}
# Visualize temporal evolution
plot_zone_evolution <- function(temporal_data) {
key_zones <- c("Restricted Area", "Above-Break Three",
"Corner Three", "Long Mid-Range", "Short Mid-Range")
plot_data <- temporal_data %>%
filter(region %in% key_zones)
p <- ggplot(plot_data, aes(x = season, y = frequency,
color = region, group = region)) +
geom_line(size = 1.5, alpha = 0.8) +
geom_point(size = 3, alpha = 0.9) +
scale_color_viridis(discrete = TRUE, option = "turbo") +
labs(
title = "Evolution of Shot Selection by Court Region",
subtitle = "Tracking the three-point revolution and mid-range decline",
x = "Season",
y = "Frequency (%)",
color = "Region"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", size = 16),
plot.subtitle = element_text(color = "gray40"),
legend.position = "right",
axis.text.x = element_text(angle = 45, hjust = 1)
)
return(p)
}
# Create efficiency vs frequency scatter plot
plot_efficiency_frequency <- function(regional_stats) {
p <- ggplot(regional_stats, aes(x = frequency, y = points_per_attempt)) +
geom_point(aes(size = attempts, color = region), alpha = 0.7) +
geom_text(aes(label = region), vjust = -1, size = 3.5, fontface = "bold") +
scale_color_viridis(discrete = TRUE, option = "plasma") +
scale_size_continuous(range = c(5, 20), labels = comma) +
labs(
title = "Shot Efficiency vs. Frequency by Region",
subtitle = "Bubble size represents total attempts",
x = "Frequency (%)",
y = "Points Per Attempt",
color = "Region",
size = "Attempts"
) +
theme_minimal(base_size = 14) +
theme(
plot.title = element_text(face = "bold", size = 16),
plot.subtitle = element_text(color = "gray40"),
legend.position = "right"
)
return(p)
}
# Example usage
seasons <- c(2024)
pbp_data <- load_nba_pbp(seasons)
# Analyze regional efficiency
regional_stats <- analyze_regional_efficiency(pbp_data)
print(regional_stats)
# Create visualizations
efficiency_plot <- plot_zone_efficiency(regional_stats)
frequency_plot <- plot_zone_frequency(regional_stats)
# Combine plots
combined_plot <- efficiency_plot + frequency_plot
ggsave("zone_analysis_combined.png", combined_plot,
width = 16, height = 8, dpi = 300)
# Temporal analysis
temporal_seasons <- c(2019, 2020, 2021, 2022, 2023, 2024)
temporal_data <- analyze_temporal_zones(temporal_seasons)
evolution_plot <- plot_zone_evolution(temporal_data)
ggsave("zone_evolution.png", evolution_plot,
width = 14, height = 8, dpi = 300)
# Efficiency vs frequency
eff_freq_plot <- plot_efficiency_frequency(regional_stats)
ggsave("efficiency_frequency_scatter.png", eff_freq_plot,
width = 12, height = 8, dpi = 300)
R Analysis Features:
- Confidence Intervals: Statistical significance testing for zone efficiency
- Expected Points Added: Calculates value above/below league average by region
- Team Profiling: Compares shot distribution strategies across teams
- Temporal Trends: Multi-season analysis of zone preference evolution
- Advanced Visualizations: Publication-quality plots with error bars and annotations
Evolution of Shot Selection by Region
The Three-Point Revolution (2010-2024)
Key Trends Over Time:
2010-2014: Early Adoption
- Three-point attempts: ~20 per game league-wide
- Mid-range shots still dominated at ~35% of all attempts
- Corner three frequency: 5.2% of shots
- Traditional post-ups and mid-range remained staples
2015-2018: Rapid Transformation
- Three-point attempts surge to ~30 per game
- Golden State Warriors' championship success accelerates trend
- Mid-range frequency drops to ~25% of attempts
- Analytics departments gain influence over shot selection
- Moreyball philosophy (rim + three) becomes mainstream
2019-2024: Modern Optimization
- Three-point attempts exceed 35 per game
- Mid-range attempts fall to historic lows (~19% of shots)
- Restricted area attempts remain stable at ~30%
- Corner three optimization: frequency increased to 8.7%
- Emergence of "heliocentric" offenses maximizing star efficiency
Regional Frequency Changes (2010 vs 2024)
| Region | 2010-11 Frequency | 2023-24 Frequency | Change | Trend Direction |
|---|---|---|---|---|
| Restricted Area | 28.4% | 30.2% | +1.8% | Stable/Slight Increase |
| Paint (Non-RA) | 14.2% | 11.3% | -2.9% | Moderate Decline |
| Short Mid-Range | 18.5% | 9.2% | -9.3% | Dramatic Decline |
| Long Mid-Range | 20.1% | 10.1% | -10.0% | Dramatic Decline |
| Corner Three | 5.2% | 8.7% | +3.5% | Significant Increase |
| Above-Break Three | 13.6% | 30.5% | +16.9% | Explosive Growth |
Key Insight: The most dramatic shift has been the near-doubling of above-the-break three-point attempts, while combined mid-range frequency has fallen by nearly 20 percentage points. This represents the most significant tactical evolution in basketball history.
Strategic Implications of Regional Analysis
Offensive Strategy
Shot Selection Hierarchy
- Rim Attacks (Priority 1): Highest efficiency at 1.29 PPA, but defense concentrates here
- Corner Threes (Priority 2): Second-highest value, shortest three-point distance, requires ball movement
- Above-Break Threes (Priority 3): Volume option for elite shooters, 1.09 PPA
- Mid-Range (Situational): Used primarily in late-clock situations or by elite mid-range shooters
Spacing and Floor Balance
- Five-Out Offense: All players threaten three-point line, maximizes driving lanes
- Corner Specialist Positioning: Stationing shooters in corners creates optimal spacing
- Drive-and-Kick Sequences: Collapsing defense on rim attempts creates open corner looks
- Elbow Actions: Using high post to trigger defensive rotations, creating corner opportunities
Player Deployment by Zone Strength
- Rim Threats: Centers and athletic wings operating in restricted area (Giannis, Zion)
- Corner Snipers: High-percentage three-point shooters stationed in corners (PJ Tucker, Danny Green archetype)
- Pull-Up Shooters: Guards creating above-break threes off dribble (Curry, Lillard)
- Mid-Range Artists: Elite scorers maintaining efficiency from 15-22 feet (DeRozan, Durant, CP3)
Defensive Strategy
Rim Protection Priority
- Paint Packing: Preventing high-value restricted area attempts
- Drop Coverage: Protecting rim while conceding pull-up mid-range
- Weak-Side Help: Rotating from corners to contest drives
- Vertical Spacing: Contesting without fouling in restricted area
Three-Point Defense
- Closeout Discipline: Contesting threes while preventing blow-bys
- Corner Coverage: Never leaving corner shooters unattended
- Switch Everything: Preventing open threes through seamless switching
- No-Middle Defense: Forcing drivers to baseline, away from help and corner kick-outs
The Mid-Range Tradeoff
Modern defensive schemes intentionally concede mid-range attempts to elite scorers, accepting 0.80-0.84 PPA rather than risk 1.09-1.29 PPA from threes and rim attempts. This represents a calculated value tradeoff based on regional efficiency data.
Exception: Elite mid-range shooters (Durant, DeRozan, CP3) can exploit this gap, shooting 45-50% from mid-range for ~0.90-1.00 PPA, which approaches three-point value when accounting for free-throw generation and lower turnover rates.
Advanced Tactical Considerations
Context-Dependent Zone Value
| Situation | Optimal Zone | Reasoning |
|---|---|---|
| End of Quarter | Above-Break Three | Maximize expected value with limited time |
| Late Shot Clock | Mid-Range Pull-Up | Most available shot against set defense |
| Transition | Restricted Area | Highest percentage before defense sets |
| Bonus Free Throws | Restricted Area | Drawing fouls adds ~0.5 to expected value |
| Close Late Game | Varies by Team | Deploy best scorers in their optimal zones |
Zone-Specific Personnel Requirements
Restricted Area Optimization:
- Vertical athleticism for finishing through contact
- Strength to create post position
- Touch for floaters and runners when rim is protected
Corner Three Optimization:
- Shooting accuracy (>38% threshold for value)
- Court awareness to stay in-bounds
- Quick release for catch-and-shoot opportunities
Above-Break Three Optimization:
- Deep range (25+ feet for pull-up attempts)
- Shot creation ability off dribble
- Balance and footwork for contested attempts
Future Trends and Predictions
Anticipated Developments (2025-2030):
- Further Mid-Range Decline: Continued optimization may push mid-range below 15% of attempts
- Logo Shot Normalization: As player skill increases, 30+ foot attempts may become routine
- Defensive Counter-Evolution: New schemes to disrupt three-point optimization
- Rule Changes: Potential adjustments to restore mid-range viability (extended restricted area, deeper three-point line)
- Zone Defense Renaissance: More sophisticated zone schemes to control court regions
- Hybrid Players: Evolution toward position-less basketball with all players threatening all zones
Practical Applications
For Coaches
- Design offensive sets that generate high-value shots in restricted area and corner three zones
- Identify player zone strengths through tracking data and deploy accordingly
- Implement defensive schemes that force opponents toward low-value mid-range attempts
- Use zone efficiency data to evaluate lineup effectiveness
- Develop player skills aligned with high-value zones (rim finishing, corner shooting)
For Players
- Understand personal zone efficiency to identify areas for skill development
- Recognize defensive schemes designed to force specific zone attempts
- Develop counter-moves when defenses take away preferred zones
- Build awareness of spacing principles that create optimal zone opportunities
- Study elite scorers' zone usage patterns in similar roles
For Analysts
- Track zone efficiency trends to identify emerging tactical shifts
- Compare team zone profiles to league averages for strategic insights
- Evaluate player value through zone-specific expected points added
- Model optimal shot distributions based on team personnel
- Analyze opponent tendencies by zone to inform game-planning
For Scouts
- Assess prospect value through zone efficiency profiles
- Identify skill translation risks (mid-range dominant college players)
- Project NBA impact based on skills aligned with high-value zones
- Evaluate fit with team offensive philosophy through zone analysis
- Track development of zone-specific skills (corner three accuracy, rim finishing)
Key Takeaways
- Court region analysis has fundamentally transformed basketball strategy, creating a clear shot value hierarchy
- Restricted area (1.29 PPA) and corner threes (1.17 PPA) represent the highest-value zones
- Mid-range attempts have declined dramatically due to lower efficiency (0.80-0.84 PPA)
- The three-point revolution has driven above-break three frequency from 13.6% (2010) to 30.5% (2024)
- Modern offensive strategy centers on maximizing rim attacks and three-point attempts
- Defensive schemes must balance protecting the rim while contesting three-point attempts
- Python (nba_api) and R (hoopR) provide powerful tools for analyzing zone-specific performance
- Context matters: situational factors can shift zone value (end of quarter, shot clock, foul situation)
- Elite mid-range shooters can still create value, but they represent exceptions to the general optimization
- Future evolution likely includes further three-point emphasis and potential rule changes to restore balance
Conclusion
Court region analysis represents one of the most impactful applications of basketball analytics, directly influencing how the game is played at every level. The quantification of shot value by zone has driven strategic optimization, leading to the dramatic shift away from mid-range attempts toward rim attacks and three-point shots. Understanding regional efficiency patterns is essential for modern coaches, players, analysts, and scouts.
The tools and methodologies presented here—from Python shot chart analysis to R statistical modeling—enable deep investigation of zone-specific performance. As the game continues to evolve, region-based analytics will remain central to tactical decision-making, player development, and strategic innovation. The data clearly shows that not all shots are created equal, and success increasingly depends on maximizing attempts from the highest-value zones while minimizing lower-efficiency opportunities.