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:
-
Passing EPA plummeted: Outside RZ pass EPA was +0.18; inside RZ it dropped to -0.05
-
Rushing couldn't compensate: Rush EPA went from -0.02 to -0.12 in the red zone
-
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:
-
Personnel changes: - Added a power-running package - Elevated a blocking tight end - Used heavier personnel groupings in red zone
-
Scheme modifications: - More play-action from under center - Fade routes to larger receivers - Power running plays with pulling guards
-
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:
- Calculate their red zone TD rate and compare to overall EPA
- Break down performance by yard line (1-5, 6-10, 11-20)
- Compare pass vs rush efficiency in the red zone
- Identify potential causes of any discrepancies
- 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.