Quick Reference Card
Counting Statistics vs. Rate Statistics
| Type |
Purpose |
Examples |
When to Use |
| Counting |
Volume measurement |
Yards, TDs, tackles |
Comparing players with similar opportunities |
| Rate |
Efficiency measurement |
YPC, Comp%, TD% |
Comparing across different opportunity levels |
| Per-Game |
Normalized volume |
YPG, PPG |
Comparing across different game counts |
Passing Statistics
Completion % = (Completions / Attempts) × 100
Yards per Attempt = Passing Yards / Attempts
TD % = (Passing TDs / Attempts) × 100
INT % = (Interceptions / Attempts) × 100
TD-to-INT Ratio = Touchdowns / Interceptions
def passer_rating(comp_pct, ypa, td_pct, int_pct):
"""
Calculate NFL-style passer rating.
Parameters are percentages (e.g., 65.0 for 65%)
except ypa which is raw yards per attempt.
"""
a = max(0, min(((comp_pct - 30) / 20), 2.375))
b = max(0, min(((ypa - 3) / 4), 2.375))
c = max(0, min((td_pct / 5), 2.375))
d = max(0, min((2.375 - (int_pct / 4)), 2.375))
return ((a + b + c + d) / 6) * 100
Rushing Statistics
Yards per Carry = Rushing Yards / Carries
Yards per Game = Total Yards / Games Played
Yards After Contact = Total YAC / Carries
Receiving Statistics
Catch Rate = Receptions / Targets × 100
Yards per Reception = Receiving Yards / Receptions
Yards per Target = Receiving Yards / Targets
Statistical Benchmarks (FBS)
Quarterback Benchmarks
| Metric |
Poor |
Average |
Good |
Elite |
| Completion % |
<55% |
55-62% |
62-68% |
>68% |
| Yards/Attempt |
<6.0 |
6.0-7.5 |
7.5-9.0 |
>9.0 |
| TD % |
<3% |
3-5% |
5-7% |
>7% |
| INT % |
>4% |
2.5-4% |
1.5-2.5% |
<1.5% |
| Passer Rating |
<100 |
100-130 |
130-160 |
>160 |
Running Back Benchmarks
| Metric |
Poor |
Average |
Good |
Elite |
| Yards/Carry |
<3.5 |
3.5-4.5 |
4.5-5.5 |
>5.5 |
| Yards/Game |
<50 |
50-80 |
80-110 |
>110 |
| 10+ Yard Runs % |
<8% |
8-12% |
12-16% |
>16% |
Receiver Benchmarks
| Metric |
Poor |
Average |
Good |
Elite |
| Catch Rate |
<55% |
55-65% |
65-72% |
>72% |
| Yards/Reception |
<10 |
10-13 |
13-16 |
>16 |
| Yards/Target |
<6.0 |
6.0-8.0 |
8.0-10.0 |
>10.0 |
Team Statistics Reference
Offensive Benchmarks
| Metric |
Poor |
Average |
Good |
Elite |
| Points/Game |
<20 |
20-30 |
30-40 |
>40 |
| Yards/Play |
<5.0 |
5.0-6.0 |
6.0-7.0 |
>7.0 |
| 3rd Down % |
<35% |
35-42% |
42-50% |
>50% |
| Red Zone TD % |
<50% |
50-60% |
60-70% |
>70% |
Defensive Benchmarks
| Metric |
Elite |
Good |
Average |
Poor |
| Points Allowed/Game |
<17 |
17-24 |
24-30 |
>30 |
| Yards/Play Allowed |
<4.5 |
4.5-5.5 |
5.5-6.5 |
>6.5 |
| 3rd Down Stop % |
>65% |
58-65% |
50-58% |
<50% |
| Turnovers Forced/Game |
>2.0 |
1.5-2.0 |
1.0-1.5 |
<1.0 |
Common Calculations in Python
Basic Stats Calculator
class FootballStatsCalculator:
"""Quick reference for common calculations."""
@staticmethod
def passing_efficiency(comp, att, yards, tds, ints):
"""Return dictionary of passing efficiency metrics."""
return {
'comp_pct': comp / att * 100,
'ypa': yards / att,
'td_pct': tds / att * 100,
'int_pct': ints / att * 100,
'td_int_ratio': tds / max(ints, 1),
'net_ypa': (yards - 45 * ints) / att # Adjusted for INTs
}
@staticmethod
def rushing_efficiency(yards, carries, games):
"""Return dictionary of rushing efficiency metrics."""
return {
'ypc': yards / carries,
'ypg': yards / games,
'carries_pg': carries / games
}
@staticmethod
def receiving_efficiency(rec, targets, yards, games):
"""Return dictionary of receiving efficiency metrics."""
return {
'catch_rate': rec / targets * 100,
'ypr': yards / rec,
'ypt': yards / targets,
'rpg': rec / games,
'ypg': yards / games
}
Team Metrics Calculator
def calculate_team_metrics(team_data):
"""Calculate comprehensive team metrics."""
# Offensive efficiency
total_yards = team_data['pass_yards'] + team_data['rush_yards']
total_plays = team_data['pass_att'] + team_data['rush_att']
metrics = {
'yards_per_play': total_yards / total_plays,
'points_per_game': team_data['points'] / team_data['games'],
'third_down_pct': team_data['third_conv'] / team_data['third_att'] * 100,
'red_zone_td_pct': team_data['rz_td'] / team_data['rz_att'] * 100,
'turnover_margin': team_data['takeaways'] - team_data['giveaways']
}
return metrics
Analysis Decision Tree
Comparing Players/Teams?
├── Same number of games/opportunities?
│ ├── Yes → Counting stats are valid
│ └── No → Use rate stats or per-game stats
│
├── Different eras (years apart)?
│ ├── Yes → Apply era adjustment
│ └── No → Direct comparison valid
│
├── Different conferences?
│ ├── Yes → Consider strength of schedule
│ └── No → Direct comparison more valid
│
└── Different roles/usage?
├── Yes → Focus on efficiency metrics
└── No → Volume comparisons valid
Key Concepts to Remember
1. Context Matters
- A 300-yard passing game in a blowout loss (garbage time) differs from 300 yards in a close win
- High volume often comes with decreased efficiency
- Opponent quality affects all statistics
2. Rate vs. Volume Trade-offs
- High-volume players may have lower efficiency
- Limited sample sizes make rate stats unreliable
- Per-game stats balance volume with opportunity
3. Composite Metrics
- Single metrics capture one dimension
- Combine multiple metrics for complete picture
- Weight metrics based on analysis goals
4. Era Adjustment
- Passing statistics have inflated over time
- Compare to era averages, not absolute benchmarks
- Modern offenses produce more yards but also more plays
5. Limitations of Traditional Stats
- Don't account for opponent quality
- Miss play-by-play context
- Can't measure what didn't happen (throwaways, etc.)
- Depend on official scorer decisions
Quick Pandas Operations
import pandas as pd
# Calculate multiple rate stats at once
df['comp_pct'] = df['completions'] / df['attempts'] * 100
df['ypa'] = df['yards'] / df['attempts']
df['td_pct'] = df['touchdowns'] / df['attempts'] * 100
# Rank players across multiple categories
for col in ['yards', 'touchdowns', 'comp_pct', 'ypa']:
df[f'{col}_rank'] = df[col].rank(ascending=False)
# Create composite ranking
rank_cols = [c for c in df.columns if c.endswith('_rank')]
df['composite_rank'] = df[rank_cols].mean(axis=1)
df['final_rank'] = df['composite_rank'].rank()
# Filter by minimum threshold
qualified = df[df['attempts'] >= 100]
# Group by team and aggregate
team_stats = df.groupby('team').agg({
'yards': 'sum',
'touchdowns': 'sum',
'attempts': 'sum'
})
team_stats['team_ypa'] = team_stats['yards'] / team_stats['attempts']
Common Mistakes to Avoid
- Comparing without context: Always note games played, opportunities, opponent quality
- Ignoring minimum thresholds: Small samples produce misleading rates
- Treating all yards equally: A 50-yard TD differs from 50 checkdown yards
- Overlooking era effects: Statistics from different years need adjustment
- Single-metric evaluation: Use multiple metrics for complete assessment
- Confusing correlation with causation: High yards don't cause wins
Chapter Summary
Traditional football statistics provide the foundation for understanding player and team performance. While limited in contextual awareness, they remain essential for:
- Initial player evaluation and comparison
- Historical analysis and record tracking
- Box score analysis and game summaries
- Building blocks for advanced metrics
Master these fundamentals before moving to advanced metrics like EPA and win probability.