Case Study 1: Identifying an Elite Pass Rusher
Overview
In this case study, you'll evaluate four defensive end prospects using advanced pass rush metrics to determine which players' production is sustainable and which might be statistical outliers. This mirrors the type of analysis NFL teams perform during draft evaluation.
The Scenario
Your team has the 15th pick in the upcoming draft and needs a pass rusher. Four edge defenders are available, each with impressive but different statistical profiles. Your task is to:
- Calculate advanced pass rush metrics for each prospect
- Identify which stats are skill-based vs. luck-based
- Project NFL success
- Recommend a draft strategy
Part 1: The Data
import pandas as pd
import numpy as np
from typing import Dict, List
# Edge rusher profiles (college statistics)
prospects = {
'Chase Dominator': {
'school': 'SEC University',
'games': 13,
'pass_rush_snaps': 412,
'pressures': 68,
'sacks': 14.5,
'hurries': 42,
'hits': 12,
'pass_rush_wins': 178,
'double_teams_faced': 145,
'double_team_wins': 52,
'run_snaps': 285,
'run_stops': 28,
'tfls': 18.5,
'avg_time_to_pressure': 2.4
},
'Marcus Speed': {
'school': 'Big Ten State',
'games': 12,
'pass_rush_snaps': 385,
'pressures': 52,
'sacks': 12.0,
'hurries': 28,
'hits': 12,
'pass_rush_wins': 165,
'double_teams_faced': 88,
'double_team_wins': 24,
'run_snaps': 310,
'run_stops': 22,
'tfls': 14.0,
'avg_time_to_pressure': 2.8
},
'Tyler Technique': {
'school': 'ACC College',
'games': 13,
'pass_rush_snaps': 398,
'pressures': 58,
'sacks': 8.5,
'hurries': 38,
'hits': 12,
'pass_rush_wins': 188,
'double_teams_faced': 162,
'double_team_wins': 68,
'run_snaps': 295,
'run_stops': 35,
'tfls': 16.0,
'avg_time_to_pressure': 2.6
},
'Devon Sack Artist': {
'school': 'Pac-12 Tech',
'games': 12,
'pass_rush_snaps': 355,
'pressures': 45,
'sacks': 15.0,
'hurries': 22,
'hits': 8,
'pass_rush_wins': 138,
'double_teams_faced': 62,
'double_team_wins': 18,
'run_snaps': 242,
'run_stops': 15,
'tfls': 11.5,
'avg_time_to_pressure': 3.1
}
}
# Calculate traditional stats
print("=" * 80)
print("TRADITIONAL STATISTICS")
print("=" * 80)
print(f"{'Prospect':<20} {'Games':>6} {'Sacks':>8} {'TFLs':>8} {'Sacks/G':>10}")
print("-" * 80)
for name, stats in prospects.items():
sacks_per_game = stats['sacks'] / stats['games']
print(f"{name:<20} {stats['games']:>6} {stats['sacks']:>8.1f} "
f"{stats['tfls']:>8.1f} {sacks_per_game:>10.2f}")
Part 2: Advanced Metrics Calculation
class EdgeRusherEvaluator:
"""Evaluate edge rushers using advanced metrics."""
def __init__(self, prospects: Dict):
self.prospects = prospects
def calculate_metrics(self, name: str) -> Dict:
"""Calculate advanced pass rush metrics."""
stats = self.prospects[name]
# Volume
pass_rush_snaps = stats['pass_rush_snaps']
# Pressure metrics
pressure_rate = stats['pressures'] / pass_rush_snaps * 100
sack_rate = stats['sacks'] / pass_rush_snaps * 100
hurry_rate = stats['hurries'] / pass_rush_snaps * 100
# Pass rush win rate
prwr = stats['pass_rush_wins'] / pass_rush_snaps * 100
# Sack conversion (sacks / pressures) - higher = more luck
sack_conversion = stats['sacks'] / stats['pressures'] * 100
# Double team metrics
double_team_rate = stats['double_teams_faced'] / pass_rush_snaps * 100
double_team_win_rate = stats['double_team_wins'] / stats['double_teams_faced'] * 100
# Run defense
run_stop_rate = stats['run_stops'] / stats['run_snaps'] * 100
# Time to pressure (lower is better)
time_to_pressure = stats['avg_time_to_pressure']
# Efficiency score
efficiency = self._calculate_efficiency(
prwr, pressure_rate, sack_conversion, double_team_win_rate
)
return {
'name': name,
'games': stats['games'],
'pass_rush_snaps': pass_rush_snaps,
'pressure_rate': round(pressure_rate, 1),
'sack_rate': round(sack_rate, 1),
'hurry_rate': round(hurry_rate, 1),
'prwr': round(prwr, 1),
'sack_conversion': round(sack_conversion, 1),
'double_team_rate': round(double_team_rate, 1),
'double_team_win_rate': round(double_team_win_rate, 1),
'run_stop_rate': round(run_stop_rate, 1),
'time_to_pressure': round(time_to_pressure, 2),
'efficiency_score': round(efficiency, 1)
}
def _calculate_efficiency(self, prwr: float, pressure_rate: float,
sack_conv: float, dt_win: float) -> float:
"""
Calculate efficiency score.
Penalizes high sack conversion (unsustainable) and
rewards PRWR, pressure rate, and double team wins.
"""
# High sack conversion is often luck - penalize it
sack_conv_penalty = max(0, (sack_conv - 20) / 10) # Penalty if > 20%
prwr_score = min(100, prwr / 55 * 100)
pressure_score = min(100, pressure_rate / 20 * 100)
dt_score = min(100, dt_win / 45 * 100)
base_score = prwr_score * 0.40 + pressure_score * 0.35 + dt_score * 0.25
adjusted = base_score - sack_conv_penalty * 5
return max(0, min(100, adjusted))
def compare_all(self) -> pd.DataFrame:
"""Compare all prospects."""
results = []
for name in self.prospects:
metrics = self.calculate_metrics(name)
results.append({
'prospect': name,
'prwr': metrics['prwr'],
'pressure%': metrics['pressure_rate'],
'sack%': metrics['sack_rate'],
'sack_conv': metrics['sack_conversion'],
'dt_win%': metrics['double_team_win_rate'],
'time_to_p': metrics['time_to_pressure'],
'efficiency': metrics['efficiency_score']
})
return pd.DataFrame(results)
# Evaluate prospects
evaluator = EdgeRusherEvaluator(prospects)
print("\n" + "=" * 80)
print("ADVANCED PASS RUSH METRICS")
print("=" * 80)
comparison = evaluator.compare_all()
print(comparison.to_string(index=False))
Part 3: Sustainability Analysis
def analyze_sustainability(evaluator: EdgeRusherEvaluator) -> Dict:
"""
Analyze which production is sustainable.
Key indicators of sustainable production:
- High PRWR (skill)
- Low sack conversion rate (20% is normal, 30%+ is lucky)
- High pressure rate
- Consistent time to pressure
"""
results = {}
for name in evaluator.prospects:
metrics = evaluator.calculate_metrics(name)
# Sustainability factors
factors = {
'prwr_sustainable': metrics['prwr'] >= 42,
'pressure_sustainable': metrics['pressure_rate'] >= 12,
'sack_conv_sustainable': metrics['sack_conversion'] <= 25,
'time_sustainable': metrics['time_to_pressure'] <= 2.8
}
sustainable_count = sum(factors.values())
# Project regression/progression
if metrics['sack_conversion'] > 28:
projection = 'REGRESSION likely (sack rate unsustainable)'
elif metrics['sack_conversion'] < 18 and metrics['pressure_rate'] > 14:
projection = 'PROGRESSION likely (underconverting pressures)'
else:
projection = 'STABLE production expected'
results[name] = {
'sustainability_score': sustainable_count,
'factors': factors,
'projection': projection,
'red_flags': self._identify_red_flags(metrics)
}
return results
def _identify_red_flags(metrics: Dict) -> List[str]:
"""Identify concerning factors."""
red_flags = []
if metrics['sack_conversion'] > 30:
red_flags.append(f"Very high sack conversion ({metrics['sack_conversion']}%) - regression likely")
if metrics['double_team_rate'] < 25:
red_flags.append(f"Rarely double teamed ({metrics['double_team_rate']}%) - production may decline vs better tackles")
if metrics['time_to_pressure'] > 3.0:
red_flags.append(f"Slow time to pressure ({metrics['time_to_pressure']}s) - may not translate to NFL")
if metrics['run_stop_rate'] < 6:
red_flags.append(f"Poor run defense ({metrics['run_stop_rate']}%) - one-dimensional")
return red_flags
# Analyze sustainability
print("\n" + "=" * 80)
print("SUSTAINABILITY ANALYSIS")
print("=" * 80)
sustainability = analyze_sustainability(evaluator)
for name, analysis in sustainability.items():
metrics = evaluator.calculate_metrics(name)
print(f"\n{name}:")
print(f" Sustainability Score: {analysis['sustainability_score']}/4")
print(f" Projection: {analysis['projection']}")
if analysis['red_flags']:
print(" Red Flags:")
for flag in analysis['red_flags']:
print(f" - {flag}")
else:
print(" Red Flags: None")
Part 4: Scouting Reports
def generate_scouting_report(evaluator: EdgeRusherEvaluator, name: str) -> str:
"""Generate detailed scouting report."""
metrics = evaluator.calculate_metrics(name)
stats = evaluator.prospects[name]
# Identify strengths
strengths = []
if metrics['prwr'] >= 45:
strengths.append(f"Elite pass rush win rate ({metrics['prwr']}%)")
if metrics['pressure_rate'] >= 15:
strengths.append(f"High-volume pressure generator ({metrics['pressure_rate']}%)")
if metrics['double_team_win_rate'] >= 40:
strengths.append(f"Wins against double teams ({metrics['double_team_win_rate']}%)")
if metrics['time_to_pressure'] <= 2.5:
strengths.append(f"Quick first step ({metrics['time_to_pressure']}s)")
if metrics['run_stop_rate'] >= 10:
strengths.append(f"Strong run defender ({metrics['run_stop_rate']}%)")
# Identify concerns
concerns = []
if metrics['sack_conversion'] > 28:
concerns.append(f"Inflated sack numbers ({metrics['sack_conversion']}% conversion)")
if metrics['double_team_rate'] < 25:
concerns.append(f"Not respected (only {metrics['double_team_rate']}% double teams)")
if metrics['prwr'] < 40:
concerns.append(f"Below average win rate ({metrics['prwr']}%)")
if metrics['run_stop_rate'] < 7:
concerns.append(f"Limited run defense ({metrics['run_stop_rate']}%)")
# NFL projection
if metrics['efficiency_score'] >= 75 and len(concerns) == 0:
nfl_projection = "Pro Bowl caliber - likely Day 1 impact"
elif metrics['efficiency_score'] >= 65:
nfl_projection = "Quality starter - should contribute Year 1"
elif metrics['efficiency_score'] >= 55:
nfl_projection = "Rotational player - development needed"
else:
nfl_projection = "Project - significant development required"
report = f"""
╔══════════════════════════════════════════════════════════════════════════════╗
║ SCOUTING REPORT: {name:<30} ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ {stats['school']:<76} ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ PRODUCTION ║
║ Games: {stats['games']:<5} Sacks: {stats['sacks']:<6} TFLs: {stats['tfls']:<6} ║
║ Pass Rush Snaps: {stats['pass_rush_snaps']:<5} Pressures: {stats['pressures']:<5} ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ ADVANCED METRICS ║
║ PRWR: {metrics['prwr']}% Pressure Rate: {metrics['pressure_rate']}% Sack Rate: {metrics['sack_rate']}% ║
║ Sack Conversion: {metrics['sack_conversion']}% Time to Pressure: {metrics['time_to_pressure']}s ║
║ Double Team Win Rate: {metrics['double_team_win_rate']}% ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ EFFICIENCY SCORE: {metrics['efficiency_score']}/100 ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ STRENGTHS ║"""
for s in strengths[:4]:
report += f"\n║ • {s:<72} ║"
if not strengths:
report += "\n║ • No elite traits identified ║"
report += """
╠══════════════════════════════════════════════════════════════════════════════╣
║ CONCERNS ║"""
for c in concerns[:4]:
report += f"\n║ • {c:<72} ║"
if not concerns:
report += "\n║ • No significant concerns ║"
report += f"""
╠══════════════════════════════════════════════════════════════════════════════╣
║ NFL PROJECTION ║
║ {nfl_projection:<74} ║
╚══════════════════════════════════════════════════════════════════════════════╝"""
return report
# Generate reports
for name in prospects:
print(generate_scouting_report(evaluator, name))
Part 5: Final Rankings and Recommendation
def create_draft_rankings(evaluator: EdgeRusherEvaluator) -> pd.DataFrame:
"""Create final draft rankings."""
results = []
for name in evaluator.prospects:
m = evaluator.calculate_metrics(name)
# Composite with sustainability adjustments
base_score = m['efficiency_score']
# Penalty for unsustainable sack conversion
if m['sack_conversion'] > 28:
base_score -= 10
elif m['sack_conversion'] > 25:
base_score -= 5
# Bonus for double team resistance
if m['double_team_rate'] > 35:
base_score += 5
# Run defense bonus
if m['run_stop_rate'] >= 10:
base_score += 3
results.append({
'prospect': name,
'sacks': evaluator.prospects[name]['sacks'],
'prwr': m['prwr'],
'pressure%': m['pressure_rate'],
'adjusted_score': round(base_score, 1)
})
df = pd.DataFrame(results)
df['rank'] = df['adjusted_score'].rank(ascending=False).astype(int)
return df.sort_values('rank')
rankings = create_draft_rankings(evaluator)
print("\n" + "=" * 80)
print("FINAL DRAFT RANKINGS")
print("=" * 80)
print(rankings.to_string(index=False))
print("""
DRAFT RECOMMENDATION:
=====================
1. TYLER TECHNIQUE (Rank 1)
- Highest PRWR despite facing most double teams
- Low sack conversion = room for MORE sacks
- Complete player (run defense + pass rush)
- RECOMMENDATION: Target at #15 if available
2. CHASE DOMINATOR (Rank 2)
- Elite pressure production
- Quick first step (2.4s time to pressure)
- Minor concern: somewhat high sack conversion
- RECOMMENDATION: Excellent pick at #15
3. MARCUS SPEED (Rank 3)
- Solid all-around player
- Doesn't face many double teams (concern)
- Production may decline vs. NFL tackles
- RECOMMENDATION: Consider if top 2 unavailable
4. DEVON SACK ARTIST (Rank 4)
- SACK TOTAL IS MISLEADING
- Highest sack conversion = unsustainable luck
- Slow time to pressure won't translate
- Rarely double teamed = not respected
- RECOMMENDATION: AVOID - production will regress
""")
Summary
This case study demonstrated how to evaluate pass rushers beyond sack totals:
- Pass Rush Win Rate (PRWR): Most stable predictor of future success
- Sack Conversion Rate: High rates indicate luck, expect regression
- Double Team Rate: How opponents respect the rusher
- Time to Pressure: Speed of winning affects NFL translation
- Run Defense: Complete players are more valuable
Key Finding:
Devon Sack Artist's 15 sacks are misleading—his 33% sack conversion rate is unsustainable. Tyler Technique, despite fewer sacks, projects better due to elite PRWR and sustainable production profile.