Defensive Rating (DRtg)

Beginner 10 min read 1 views Nov 27, 2025

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)

  1. 2003-04 San Antonio Spurs: 88.4 DRtg - Peak Tim Duncan, elite defensive system
  2. 1998-99 San Antonio Spurs: 89.6 DRtg - Lockout-shortened season, defensive dominance
  3. 1974-75 Washington Bullets: 90.4 DRtg - Early metric-tracking era
  4. 1973-74 Milwaukee Bucks: 91.6 DRtg - Kareem Abdul-Jabbar era defense
  5. 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)

  1. 2015-16 San Antonio Spurs: 99.0 DRtg - Kawhi Leonard DPOY season
  2. 2013-14 Indiana Pacers: 99.5 DRtg - Paul George, Roy Hibbert defensive anchor
  3. 2010-11 Boston Celtics: 100.3 DRtg - KG-anchored elite defense
  4. 2016-17 San Antonio Spurs: 103.5 DRtg - Continued defensive excellence
  5. 2012-13 Indiana Pacers: 100.5 DRtg - Defense-first identity

Recent Season Team DRtg Leaders (2023-24)

  1. Minnesota Timberwolves: 108.4 DRtg - Rudy Gobert anchoring defense
  2. Boston Celtics: 110.6 DRtg - Championship-caliber defense
  3. Orlando Magic: 111.1 DRtg - Young defensive identity
  4. Oklahoma City Thunder: 111.4 DRtg - Emerging defensive team
  5. 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.

  1. Ben Wallace (2003-04 Pistons): 89.3 DRtg - DPOY on championship team
  2. Tim Duncan (2003-04 Spurs): 89.7 DRtg - All-time great defensive season
  3. Kawhi Leonard (2015-16 Spurs): 95.3 DRtg - DPOY on elite defense
  4. Marcus Camby (1997-98 Knicks): 90.8 DRtg - Elite rim protector
  5. Kevin Garnett (2007-08 Celtics): 95.7 DRtg - DPOY championship season

Career DRtg Leaders (Minimum 10,000 Minutes)

  1. Tim Duncan: 99.8 DRtg - Consistent elite defender across 19 seasons
  2. Ben Wallace: 100.1 DRtg - Defensive specialist, 4× DPOY
  3. Kawhi Leonard: 103.2 DRtg - 2× DPOY, elite two-way player
  4. Kevin Garnett: 100.5 DRtg - DPOY, versatile defender
  5. 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.

Discussion

Have questions or feedback? Join our community discussion on Discord or GitHub Discussions.