Case Study 1: Evaluating Running Back Draft Prospects
Overview
In this case study, you'll use advanced rushing metrics to evaluate four running back prospects for the NFL draft. Each back has different strengths and weaknesses that traditional stats may not reveal.
The Scenario
You're an analytics assistant for an NFL team preparing for the draft. The team has identified four running backs of interest and wants a data-driven evaluation. Your task is to:
- Calculate advanced metrics for each prospect
- Identify strengths and weaknesses
- Project which skills translate to the NFL
- Recommend a draft strategy
Part 1: The Data
import pandas as pd
import numpy as np
from typing import Dict, List
# Prospect profiles (college statistics)
prospects = {
'Marcus Power': {
'school': 'SEC University',
'games': 13,
'carries': 245,
'yards': 1,356,
'touchdowns': 14,
'yards_before_contact': 780,
'broken_tackles': 58,
'explosive_runs_10': 28,
'explosive_runs_20': 8,
'stuffed_runs': 32,
'third_short_att': 28,
'third_short_conv': 22,
'first_down_carries': 95,
'first_down_success': 48,
'avg_defenders_in_box': 7.2
},
'Tyler Speed': {
'school': 'Pac-12 College',
'games': 12,
'carries': 198,
'yards': 1,245,
'touchdowns': 11,
'yards_before_contact': 845,
'broken_tackles': 32,
'explosive_runs_10': 35,
'explosive_runs_20': 12,
'stuffed_runs': 45,
'third_short_att': 18,
'third_short_conv': 11,
'first_down_carries': 82,
'first_down_success': 42,
'avg_defenders_in_box': 6.8
},
'Jordan Workhorse': {
'school': 'Big Ten State',
'games': 14,
'carries': 312,
'yards': 1,489,
'touchdowns': 16,
'yards_before_contact': 1,050,
'broken_tackles': 45,
'explosive_runs_10': 24,
'explosive_runs_20': 5,
'stuffed_runs': 52,
'third_short_att': 35,
'third_short_conv': 26,
'first_down_carries': 128,
'first_down_success': 58,
'avg_defenders_in_box': 7.5
},
'Devon Versatile': {
'school': 'ACC Tech',
'games': 13,
'carries': 178,
'yards': 945,
'touchdowns': 9,
'yards_before_contact': 425,
'broken_tackles': 48,
'explosive_runs_10': 22,
'explosive_runs_20': 7,
'stuffed_runs': 25,
'third_short_att': 15,
'third_short_conv': 12,
'first_down_carries': 68,
'first_down_success': 38,
'receiving_targets': 52,
'receptions': 45,
'receiving_yards': 385,
'avg_defenders_in_box': 6.5
}
}
# Calculate traditional stats
print("=" * 80)
print("TRADITIONAL STATISTICS")
print("=" * 80)
print(f"{'Prospect':<20} {'Carries':>8} {'Yards':>8} {'YPC':>6} {'TDs':>5}")
print("-" * 80)
for name, stats in prospects.items():
ypc = stats['yards'] / stats['carries']
print(f"{name:<20} {stats['carries']:>8} {stats['yards']:>8} {ypc:>6.2f} {stats['touchdowns']:>5}")
Part 2: Advanced Metrics Calculation
class ProspectEvaluator:
"""Evaluate RB prospects using advanced metrics."""
def __init__(self, prospects: Dict):
self.prospects = prospects
def calculate_metrics(self, name: str) -> Dict:
"""Calculate all advanced metrics for a prospect."""
stats = self.prospects[name]
# Volume and traditional
ypc = stats['yards'] / stats['carries']
ypg = stats['yards'] / stats['games']
# Yards after contact
total_yac = stats['yards'] - stats['yards_before_contact']
avg_ybc = stats['yards_before_contact'] / stats['carries']
avg_yac = total_yac / stats['carries']
yac_share = total_yac / stats['yards'] * 100
# Broken tackles
bt_per_carry = stats['broken_tackles'] / stats['carries']
# Explosive plays
explosive_rate = stats['explosive_runs_10'] / stats['carries'] * 100
big_play_rate = stats['explosive_runs_20'] / stats['carries'] * 100
# Stuffs
stuff_rate = stats['stuffed_runs'] / stats['carries'] * 100
# Success rate (1st down: 40% threshold)
first_down_success_rate = stats['first_down_success'] / stats['first_down_carries'] * 100
# Short yardage
short_conv_rate = stats['third_short_conv'] / stats['third_short_att'] * 100
# RYOE estimate (simplified: compare to expected based on box count)
# Expected = 4.2 - 0.35 * (box - 7)
expected_ypc = 4.2 - 0.35 * (stats['avg_defenders_in_box'] - 7)
ryoe_per_carry = ypc - expected_ypc
# Receiving (if available)
receiving = {}
if 'receptions' in stats:
receiving = {
'targets': stats['receiving_targets'],
'receptions': stats['receptions'],
'catch_rate': stats['receptions'] / stats['receiving_targets'] * 100,
'receiving_yards': stats['receiving_yards'],
'ypr': stats['receiving_yards'] / stats['receptions']
}
return {
'name': name,
'games': stats['games'],
'carries': stats['carries'],
'yards': stats['yards'],
'ypc': round(ypc, 2),
'ypg': round(ypg, 1),
'touchdowns': stats['touchdowns'],
'avg_ybc': round(avg_ybc, 2),
'avg_yac': round(avg_yac, 2),
'yac_share': round(yac_share, 1),
'bt_per_carry': round(bt_per_carry, 3),
'explosive_rate': round(explosive_rate, 1),
'big_play_rate': round(big_play_rate, 1),
'stuff_rate': round(stuff_rate, 1),
'first_down_success': round(first_down_success_rate, 1),
'short_yardage_conv': round(short_conv_rate, 1),
'expected_ypc': round(expected_ypc, 2),
'ryoe_per_carry': round(ryoe_per_carry, 2),
'receiving': receiving
}
def compare_all(self) -> pd.DataFrame:
"""Compare all prospects."""
results = []
for name in self.prospects:
metrics = self.calculate_metrics(name)
results.append({
'prospect': name,
'ypc': metrics['ypc'],
'avg_yac': metrics['avg_yac'],
'bt/carry': metrics['bt_per_carry'],
'stuff%': metrics['stuff_rate'],
'explosive%': metrics['explosive_rate'],
'success%': metrics['first_down_success'],
'short_yd%': metrics['short_yardage_conv'],
'ryoe': metrics['ryoe_per_carry']
})
return pd.DataFrame(results)
# Evaluate prospects
evaluator = ProspectEvaluator(prospects)
print("\n" + "=" * 80)
print("ADVANCED METRICS COMPARISON")
print("=" * 80)
comparison = evaluator.compare_all()
print(comparison.to_string(index=False))
Part 3: Individual Scouting Reports
def generate_scouting_report(evaluator: ProspectEvaluator, name: str) -> str:
"""Generate detailed scouting report."""
metrics = evaluator.calculate_metrics(name)
# Identify strengths (top 25% thresholds)
strengths = []
if metrics['avg_yac'] > 2.5:
strengths.append(f"Elite yards after contact ({metrics['avg_yac']} avg)")
if metrics['bt_per_carry'] > 0.20:
strengths.append(f"Excellent tackle breaker ({metrics['bt_per_carry']:.3f} BT/carry)")
if metrics['explosive_rate'] > 15:
strengths.append(f"Big play threat ({metrics['explosive_rate']}% explosive rate)")
if metrics['short_yardage_conv'] > 75:
strengths.append(f"Reliable short yardage ({metrics['short_yardage_conv']}% conv)")
if metrics['stuff_rate'] < 15:
strengths.append(f"Rarely stuffed ({metrics['stuff_rate']}% stuff rate)")
if metrics['ryoe_per_carry'] > 0.5:
strengths.append(f"Outperforms expectation ({metrics['ryoe_per_carry']:+.2f} RYOE)")
if metrics['receiving'] and metrics['receiving']['catch_rate'] > 80:
strengths.append(f"Strong receiving threat ({metrics['receiving']['catch_rate']:.0f}% catch rate)")
# Identify concerns
concerns = []
if metrics['avg_yac'] < 2.0:
concerns.append(f"Below average YAC ({metrics['avg_yac']})")
if metrics['stuff_rate'] > 20:
concerns.append(f"High stuff rate ({metrics['stuff_rate']}%)")
if metrics['explosive_rate'] < 10:
concerns.append(f"Limited explosive plays ({metrics['explosive_rate']}%)")
if metrics['short_yardage_conv'] < 60:
concerns.append(f"Short yardage struggles ({metrics['short_yardage_conv']}%)")
if metrics['ryoe_per_carry'] < 0:
concerns.append(f"Below expected production ({metrics['ryoe_per_carry']:+.2f} RYOE)")
# Context
context = []
if metrics['avg_ybc'] > 3.5:
context.append("Ran behind excellent blocking")
elif metrics['avg_ybc'] < 2.5:
context.append("Ran behind below-average blocking")
if evaluator.prospects[name]['avg_defenders_in_box'] > 7.3:
context.append("Faced loaded boxes frequently")
elif evaluator.prospects[name]['avg_defenders_in_box'] < 6.8:
context.append("Often faced light boxes")
report = f"""
╔══════════════════════════════════════════════════════════════════════════╗
║ SCOUTING REPORT: {name:<25} ║
╠══════════════════════════════════════════════════════════════════════════╣
║ {evaluator.prospects[name]['school']:<72} ║
╠══════════════════════════════════════════════════════════════════════════╣
║ PRODUCTION ║
║ Carries: {metrics['carries']:<6} Yards: {metrics['yards']:<6} TDs: {metrics['touchdowns']:<4} ║
║ YPC: {metrics['ypc']:<6} YPG: {metrics['ypg']:<8} ║
╠══════════════════════════════════════════════════════════════════════════╣
║ ADVANCED METRICS ║
║ Avg YBC: {metrics['avg_ybc']:<6} Avg YAC: {metrics['avg_yac']:<6} YAC Share: {metrics['yac_share']}% ║
║ BT/Carry: {metrics['bt_per_carry']:<8} Explosive: {metrics['explosive_rate']}% Stuff: {metrics['stuff_rate']}% ║
║ RYOE/Carry: {metrics['ryoe_per_carry']:+.2f} ║
╠══════════════════════════════════════════════════════════════════════════╣
║ SITUATIONAL ║
║ 1st Down Success: {metrics['first_down_success']}% Short Yardage Conv: {metrics['short_yardage_conv']}% ║
╠══════════════════════════════════════════════════════════════════════════╣
║ STRENGTHS ║"""
for s in strengths[:4]:
report += f"\n║ • {s:<68} ║"
if not strengths:
report += "\n║ • No elite traits identified ║"
report += """
╠══════════════════════════════════════════════════════════════════════════╣
║ CONCERNS ║"""
for c in concerns[:4]:
report += f"\n║ • {c:<68} ║"
if not concerns:
report += "\n║ • No significant concerns ║"
report += """
╠══════════════════════════════════════════════════════════════════════════╣
║ CONTEXT ║"""
for ctx in context:
report += f"\n║ • {ctx:<68} ║"
report += "\n╚══════════════════════════════════════════════════════════════════════════╝"
return report
# Generate reports for all prospects
for name in prospects:
print(generate_scouting_report(evaluator, name))
Part 4: Final Rankings and Recommendations
def create_draft_rankings(evaluator: ProspectEvaluator) -> pd.DataFrame:
"""Create final draft rankings with composite scores."""
results = []
for name in evaluator.prospects:
m = evaluator.calculate_metrics(name)
# Composite score (0-100)
# Weights: YAC (25%), RYOE (20%), Success (15%), Short Yardage (15%),
# Explosive (15%), Low Stuff (10%)
# Normalize each metric to 0-100 scale
yac_score = min(100, m['avg_yac'] / 3.0 * 100)
ryoe_score = min(100, max(0, (m['ryoe_per_carry'] + 1) / 2 * 100))
success_score = min(100, m['first_down_success'] / 60 * 100)
short_score = min(100, m['short_yardage_conv'] / 90 * 100)
explosive_score = min(100, m['explosive_rate'] / 20 * 100)
stuff_score = min(100, max(0, (25 - m['stuff_rate']) / 25 * 100))
composite = (
yac_score * 0.25 +
ryoe_score * 0.20 +
success_score * 0.15 +
short_score * 0.15 +
explosive_score * 0.15 +
stuff_score * 0.10
)
# Add receiving bonus if applicable
if m['receiving']:
receiving_bonus = min(10, m['receiving']['receptions'] / 50 * 10)
composite += receiving_bonus
results.append({
'prospect': name,
'ypc': m['ypc'],
'yac_score': round(yac_score, 1),
'ryoe_score': round(ryoe_score, 1),
'composite': round(composite, 1)
})
df = pd.DataFrame(results)
df['rank'] = df['composite'].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 RECOMMENDATIONS:
=======================
1. MARCUS POWER (Rank 1)
- Best pure runner: Elite YAC, breaks tackles, reliable in short yardage
- Projects as early-down workhorse with goal-line value
- Day 2 pick (Rounds 2-3)
2. DEVON VERSATILE (Rank 2)
- Three-down back potential: Strong receiving skills
- Creates yards despite poor blocking (high YAC share)
- Scheme versatile, fits modern NFL offense
- Day 2 pick (Round 2-3)
3. TYLER SPEED (Rank 3)
- Home run threat: Excellent explosive play rate
- Benefited from lighter boxes and good blocking
- Boom-or-bust profile (high stuff rate)
- Day 3 pick (Rounds 4-5) - change-of-pace role
4. JORDAN WORKHORSE (Rank 4)
- Volume producer behind great blocking
- Limited ability to create on his own
- Reliable but not dynamic
- Day 3 pick (Round 5-6) or UDFA
""")
Summary
This case study demonstrated how advanced metrics reveal different prospect profiles:
- Marcus Power: Traditional bellcow with elite skill metrics
- Devon Versatile: Modern three-down back with receiving upside
- Tyler Speed: Explosive but inconsistent, benefited from scheme
- Jordan Workhorse: System product with limited upside
Key Evaluation Principles:
- YAC and broken tackles reveal portable skills
- RYOE adjusts for blocking and scheme
- Situational success (short yardage) shows reliability
- Context (box count, blocking) affects raw production