Defensive Rating (DRtg)
Defensive Rating (DRtg): The Complete Guide
Defensive Rating (DRtg) is basketball's most comprehensive metric for measuring defensive effectiveness. It estimates how many points a player or team allows per 100 possessions, providing a standardized way to evaluate defensive performance independent of pace and playing time. While offense often dominates highlight reels and headlines, defense remains fundamental to winning basketball, and DRtg offers the most systematic approach to quantifying defensive impact.
What is Defensive Rating?
Defensive Rating quantifies defensive performance by answering a fundamental question: "How many points does a player or team allow per 100 possessions?" This per-possession framework enables fair comparisons across different eras, playing styles, and pace of play.
The metric was developed by basketball statistician Dean Oliver as part of his Four Factors of Basketball Success framework. By normalizing to 100 possessions, DRtg eliminates the confounding effects of pace—a team playing at a faster tempo will naturally face more possessions and allow more total points, but that doesn't necessarily mean their defense is worse.
The Fundamental Concept
Consider two teams:
- Team A: Allows 95 points per game, averages 90 possessions per game
- Team B: Allows 108 points per game, averages 105 possessions per game
Traditional analysis might suggest Team A has the superior defense because they allow fewer total points. However, when we calculate DRtg:
- Team A DRtg: (95 / 90) × 100 = 105.6 points allowed per 100 possessions
- Team B DRtg: (108 / 105) × 100 = 102.9 points allowed per 100 possessions
Team B actually has the better defense—they allow fewer points relative to the number of possessions they defend. This is the power of pace-adjusted analysis.
Individual vs Team Defensive Rating
Defensive Rating exists in two distinct forms with different calculation methods and interpretations:
Team Defensive Rating
Formula:
Team DRtg = (Points Allowed / Possessions) × 100
Calculation: Straightforward and reliable. Simply divide the total points allowed by the number of possessions the team defended, then multiply by 100.
Possessions Calculation:
Possessions = 0.5 × ((FGA + 0.4 × FTA - 1.07 × (ORB / (ORB + Opponent DRB)) × (FGA - FGM) + TOV)
+ (Opponent FGA + 0.4 × Opponent FTA - 1.07 × (Opponent ORB / (Opponent ORB + DRB))
× (Opponent FGA - Opponent FGM) + Opponent TOV))
Characteristics:
- Highly reliable and objective
- Direct measure of team defensive performance
- Not affected by individual player estimation challenges
- Gold standard for evaluating team defense
- Used extensively in analytics and team evaluation
Individual Defensive Rating
Formula (Dean Oliver Method):
Individual DRtg = Team Defensive Possessions × (1 - (Defensive Stops / (Team FGA - Team ORB + Team TOV + 0.4 × Team FTA)))
Where Defensive Stops is estimated from:
Stops = STL + BLK × FMwt × (1 - 1.07 × DOR%) + DRB × (1 - FMwt)
+ (Opponent FGA - Opponent FGM - Team BLK) / Team × FMwt × (1 - 1.07 × DOR%) × 0.4
+ (Opponent TOV - Team STL) / Team × FMwt
Characteristics:
- Complex estimation requiring multiple team-level adjustments
- Heavily dependent on team context and lineup factors
- Subject to noise and estimation errors
- Less reliable than team DRtg
- Better used as one data point among many defensive metrics
Key Differences
| Aspect | Team DRtg | Individual DRtg |
|---|---|---|
| Reliability | High - Direct measurement | Moderate - Estimation-based |
| Calculation | Simple and transparent | Complex with many adjustments |
| Context Dependency | None - Pure team performance | High - Affected by teammates |
| Sample Size Needed | Season/Game level sufficient | Large sample needed for stability |
| Primary Use | Team evaluation, strategy | Player evaluation (with caveats) |
How to Calculate Defensive Rating
Team Defensive Rating Calculation (Step-by-Step)
Step 1: Calculate Total Possessions
The possession formula estimates how many possessions occurred in a game. A simplified version:
Team Possessions ≈ FGA - ORB + TOV + 0.44 × FTA
Step 2: Divide Points Allowed by Possessions
Points Per Possession = Points Allowed / Possessions
Step 3: Multiply by 100
DRtg = Points Per Possession × 100
Example: A team allows 105 points over 98 possessions:
DRtg = (105 / 98) × 100 = 107.1
This team allows 107.1 points per 100 possessions.
Individual Defensive Rating Calculation (Conceptual)
Individual DRtg is far more complex because defense is inherently a team activity. The calculation attempts to isolate a player's contribution through several steps:
Step 1: Calculate Defensive Stops
Estimate how many opponent possessions the player "stopped" through:
- Steals (direct stops)
- Blocks (partial credit, as some blocked shots are recovered by offense)
- Defensive rebounds (ending opponent possessions)
- Forced misses (estimated through team defense and player's role)
Step 2: Calculate Stop Percentage
What percentage of opponent possessions did the player stop while on court?
Stop % = (Stops × Opponent Possessions) / (Team Possessions × Individual Minutes / (Team Minutes / 5))
Step 3: Estimate Points Allowed Per Possession
Based on stop percentage and team defensive context.
Step 4: Scale to 100 Possessions
Individual DRtg = Points Allowed Per Possession × 100
Important Note: This calculation requires play-by-play data and sophisticated adjustments. Most analysts use pre-calculated values from sources like Basketball-Reference.com or NBA.com rather than computing from scratch.
Interpreting Defensive Rating: Lower is Better
Unlike most statistics where higher is better, Defensive Rating follows an inverse scale: lower DRtg indicates better defense. A team or player allowing fewer points per 100 possessions is more effective defensively.
Understanding DRtg Benchmarks
Modern NBA context (2020-2025 era):
Team Defensive Rating Benchmarks
| DRtg Range | Quality | Description | Examples |
|---|---|---|---|
| Under 105 | Elite | Top defensive teams, championship contenders | 2023-24 Timberwolves (108.4), Celtics (110.6) |
| 105-110 | Very Good | Above average, playoff-caliber defense | Top 10 defenses league-wide |
| 110-113 | Good | Above average but not elite | Playoff teams with solid defense |
| 113-116 | Average | League average range | Middle-of-pack teams |
| 116-120 | Below Average | Defensive weaknesses apparent | Bottom 10 defenses |
| Over 120 | Poor | Among worst defenses in league | Teams with serious defensive problems |
Individual Defensive Rating Benchmarks
Context Note: Individual DRtg is heavily influenced by team defense, so interpret with caution.
- Under 105: Elite defender on excellent defensive team (rare combination)
- 105-108: Very good defender, positive defensive impact
- 108-112: Good defender, above average contribution
- 112-116: Average defender, not a liability
- 116-120: Below average, potential defensive weakness
- Over 120: Poor defender, significant liability
Historical Context: Era Adjustments
DRtg values vary significantly across different eras due to rule changes, playing style, and offensive evolution:
Dead Ball Era (1999-2004)
- League Average DRtg: 104-106
- Characteristics: Physical defense, hand-checking allowed, slow pace
- Elite Team DRtg: Under 98
- Example: 2003-04 Spurs (88.4 DRtg) - historically dominant defense
Traditional Era (2005-2014)
- League Average DRtg: 106-108
- Characteristics: Hand-checking eliminated, moderate pace increase
- Elite Team DRtg: Under 100
- Example: 2007-08 Celtics (98.9 DRtg) - championship defense
Modern Era (2015-2019)
- League Average DRtg: 108-110
- Characteristics: Three-point revolution, pace increasing, offensive-friendly rules
- Elite Team DRtg: Under 103
- Example: 2015-16 Spurs (99.0 DRtg) - elite modern defense
Current Era (2020-Present)
- League Average DRtg: 112-115
- Characteristics: Peak offensive efficiency, spacing maximized, skilled players at all positions
- Elite Team DRtg: Under 108
- Example: 2023-24 Timberwolves (108.4 DRtg) - modern elite defense
Key Insight: Always compare DRtg values to the league average of that specific season. A 105 DRtg in 2024 is good; in 2004 it would be below average.
Historical Defensive Rating Leaders
All-Time Single-Season Team DRtg Leaders (Since 1973-74)
- 2003-04 San Antonio Spurs: 88.4 DRtg - Peak Tim Duncan, elite defensive system
- 1998-99 San Antonio Spurs: 89.6 DRtg - Lockout-shortened season, defensive dominance
- 1974-75 Washington Bullets: 90.4 DRtg - Early metric-tracking era
- 1973-74 Milwaukee Bucks: 91.6 DRtg - Kareem Abdul-Jabbar era defense
- 1997-98 San Antonio Spurs: 92.3 DRtg - Tim Duncan rookie season
Pattern: The Spurs' dynasty years (1990s-2000s) dominate historic defensive ratings, showcasing Tim Duncan's defensive impact and Gregg Popovich's defensive schemes.
Best Team DRtg Seasons (Modern Era: 2010-Present)
- 2015-16 San Antonio Spurs: 99.0 DRtg - Kawhi Leonard DPOY season
- 2013-14 Indiana Pacers: 99.5 DRtg - Paul George, Roy Hibbert defensive anchor
- 2010-11 Boston Celtics: 100.3 DRtg - KG-anchored elite defense
- 2016-17 San Antonio Spurs: 103.5 DRtg - Continued defensive excellence
- 2012-13 Indiana Pacers: 100.5 DRtg - Defense-first identity
Recent Season Team DRtg Leaders (2023-24)
- Minnesota Timberwolves: 108.4 DRtg - Rudy Gobert anchoring defense
- Boston Celtics: 110.6 DRtg - Championship-caliber defense
- Orlando Magic: 111.1 DRtg - Young defensive identity
- Oklahoma City Thunder: 111.4 DRtg - Emerging defensive team
- Cleveland Cavaliers: 111.7 DRtg - Evan Mobley impact
Individual DRtg Single-Season Leaders (Minimum 2000 Minutes)
Important Note: Individual DRtg leaders are almost always players on elite defensive teams, highlighting the metric's team-dependency.
- Ben Wallace (2003-04 Pistons): 89.3 DRtg - DPOY on championship team
- Tim Duncan (2003-04 Spurs): 89.7 DRtg - All-time great defensive season
- Kawhi Leonard (2015-16 Spurs): 95.3 DRtg - DPOY on elite defense
- Marcus Camby (1997-98 Knicks): 90.8 DRtg - Elite rim protector
- Kevin Garnett (2007-08 Celtics): 95.7 DRtg - DPOY championship season
Career DRtg Leaders (Minimum 10,000 Minutes)
- Tim Duncan: 99.8 DRtg - Consistent elite defender across 19 seasons
- Ben Wallace: 100.1 DRtg - Defensive specialist, 4× DPOY
- Kawhi Leonard: 103.2 DRtg - 2× DPOY, elite two-way player
- Kevin Garnett: 100.5 DRtg - DPOY, versatile defender
- Rudy Gobert: 103.8 DRtg - 4× DPOY, modern rim protection
Observation: Career leaders are typically defensive anchors who played extended periods on strong defensive teams.
Limitations of Defensive Rating
While DRtg is the most comprehensive single-number defensive metric, it has significant limitations that analysts must understand:
1. Individual DRtg is Team-Dependent
The Problem: A player's individual DRtg is heavily influenced by their teammates' defensive performance and team defensive scheme.
Example: A solid defender on a poor defensive team will have a worse individual DRtg than an average defender on an elite defensive team. This makes cross-team comparisons problematic.
Why This Happens: Defense is inherently collaborative. Rotations, help defense, and rim protection are shared responsibilities. The metric struggles to isolate individual contributions from team context.
Mitigation: Compare players to their teammates' DRtg or use lineup-based metrics (defensive on/off splits) to better isolate individual impact.
2. Doesn't Capture Defensive Versatility
The Problem: DRtg doesn't distinguish between different types of defensive value. A rim protector who deters shots and a perimeter defender who prevents ball handlers from penetrating both contribute defensively, but in ways DRtg doesn't differentiate.
Example: A player who guards the opponent's best player might have worse DRtg than a teammate who guards weaker opponents, even though the first player provides more defensive value.
Missing Elements:
- Defensive assignments (who guards whom)
- Shot deterrence (altered/contested shots)
- Defensive communication and leadership
- Switching ability and position versatility
3. Sample Size Sensitivity
The Problem: Individual DRtg requires large sample sizes to stabilize. Small samples (10-20 games) can produce misleading values due to random variance.
Example: A player might have exceptional DRtg over a 10-game stretch simply because opponents shot poorly during those games, not because of the player's defense.
Rule of Thumb: Require at least 500-1000 minutes of playing time before drawing conclusions from individual DRtg.
4. Lineup Effects and Bench Units
The Problem: Players who play primarily with good defenders against weak opposing lineups (common for bench players) will have artificially inflated (better) DRtg.
Example: A backup center playing against opponent bench units will often have better DRtg than the starting center facing opponent starters, even if the starter is the better defender.
5. Doesn't Account for Opponent Quality
The Problem: DRtg doesn't adjust for the strength of opposing offenses. Facing elite offensive teams naturally produces worse DRtg.
Example: A team in a division with several elite offensive teams (e.g., playing Warriors and Suns frequently) will have worse DRtg than a team facing weaker opponents, all else equal.
6. Coaching and System Effects
The Problem: Defensive schemes and coaching philosophy significantly impact DRtg independent of individual talent.
Example: The same player might have a 108 DRtg in a switching, aggressive defensive system and 113 DRtg in a drop-coverage, conservative system, not because their defensive ability changed but because of systemic factors.
7. Ignores Specific Defensive Skills
The Problem: DRtg is a results-based metric that doesn't capture specific defensive skills or processes.
What's Missing:
- Perimeter defense quality (opponent FG% when guarding)
- Post defense effectiveness
- Defensive rebounding
- Help defense rotations
- Pick-and-roll defense
- Closeout effectiveness
Modern Solution: Supplement DRtg with tracking data metrics like Defensive Field Goal Percentage (DFG%), Defensive RAPTOR, or Defensive Estimated Plus-Minus (DEPM).
8. Doesn't Measure Defensive Consistency
The Problem: DRtg is an average that doesn't reveal game-to-game variance in defensive performance.
Example: Two players with identical 110 DRtg might have very different profiles—one consistently allows 110 points per 100 possessions, while another alternates between elite (100) and poor (120) performances.
9. Pace and Playing Style Biases
The Problem: While DRtg adjusts for pace, certain playing styles can still inflate or deflate the metric.
Example: Teams that play extremely fast and take quick shots may face more possessions where opponents are in transition (easier offense), potentially hurting DRtg despite solid half-court defense.
10. Historical Comparison Challenges
The Problem: Rule changes, offensive evolution, and game style shifts make historical DRtg comparisons imperfect.
Example: A 100 DRtg in 2004 (before hand-checking elimination and three-point revolution) represents very different defensive quality than 100 DRtg in 2024.
Solution: Always adjust for era by comparing to league average DRtg of that season (e.g., "5 points per 100 possessions better than league average").
Complementary Defensive Metrics
To overcome DRtg's limitations, analysts should use it alongside other defensive metrics:
- Defensive Win Shares (DWS): Estimates wins contributed by defense
- Defensive Box Plus/Minus (DBPM): Box-score-based estimate of defensive impact
- Defensive Estimated Plus-Minus (DEPM): Regression-based defensive impact
- Defensive Real Plus-Minus (DRPM): Lineup-based defensive impact measurement
- Defensive Field Goal % (DFG%): Opponent shooting % when defender is primary
- Steals, Blocks, Defensive Rebounds: Traditional counting stats for defensive actions
- Opponent Points Per Possession (On/Off): Team defensive performance with player on vs. off court
Code Examples: Calculating and Analyzing Defensive Rating
Python: Basic Team DRtg Calculation
import pandas as pd
import numpy as np
def calculate_possessions(fga, orb, tov, fta):
"""
Calculate team possessions using simplified formula.
Parameters:
-----------
fga : int or float
Field goal attempts
orb : int or float
Offensive rebounds
tov : int or float
Turnovers
fta : int or float
Free throw attempts
Returns:
--------
float
Estimated possessions
"""
possessions = fga - orb + tov + 0.44 * fta
return possessions
def calculate_team_drtg(points_allowed, opp_fga, opp_orb, opp_tov, opp_fta):
"""
Calculate Team Defensive Rating.
Parameters:
-----------
points_allowed : int or float
Points allowed by team
opp_fga : int or float
Opponent field goal attempts
opp_orb : int or float
Opponent offensive rebounds
opp_tov : int or float
Opponent turnovers
opp_fta : int or float
Opponent free throw attempts
Returns:
--------
float
Defensive Rating (points allowed per 100 possessions)
"""
possessions = calculate_possessions(opp_fga, opp_orb, opp_tov, opp_fta)
if possessions == 0:
return 0.0
drtg = (points_allowed / possessions) * 100
return drtg
# Example: Single game team defense
game_stats = {
'team': 'Boston Celtics',
'points_allowed': 98,
'opp_fga': 85,
'opp_orb': 8,
'opp_tov': 14,
'opp_fta': 22
}
drtg = calculate_team_drtg(
game_stats['points_allowed'],
game_stats['opp_fga'],
game_stats['opp_orb'],
game_stats['opp_tov'],
game_stats['opp_fta']
)
print(f"{game_stats['team']}: {drtg:.1f} DRtg")
# Output: Boston Celtics: 105.2 DRtg
# Calculate for multiple teams
teams_data = {
'Team': ['Celtics', 'Timberwolves', 'Magic', 'Thunder', 'Lakers'],
'Points_Allowed': [9852, 9123, 9285, 9467, 10234],
'Opp_FGA': [7234, 7012, 7189, 7298, 7456],
'Opp_ORB': [789, 723, 801, 756, 834],
'Opp_TOV': [1234, 1289, 1198, 1267, 1123],
'Opp_FTA': [1823, 1756, 1834, 1901, 1967]
}
df = pd.DataFrame(teams_data)
# Calculate possessions and DRtg
df['Possessions'] = df.apply(
lambda row: calculate_possessions(
row['Opp_FGA'], row['Opp_ORB'], row['Opp_TOV'], row['Opp_FTA']
),
axis=1
)
df['DRtg'] = df.apply(
lambda row: calculate_team_drtg(
row['Points_Allowed'], row['Opp_FGA'],
row['Opp_ORB'], row['Opp_TOV'], row['Opp_FTA']
),
axis=1
)
# Sort by DRtg (lower is better)
df_sorted = df[['Team', 'Points_Allowed', 'Possessions', 'DRtg']].sort_values('DRtg')
print("\nTeam Defensive Ratings (2023-24 Season):")
print(df_sorted.to_string(index=False))
Python: Advanced Defensive Analytics
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
def analyze_defensive_performance(df):
"""
Comprehensive defensive performance analysis.
Parameters:
-----------
df : pandas.DataFrame
Team statistics dataframe with defensive metrics
Returns:
--------
dict
Analysis results and categorizations
"""
# Calculate league average
league_avg_drtg = df['DRtg'].mean()
# Calculate relative DRtg (difference from league average)
df['Relative_DRtg'] = df['DRtg'] - league_avg_drtg
# Categorize defensive quality
def categorize_defense(drtg, league_avg):
diff = drtg - league_avg
if diff <= -5:
return 'Elite'
elif diff <= -2:
return 'Very Good'
elif diff <= 2:
return 'Average'
elif diff <= 5:
return 'Below Average'
else:
return 'Poor'
df['Defense_Quality'] = df.apply(
lambda row: categorize_defense(row['DRtg'], league_avg_drtg),
axis=1
)
# Calculate defensive efficiency rank
df['Def_Rank'] = df['DRtg'].rank(method='min')
# Analyze defensive components
analysis = {
'league_avg_drtg': league_avg_drtg,
'best_defense': df.loc[df['DRtg'].idxmin(), 'Team'],
'best_drtg': df['DRtg'].min(),
'worst_defense': df.loc[df['DRtg'].idxmax(), 'Team'],
'worst_drtg': df['DRtg'].max(),
'drtg_range': df['DRtg'].max() - df['DRtg'].min(),
'std_dev': df['DRtg'].std()
}
return df, analysis
def calculate_opponent_efficiency(df):
"""
Calculate various opponent efficiency metrics.
Parameters:
-----------
df : pandas.DataFrame
Team defensive statistics
Returns:
--------
pandas.DataFrame
Enhanced dataframe with efficiency metrics
"""
# Opponent effective field goal percentage
df['Opp_eFG%'] = (df['Opp_FGM'] + 0.5 * df['Opp_3PM']) / df['Opp_FGA']
# Opponent turnover percentage
df['Opp_TOV%'] = 100 * df['Opp_TOV'] / df['Possessions']
# Opponent offensive rebounding percentage (simplified)
df['Opp_ORB%'] = 100 * df['Opp_ORB'] / (df['Opp_ORB'] + df['DRB'])
# Opponent free throw rate
df['Opp_FTr'] = df['Opp_FTA'] / df['Opp_FGA']
return df
def defensive_four_factors(df):
"""
Analyze defensive Four Factors (Dean Oliver framework).
The Four Factors of defense:
1. Opponent eFG% (most important)
2. Opponent TOV% (very important)
3. Opponent ORB% (important)
4. Opponent FTr (least important)
Parameters:
-----------
df : pandas.DataFrame
Team defensive statistics with efficiency metrics
Returns:
--------
pandas.DataFrame
Four Factors analysis
"""
four_factors = df[[
'Team', 'DRtg', 'Opp_eFG%', 'Opp_TOV%',
'Opp_ORB%', 'Opp_FTr', 'Defense_Quality'
]].copy()
# Calculate ranks for each factor (lower opponent efficiency = better defense)
four_factors['eFG%_Rank'] = four_factors['Opp_eFG%'].rank()
four_factors['TOV%_Rank'] = four_factors['Opp_TOV%'].rank(ascending=False) # Higher is better
four_factors['ORB%_Rank'] = four_factors['Opp_ORB%'].rank()
four_factors['FTr_Rank'] = four_factors['Opp_FTr'].rank()
return four_factors
# Example usage with comprehensive season data
season_data = {
'Team': ['Timberwolves', 'Celtics', 'Magic', 'Thunder', 'Cavaliers',
'Lakers', 'Warriors', 'Nets', 'Wizards', 'Pistons'],
'Points_Allowed': [9123, 9852, 9285, 9467, 9689, 10234, 10567, 10892, 11234, 11567],
'Opp_FGA': [7012, 7234, 7189, 7298, 7423, 7456, 7598, 7734, 7823, 7901],
'Opp_FGM': [3156, 3401, 3298, 3389, 3512, 3645, 3789, 3912, 4023, 4156],
'Opp_3PM': [891, 945, 912, 934, 967, 1012, 1045, 1089, 1123, 1167],
'Opp_ORB': [723, 789, 801, 756, 812, 834, 867, 901, 923, 945],
'Opp_TOV': [1289, 1234, 1198, 1267, 1212, 1123, 1089, 1045, 1012, 989],
'Opp_FTA': [1756, 1823, 1834, 1901, 1923, 1967, 2012, 2089, 2134, 2201],
'DRB': [3567, 3489, 3512, 3478, 3445, 3401, 3378, 3334, 3289, 3245]
}
df = pd.DataFrame(season_data)
# Calculate possessions and DRtg
df['Possessions'] = df.apply(
lambda row: calculate_possessions(
row['Opp_FGA'], row['Opp_ORB'], row['Opp_TOV'], row['Opp_FTA']
),
axis=1
)
df['DRtg'] = (df['Points_Allowed'] / df['Possessions']) * 100
# Analyze defensive performance
df_analyzed, analysis_summary = analyze_defensive_performance(df)
# Calculate opponent efficiency metrics
df_analyzed = calculate_opponent_efficiency(df_analyzed)
# Analyze Four Factors
four_factors_analysis = defensive_four_factors(df_analyzed)
# Display results
print("=== Defensive Rating Analysis ===\n")
print(f"League Average DRtg: {analysis_summary['league_avg_drtg']:.1f}")
print(f"Best Defense: {analysis_summary['best_defense']} ({analysis_summary['best_drtg']:.1f})")
print(f"Worst Defense: {analysis_summary['worst_defense']} ({analysis_summary['worst_drtg']:.1f})")
print(f"DRtg Range: {analysis_summary['drtg_range']:.1f}")
print(f"Standard Deviation: {analysis_summary['std_dev']:.2f}\n")
print("Team Defensive Rankings:")
print(df_analyzed[['Team', 'DRtg', 'Relative_DRtg', 'Defense_Quality', 'Def_Rank']].to_string(index=False))
print("\n=== Defensive Four Factors ===")
print(four_factors_analysis[['Team', 'DRtg', 'Opp_eFG%', 'Opp_TOV%',
'Opp_ORB%', 'Opp_FTr']].to_string(index=False))
Python: Defensive Rating Visualization
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
def visualize_defensive_ratings(df):
"""
Create comprehensive defensive rating visualizations.
Parameters:
-----------
df : pandas.DataFrame
Team defensive statistics with DRtg calculated
"""
sns.set_style("whitegrid")
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
# Sort by DRtg for visualization
df_sorted = df.sort_values('DRtg')
# 1. Defensive Rating Bar Chart
colors = ['#2ecc71' if x <= 110 else '#f39c12' if x <= 115 else '#e74c3c'
for x in df_sorted['DRtg']]
axes[0, 0].barh(df_sorted['Team'], df_sorted['DRtg'], color=colors, alpha=0.8)
axes[0, 0].axvline(x=df['DRtg'].mean(), color='red', linestyle='--',
linewidth=2, label=f"League Avg: {df['DRtg'].mean():.1f}")
axes[0, 0].set_xlabel('Defensive Rating (Points Allowed per 100 Poss)', fontsize=11)
axes[0, 0].set_ylabel('Team', fontsize=11)
axes[0, 0].set_title('Team Defensive Ratings (Lower is Better)',
fontsize=13, fontweight='bold')
axes[0, 0].legend()
axes[0, 0].invert_xaxis() # Lower values on right (better)
# 2. DRtg Distribution
axes[0, 1].hist(df['DRtg'], bins=15, edgecolor='black', alpha=0.7, color='#3498db')
axes[0, 1].axvline(df['DRtg'].mean(), color='red', linestyle='--',
linewidth=2, label=f"Mean: {df['DRtg'].mean():.1f}")
axes[0, 1].axvline(df['DRtg'].median(), color='green', linestyle='--',
linewidth=2, label=f"Median: {df['DRtg'].median():.1f}")
axes[0, 1].set_xlabel('Defensive Rating', fontsize=11)
axes[0, 1].set_ylabel('Number of Teams', fontsize=11)
axes[0, 1].set_title('Distribution of Defensive Ratings',
fontsize=13, fontweight='bold')
axes[0, 1].legend()
# 3. Four Factors Scatter: DRtg vs Opp eFG%
axes[1, 0].scatter(df['Opp_eFG%']*100, df['DRtg'], s=100, alpha=0.6, color='#e74c3c')
# Add trend line
z = np.polyfit(df['Opp_eFG%']*100, df['DRtg'], 1)
p = np.poly1d(z)
axes[1, 0].plot(df['Opp_eFG%']*100, p(df['Opp_eFG%']*100),
"r--", alpha=0.8, linewidth=2)
# Add correlation
corr = df['Opp_eFG%'].corr(df['DRtg'])
axes[1, 0].text(0.05, 0.95, f'Correlation: {corr:.3f}',
transform=axes[1, 0].transAxes, fontsize=10,
verticalalignment='top', bbox=dict(boxstyle='round',
facecolor='wheat', alpha=0.5))
axes[1, 0].set_xlabel('Opponent eFG% (Most Important Defensive Factor)', fontsize=11)
axes[1, 0].set_ylabel('Defensive Rating', fontsize=11)
axes[1, 0].set_title('DRtg vs Opponent Shooting Efficiency',
fontsize=13, fontweight='bold')
axes[1, 0].invert_yaxis()
# 4. Defensive Quality Pie Chart
quality_counts = df['Defense_Quality'].value_counts()
colors_pie = ['#2ecc71', '#3498db', '#f39c12', '#e74c3c', '#95a5a6']
axes[1, 1].pie(quality_counts.values, labels=quality_counts.index,
autopct='%1.1f%%', startangle=90, colors=colors_pie[:len(quality_counts)])
axes[1, 1].set_title('Distribution of Defensive Quality Tiers',
fontsize=13, fontweight='bold')
plt.tight_layout()
plt.savefig('defensive_rating_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
def plot_drtg_trends_over_seasons(seasons_df):
"""
Visualize DRtg trends across multiple seasons.
Parameters:
-----------
seasons_df : pandas.DataFrame
DataFrame with columns: Season, League_Avg_DRtg
"""
plt.figure(figsize=(14, 7))
plt.plot(seasons_df['Season'], seasons_df['League_Avg_DRtg'],
marker='o', linewidth=2.5, markersize=9, color='#e74c3c', label='League Average DRtg')
# Add shaded regions for eras
plt.axhspan(88, 100, alpha=0.1, color='green', label='Historic Elite Range')
plt.axhspan(100, 108, alpha=0.1, color='yellow', label='Traditional Era Range')
plt.axhspan(108, 116, alpha=0.1, color='orange', label='Modern Era Range')
plt.xlabel('Season', fontsize=13)
plt.ylabel('League Average Defensive Rating', fontsize=13)
plt.title('NBA League Average Defensive Rating Over Time\n(Higher values = more points allowed = worse defense)',
fontsize=15, fontweight='bold')
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.legend(loc='upper left')
plt.tight_layout()
plt.savefig('drtg_historical_trends.png', dpi=300, bbox_inches='tight')
plt.show()
# Example usage
if __name__ == "__main__":
# Use previously created dataframe
visualize_defensive_ratings(df_analyzed)
# Historical trends
historical_data = pd.DataFrame({
'Season': ['2010-11', '2011-12', '2012-13', '2013-14', '2014-15',
'2015-16', '2016-17', '2017-18', '2018-19', '2019-20',
'2020-21', '2021-22', '2022-23', '2023-24'],
'League_Avg_DRtg': [106.2, 105.8, 106.5, 107.0, 106.8,
107.8, 108.8, 109.0, 110.4, 109.5,
112.0, 110.6, 114.0, 114.6]
})
plot_drtg_trends_over_seasons(historical_data)
R: Defensive Rating Analysis
# Load required libraries
library(tidyverse)
library(ggplot2)
library(scales)
# Function to calculate possessions
calculate_possessions <- function(fga, orb, tov, fta) {
possessions <- fga - orb + tov + 0.44 * fta
return(possessions)
}
# Function to calculate team defensive rating
calculate_team_drtg <- function(points_allowed, opp_fga, opp_orb, opp_tov, opp_fta) {
possessions <- calculate_possessions(opp_fga, opp_orb, opp_tov, opp_fta)
drtg <- ifelse(possessions == 0, 0, (points_allowed / possessions) * 100)
return(drtg)
}
# Sample team defensive data
team_defense <- tibble(
team = c("Timberwolves", "Celtics", "Magic", "Thunder", "Cavaliers",
"Heat", "Pelicans", "Lakers", "Warriors", "Wizards"),
points_allowed = c(9123, 9852, 9285, 9467, 9689, 9834, 10012, 10234, 10456, 11123),
opp_fga = c(7012, 7234, 7189, 7298, 7423, 7489, 7556, 7623, 7689, 7834),
opp_fgm = c(3156, 3401, 3298, 3389, 3512, 3578, 3645, 3712, 3789, 3901),
opp_3pm = c(891, 945, 912, 934, 967, 989, 1012, 1034, 1056, 1089),
opp_orb = c(723, 789, 801, 756, 812, 834, 856, 878, 901, 945),
opp_tov = c(1289, 1234, 1198, 1267, 1212, 1178, 1145, 1123, 1089, 1034),
opp_fta = c(1756, 1823, 1834, 1901, 1923, 1956, 1989, 2012, 2045, 2123),
drb = c(3567, 3489, 3512, 3478, 3445, 3423, 3401, 3378, 3356, 3289)
)
# Calculate defensive metrics
team_defense <- team_defense %>%
mutate(
possessions = calculate_possessions(opp_fga, opp_orb, opp_tov, opp_fta),
drtg = (points_allowed / possessions) * 100,
opp_efg_pct = (opp_fgm + 0.5 * opp_3pm) / opp_fga,
opp_tov_pct = 100 * opp_tov / possessions,
opp_orb_pct = 100 * opp_orb / (opp_orb + drb),
opp_ftr = opp_fta / opp_fga
) %>%
arrange(drtg)
# Add league context
league_avg_drtg <- mean(team_defense$drtg)
team_defense <- team_defense %>%
mutate(
relative_drtg = drtg - league_avg_drtg,
def_rank = row_number(),
defense_quality = case_when(
relative_drtg <= -5 ~ "Elite",
relative_drtg <= -2 ~ "Very Good",
relative_drtg <= 2 ~ "Average",
relative_drtg <= 5 ~ "Below Average",
TRUE ~ "Poor"
)
)
# Display results
cat("=== Team Defensive Rating Analysis ===\n\n")
cat(sprintf("League Average DRtg: %.1f\n", league_avg_drtg))
cat(sprintf("Best Defense: %s (%.1f)\n",
team_defense$team[1], team_defense$drtg[1]))
cat(sprintf("Worst Defense: %s (%.1f)\n\n",
team_defense$team[nrow(team_defense)],
team_defense$drtg[nrow(team_defense)]))
print("Defensive Rankings:")
team_defense %>%
select(def_rank, team, drtg, relative_drtg, defense_quality) %>%
mutate(drtg = round(drtg, 1),
relative_drtg = round(relative_drtg, 1)) %>%
print(n = Inf)
# Visualization 1: Defensive Rating Bar Chart
p1 <- ggplot(team_defense, aes(x = reorder(team, -drtg), y = drtg, fill = defense_quality)) +
geom_bar(stat = "identity", alpha = 0.8) +
geom_hline(yintercept = league_avg_drtg, linetype = "dashed",
color = "red", size = 1) +
scale_fill_manual(values = c("Elite" = "#2ecc71", "Very Good" = "#3498db",
"Average" = "#f39c12", "Below Average" = "#e67e22",
"Poor" = "#e74c3c")) +
coord_flip() +
labs(
title = "Team Defensive Ratings (2023-24 Season)",
subtitle = sprintf("League Average: %.1f DRtg (dashed line)", league_avg_drtg),
x = "Team",
y = "Defensive Rating (Points Allowed per 100 Possessions)",
fill = "Defensive Quality"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
axis.title = element_text(size = 12),
axis.text = element_text(size = 10),
legend.position = "bottom"
)
print(p1)
ggsave("defensive_rating_by_team.png", p1, width = 12, height = 8, dpi = 300)
# Visualization 2: DRtg vs Opponent eFG%
p2 <- ggplot(team_defense, aes(x = opp_efg_pct * 100, y = drtg)) +
geom_point(aes(color = defense_quality), size = 4, alpha = 0.7) +
geom_smooth(method = "lm", se = TRUE, color = "#e74c3c", linetype = "dashed") +
scale_color_manual(values = c("Elite" = "#2ecc71", "Very Good" = "#3498db",
"Average" = "#f39c12", "Below Average" = "#e67e22",
"Poor" = "#e74c3c")) +
labs(
title = "Defensive Rating vs Opponent Shooting Efficiency",
subtitle = "Opponent eFG% is the most important defensive factor",
x = "Opponent Effective Field Goal % (eFG%)",
y = "Defensive Rating",
color = "Defensive Quality"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
axis.title = element_text(size = 12),
legend.position = "bottom"
)
print(p2)
ggsave("drtg_vs_opponent_efg.png", p2, width = 10, height = 7, dpi = 300)
# Statistical summary
cat("\n=== Defensive Four Factors Correlation with DRtg ===\n")
correlations <- team_defense %>%
select(drtg, opp_efg_pct, opp_tov_pct, opp_orb_pct, opp_ftr) %>%
cor()
cat(sprintf("Opp eFG%% correlation: %.3f (most important)\n", correlations[1, 2]))
cat(sprintf("Opp TOV%% correlation: %.3f (forced turnovers help)\n", -correlations[1, 3]))
cat(sprintf("Opp ORB%% correlation: %.3f (limit 2nd chances)\n", correlations[1, 4]))
cat(sprintf("Opp FTr correlation: %.3f (least important)\n", correlations[1, 5]))
R: Historical DRtg Trends Analysis
library(tidyverse)
library(ggplot2)
# Historical league average defensive ratings
historical_drtg <- tibble(
season = c("2004-05", "2005-06", "2006-07", "2007-08", "2008-09", "2009-10",
"2010-11", "2011-12", "2012-13", "2013-14", "2014-15", "2015-16",
"2016-17", "2017-18", "2018-19", "2019-20", "2020-21", "2021-22",
"2022-23", "2023-24"),
league_avg_drtg = c(106.4, 106.1, 106.7, 107.5, 108.3, 107.9,
106.2, 105.8, 106.5, 107.0, 106.8, 107.8,
108.8, 109.0, 110.4, 109.5, 112.0, 110.6,
114.0, 114.6),
era = c(rep("Traditional", 6), rep("Early Modern", 6), rep("Modern", 5), rep("Current", 3))
)
# Calculate year-over-year change
historical_drtg <- historical_drtg %>%
mutate(
year = as.numeric(substr(season, 1, 4)),
yoy_change = league_avg_drtg - lag(league_avg_drtg)
)
# Visualization: Historical trends
p_historical <- ggplot(historical_drtg, aes(x = season, y = league_avg_drtg, group = 1)) +
geom_line(size = 1.5, color = "#e74c3c") +
geom_point(aes(color = era), size = 4) +
geom_hline(yintercept = 110, linetype = "dashed", color = "blue", alpha = 0.5) +
scale_color_manual(values = c("Traditional" = "#2ecc71",
"Early Modern" = "#3498db",
"Modern" = "#f39c12",
"Current" = "#e74c3c")) +
labs(
title = "NBA League Average Defensive Rating Trends (2004-2024)",
subtitle = "Rising DRtg reflects increasingly efficient offenses",
x = "Season",
y = "League Average Defensive Rating",
color = "Era"
) +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold"),
plot.subtitle = element_text(size = 12),
axis.title = element_text(size = 12),
axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "bottom"
) +
annotate("text", x = 10, y = 110.5,
label = "110 DRtg threshold",
color = "blue", size = 3.5)
print(p_historical)
ggsave("drtg_historical_trends.png", p_historical, width = 14, height = 7, dpi = 300)
# Summary statistics by era
cat("\n=== Defensive Rating by Era ===\n")
era_summary <- historical_drtg %>%
group_by(era) %>%
summarise(
avg_drtg = mean(league_avg_drtg),
min_drtg = min(league_avg_drtg),
max_drtg = max(league_avg_drtg),
seasons = n()
) %>%
arrange(avg_drtg)
print(era_summary)
Practical Applications of Defensive Rating
For Team Executives and Analysts
- Roster Construction: Balance offensive firepower with defensive capability using DRtg as evaluation baseline
- Trade Evaluation: Compare players' defensive impact through DRtg and on/off defensive metrics
- Draft Scouting: Project college players' NBA defensive fit by analyzing positional DRtg trends
- Coaching Evaluation: Track year-over-year team DRtg changes to assess coaching effectiveness
- Free Agency: Identify defensive specialists who improve team DRtg within budget constraints
For Coaches
- Lineup Optimization: Test defensive lineup combinations using DRtg in different configurations
- Matchup Planning: Compare team DRtg against specific opponent offensive styles
- Defensive System Evaluation: Monitor whether scheme changes improve or hurt DRtg
- Player Development: Track young players' defensive progression through individual DRtg trends
- In-Game Adjustments: Use quarter/half DRtg to identify when defensive breakdowns occur
For Bettors and Fantasy Players
- Totals Betting: Team DRtg helps predict game totals (under/over) with greater accuracy
- Player Props: Opponent DRtg affects player performance expectations (points, rebounds, etc.)
- Fantasy Basketball: Target offensive players facing weak defenses (high DRtg teams)
- Daily Fantasy: Avoid players facing elite defenses (low DRtg teams) in cash games
Conclusion
Defensive Rating represents the most comprehensive single-number metric for evaluating defensive performance in basketball. By measuring points allowed per 100 possessions, DRtg provides pace-adjusted comparisons that enable meaningful analysis across teams, eras, and playing styles.
While team DRtg is highly reliable and forms the foundation of defensive analysis, individual DRtg remains more controversial due to the inherent difficulty of isolating individual defensive contributions from team context. The best analytical approach combines DRtg with supplementary metrics—opponent field goal percentage, steals, blocks, defensive rebounding, and advanced metrics like DRPM—to build a complete defensive profile.
As offenses continue to evolve with improved spacing, shooting, and analytics-driven shot selection, defensive ratings have risen league-wide. Modern elite defenses must excel at limiting three-point attempts, protecting the rim, forcing turnovers, and preventing offensive rebounds—the defensive Four Factors that most strongly correlate with low DRtg.
Understanding defensive rating's calculation, interpretation, and limitations is essential for anyone serious about basketball analysis. Defense may not generate highlight plays as frequently as offense, but championship teams are built on defensive foundations that DRtg helps us measure and evaluate systematically.