Case Study: The Red Zone Paradox

Why the team with the best offense couldn't score touchdowns


Introduction

In 2022, the NFL witnessed a puzzling phenomenon: one of the league's most explosive offenses consistently stalled in the red zone. Despite ranking near the top in total yards, EPA, and big plays, this team ranked in the bottom third for red zone touchdown percentage. Their season became a case study in why situational performance can diverge from overall efficiency.

This case study examines the disconnect between offensive dominance and red zone struggles, uncovering the strategic and personnel factors that drive situational performance.


The Puzzle

Setting the Stage

Consider these season statistics for our mystery team:

Overall Offensive Performance: - Total yards: 6,432 (2nd in NFL) - EPA/play: +0.095 (5th) - Yards per play: 6.1 (4th) - First downs: 368 (6th) - Points per game: 25.8 (7th)

Red Zone Performance: - Red zone trips: 62 (above average) - Red zone TD rate: 49% (26th) - Red zone points per trip: 3.8 (24th) - Red zone EPA: -0.08 (29th)

How could an offense rank top-5 in overall efficiency but bottom-10 in the red zone?


Data Analysis

Step 1: Confirming the Gap

import pandas as pd
import numpy as np
import nfl_data_py as nfl

# Load 2022 data
pbp = nfl.import_pbp_data([2022])

def calculate_zone_comparison(pbp: pd.DataFrame, team: str) -> dict:
    """Compare performance in red zone vs outside red zone."""

    team_plays = pbp[
        (pbp['posteam'] == team) &
        (pbp['play_type'].isin(['pass', 'run'])) &
        (pbp['epa'].notna())
    ]

    # Red zone
    rz = team_plays[team_plays['yardline_100'] <= 20]
    outside_rz = team_plays[team_plays['yardline_100'] > 20]

    return {
        'outside_rz_epa': outside_rz['epa'].mean(),
        'rz_epa': rz['epa'].mean(),
        'outside_rz_success': (outside_rz['epa'] > 0).mean(),
        'rz_success': (rz['epa'] > 0).mean(),
        'outside_rz_plays': len(outside_rz),
        'rz_plays': len(rz)
    }

# Analysis for our team
comparison = calculate_zone_comparison(pbp, 'TEAM')
print("Zone Comparison:")
print(f"Outside RZ EPA: {comparison['outside_rz_epa']:+.3f}")
print(f"Red Zone EPA: {comparison['rz_epa']:+.3f}")
print(f"Difference: {comparison['rz_epa'] - comparison['outside_rz_epa']:+.3f}")

Result: - Outside Red Zone EPA: +0.12 - Red Zone EPA: -0.08 - Drop-off: -0.20 EPA per play

This 0.20 EPA decline was twice the league average drop when entering the red zone.

Step 2: Identifying the Causes

def analyze_rz_breakdown(pbp: pd.DataFrame, team: str) -> dict:
    """Detailed red zone performance breakdown."""

    rz_plays = pbp[
        (pbp['posteam'] == team) &
        (pbp['yardline_100'] <= 20) &
        (pbp['play_type'].isin(['pass', 'run'])) &
        (pbp['epa'].notna())
    ]

    # By play type
    passes = rz_plays[rz_plays['play_type'] == 'pass']
    rushes = rz_plays[rz_plays['play_type'] == 'run']

    # By field position within red zone
    deep_rz = rz_plays[rz_plays['yardline_100'] <= 10]  # Inside 10
    outer_rz = rz_plays[(rz_plays['yardline_100'] > 10) & (rz_plays['yardline_100'] <= 20)]

    return {
        'pass_epa': passes['epa'].mean(),
        'rush_epa': rushes['epa'].mean(),
        'pass_rate': len(passes) / len(rz_plays),
        'deep_rz_epa': deep_rz['epa'].mean(),
        'outer_rz_epa': outer_rz['epa'].mean(),
        'goal_to_go_td_rate': (rz_plays[rz_plays['goal_to_go'] == 1]['touchdown'] == 1).mean()
    }

rz_breakdown = analyze_rz_breakdown(pbp, 'TEAM')
print("\nRed Zone Breakdown:")
for key, value in rz_breakdown.items():
    print(f"  {key}: {value:.3f}")

Key Findings:

  1. Passing EPA plummeted: Outside RZ pass EPA was +0.18; inside RZ it dropped to -0.05

  2. Rushing couldn't compensate: Rush EPA went from -0.02 to -0.12 in the red zone

  3. Goal line struggles: TD rate from inside the 5 was only 52% (league average: 65%)

Step 3: Understanding Why

Factor 1: Personnel Mismatch

The team's offensive identity was built on speed and explosive plays: - Multiple deep threat receivers - Athletic tight ends who excelled in space - An undersized but quick offensive line

This worked brilliantly in the open field. But in the compressed red zone: - Deep speed becomes irrelevant - Physical tight ends have an advantage - Power blocking matters more

def analyze_play_type_success(pbp: pd.DataFrame, team: str) -> pd.DataFrame:
    """Analyze success by play type in different field zones."""

    team_plays = pbp[
        (pbp['posteam'] == team) &
        (pbp['play_type'].isin(['pass', 'run'])) &
        (pbp['epa'].notna())
    ]

    results = []
    for zone in ['Outside 20', 'Red Zone (11-20)', 'Inside 10']:
        if zone == 'Outside 20':
            zone_plays = team_plays[team_plays['yardline_100'] > 20]
        elif zone == 'Red Zone (11-20)':
            zone_plays = team_plays[(team_plays['yardline_100'] > 10) &
                                     (team_plays['yardline_100'] <= 20)]
        else:
            zone_plays = team_plays[team_plays['yardline_100'] <= 10]

        for play_type in ['pass', 'run']:
            type_plays = zone_plays[zone_plays['play_type'] == play_type]
            if len(type_plays) > 0:
                results.append({
                    'zone': zone,
                    'play_type': play_type,
                    'plays': len(type_plays),
                    'epa': type_plays['epa'].mean(),
                    'success_rate': (type_plays['epa'] > 0).mean()
                })

    return pd.DataFrame(results)

play_analysis = analyze_play_type_success(pbp, 'TEAM')
print("\nPlay Type Analysis by Zone:")
print(play_analysis.to_string(index=False))

Factor 2: Scheme Limitations

The team's offensive scheme relied heavily on: - Pre-snap motion to create confusion - RPO (run-pass option) reads - Screens and quick throws to get playmakers in space

In the red zone, these concepts became less effective: - Less field width limited motion advantages - Compressed space made RPO reads harder - Screens had nowhere to go

def analyze_pass_distance(pbp: pd.DataFrame, team: str) -> dict:
    """Compare air yards by zone."""

    team_passes = pbp[
        (pbp['posteam'] == team) &
        (pbp['play_type'] == 'pass') &
        (pbp['air_yards'].notna())
    ]

    rz = team_passes[team_passes['yardline_100'] <= 20]
    outside = team_passes[team_passes['yardline_100'] > 20]

    return {
        'outside_avg_air_yards': outside['air_yards'].mean(),
        'rz_avg_air_yards': rz['air_yards'].mean(),
        'outside_short_pass_rate': (outside['air_yards'] < 5).mean(),
        'rz_short_pass_rate': (rz['air_yards'] < 5).mean()
    }

air_yards = analyze_pass_distance(pbp, 'TEAM')
print("\nPass Distance Analysis:")
print(f"Outside RZ avg air yards: {air_yards['outside_avg_air_yards']:.1f}")
print(f"Red Zone avg air yards: {air_yards['rz_avg_air_yards']:.1f}")

Factor 3: Opponent Adjustments

Defenses recognized the team's red zone weaknesses and adjusted: - Stacked the box more frequently - Played physical press coverage - Reduced cushion on outside receivers - Dared short-yardage running

def analyze_defensive_adjustments(pbp: pd.DataFrame, team: str) -> dict:
    """Examine how defenses adjusted in the red zone."""

    team_plays = pbp[
        (pbp['posteam'] == team) &
        (pbp['play_type'].isin(['pass', 'run']))
    ]

    rz = team_plays[team_plays['yardline_100'] <= 20]
    outside = team_plays[team_plays['yardline_100'] > 20]

    return {
        'rz_stacked_box_rate': rz['defenders_in_box'].mean() if 'defenders_in_box' in rz.columns else 'N/A',
        'outside_stacked_box_rate': outside['defenders_in_box'].mean() if 'defenders_in_box' in outside.columns else 'N/A',
        'rz_blitz_rate': rz.get('blitz', pd.Series()).mean() if 'blitz' in rz.columns else 'N/A'
    }

The Cost

Quantifying Lost Points

def calculate_lost_points(actual_td_rate: float,
                          expected_td_rate: float,
                          trips: int) -> dict:
    """Calculate points lost from red zone inefficiency."""

    expected_tds = trips * expected_td_rate
    actual_tds = trips * actual_td_rate
    lost_tds = expected_tds - actual_tds

    # Assume FG replaces some lost TDs
    lost_points = lost_tds * 4  # Difference between TD (7) and likely FG (3)

    return {
        'expected_tds': expected_tds,
        'actual_tds': actual_tds,
        'lost_tds': lost_tds,
        'estimated_lost_points': lost_points
    }

# Using our team's numbers
cost = calculate_lost_points(
    actual_td_rate=0.49,
    expected_td_rate=0.57,  # League average
    trips=62
)

print("\nLost Points Analysis:")
print(f"Expected TDs (at avg rate): {cost['expected_tds']:.1f}")
print(f"Actual TDs: {cost['actual_tds']:.1f}")
print(f"Lost TDs: {cost['lost_tds']:.1f}")
print(f"Estimated lost points: {cost['estimated_lost_points']:.1f}")

Result: Approximately 20 points lost over the season from below-average red zone execution.

In a league where playoff seeds and home-field advantage often hinge on tiebreakers, 20 points represents meaningful value.


The Solution

Mid-Season Adjustments

The coaching staff recognized the problem and made adjustments:

  1. Personnel changes: - Added a power-running package - Elevated a blocking tight end - Used heavier personnel groupings in red zone

  2. Scheme modifications: - More play-action from under center - Fade routes to larger receivers - Power running plays with pulling guards

  3. Philosophy shift: - Accepted that field goals weren't failures - Reduced turnovers in red zone - Prioritized efficiency over explosiveness

Results

# First 9 weeks vs Final 8 weeks
early_season = {'trips': 38, 'tds': 17, 'td_rate': 0.447}
late_season = {'trips': 24, 'tds': 13, 'td_rate': 0.542}

improvement = late_season['td_rate'] - early_season['td_rate']
print(f"\nSeason Split:")
print(f"Weeks 1-9: {early_season['td_rate']:.1%} TD rate")
print(f"Weeks 10-18: {late_season['td_rate']:.1%} TD rate")
print(f"Improvement: +{improvement:.1%}")

The adjustments worked: late-season red zone TD rate improved by nearly 10 percentage points.


Lessons Learned

1. Overall Efficiency Doesn't Guarantee Situational Success

A team can be excellent in open-field play while struggling in compressed situations. The skills that generate big plays (speed, agility, scheme complexity) differ from those that score from the 5-yard line (power, size, execution).

2. Personnel Must Match Situation

Red zone success often requires: - Physical receivers who can win contested catches - Power running backs - Blocking tight ends - An offensive line built for short-yardage

Teams built for explosive plays may need specific red zone packages.

3. Scheme Adaptation Is Critical

The same plays that work at midfield may fail near the goal line. Coaches must have distinct red zone concepts that account for: - Compressed field - Tighter coverage - More aggressive defenses

4. Red Zone Struggles Are Fixable

With proper diagnosis and adjustment, teams can improve mid-season. Key steps: - Identify specific failure points - Adjust personnel groupings - Modify scheme to address weaknesses - Accept that optimal strategies may differ by field zone


Your Turn

Exercise: Analyze a current team's red zone performance:

  1. Calculate their red zone TD rate and compare to overall EPA
  2. Break down performance by yard line (1-5, 6-10, 11-20)
  3. Compare pass vs rush efficiency in the red zone
  4. Identify potential causes of any discrepancies
  5. Suggest adjustments based on your analysis

Summary

This case study revealed that offensive excellence doesn't automatically translate to red zone efficiency. The team's speed-based identity that generated yards and explosive plays became a liability when the field compressed. Only through personnel changes, scheme modifications, and philosophical adjustments did they improve.

The key insight: situational football requires situational solutions. Teams must build distinct identities for different field zones, ensuring personnel and scheme match the specific demands of red zone, short-yardage, and goal-line situations.