The evolution of basketball analytics has transformed how we evaluate player performance. While traditional counting statistics like points, rebounds, and assists provide a foundation for understanding individual contributions, they fail to capture...
In This Chapter
- Introduction
- 9.1 Player Efficiency Rating (PER)
- 9.2 Game Score
- 9.3 Player Impact Estimate (PIE)
- 9.4 Usage Rate (USG%)
- 9.5 Assist Rate and Assist Percentage
- 9.6 Rebounding Percentages
- 9.7 Steal Percentage (STL%)
- 9.8 Block Percentage (BLK%)
- 9.9 Turnover Percentage (TOV%)
- 9.10 Critiques and Limitations of Composite Metrics
- 9.11 Best Practices for Using Advanced Box Score Metrics
- 9.12 Python Implementations
- 9.13 Summary
- References
Chapter 9: Advanced Box Score Metrics
Introduction
The evolution of basketball analytics has transformed how we evaluate player performance. While traditional counting statistics like points, rebounds, and assists provide a foundation for understanding individual contributions, they fail to capture the complete picture of a player's impact. Advanced box score metrics emerged to address this limitation, combining multiple statistical categories into single composite measures that attempt to quantify overall player value.
This chapter explores the most influential advanced box score metrics in basketball analytics. We will examine their mathematical foundations, understand their intended purposes, learn how to calculate them, and critically evaluate their strengths and limitations. By the end of this chapter, you will possess both the theoretical knowledge and practical programming skills to compute, interpret, and appropriately apply these metrics in your own analyses.
The metrics covered in this chapter share a common characteristic: they are all derived entirely from traditional box score statistics. This distinguishes them from more sophisticated approaches that incorporate spatial data, lineup information, or play-by-play sequences. While this reliance on box score data creates inherent limitations, it also means these metrics can be calculated for any game or season for which traditional statistics are available, including historical data stretching back decades.
9.1 Player Efficiency Rating (PER)
9.1.1 Historical Context and Purpose
Player Efficiency Rating (PER) was developed by John Hollinger in the early 2000s and popularized through his work at ESPN.com and in his book Pro Basketball Forecast. PER represented one of the first serious attempts to create a single number that captured a player's overall statistical contribution.
The metric was designed with several goals in mind:
- Comprehensiveness: Incorporate all major box score statistics into a single value
- Rate-based: Express performance on a per-minute basis to facilitate comparisons
- League-adjusted: Normalize to a league average of 15.00 to account for era differences
- Pace-independent: Account for team pace to allow fair comparisons across different playing styles
9.1.2 The Complete PER Formula
PER's calculation is notoriously complex, involving numerous coefficients that Hollinger derived through a combination of statistical analysis and basketball intuition. The complete formula proceeds through several stages.
Stage 1: Calculate League Factors
Before computing individual PER values, we must establish several league-wide factors:
$$\text{factor} = \frac{2}{3} - \frac{(0.5 \times \text{lg\_AST} / \text{lg\_FG})}{(2 \times \text{lg\_FG} / \text{lg\_FT})}$$
$$\text{VOP} = \frac{\text{lg\_PTS}}{\text{lg\_FGA} - \text{lg\_ORB} + \text{lg\_TOV} + 0.44 \times \text{lg\_FTA}}$$
$$\text{DRB\%} = \frac{\text{lg\_TRB} - \text{lg\_ORB}}{\text{lg\_TRB}}$$
Where:
- lg_AST = League total assists
- lg_FG = League total field goals made
- lg_FT = League total free throws made
- lg_PTS = League total points
- lg_FGA = League total field goal attempts
- lg_ORB = League total offensive rebounds
- lg_TOV = League total turnovers
- lg_FTA = League total free throw attempts
- lg_TRB = League total rebounds
The factor represents an adjustment for the relationship between assists, field goals, and free throws at the league level. VOP (Value of Possession) quantifies the average point value generated per possession. DRB% represents the proportion of total rebounds that are defensive rebounds.
Stage 2: Calculate Unadjusted PER (uPER)
The core of PER is the unadjusted PER calculation, which combines individual statistics with the league factors:
$$\text{uPER} = \frac{1}{\text{MIN}} \times \Bigg($$
$$\text{3P} + \frac{2}{3} \times \text{AST}$$
$$+ (2 - \text{factor} \times \frac{\text{tm\_AST}}{\text{tm\_FG}}) \times \text{FG}$$
$$+ (\text{FT} \times 0.5 \times (1 + (1 - \frac{\text{tm\_AST}}{\text{tm\_FG}}) + \frac{2}{3} \times \frac{\text{tm\_AST}}{\text{tm\_FG}}))$$
$$- \text{VOP} \times \text{TOV}$$
$$- \text{VOP} \times \text{DRB\%} \times (\text{FGA} - \text{FG})$$
$$- \text{VOP} \times 0.44 \times (0.44 + (0.56 \times \text{DRB\%})) \times (\text{FTA} - \text{FT})$$
$$+ \text{VOP} \times (1 - \text{DRB\%}) \times (\text{TRB} - \text{ORB})$$
$$+ \text{VOP} \times \text{DRB\%} \times \text{ORB}$$
$$+ \text{VOP} \times \text{STL}$$
$$+ \text{VOP} \times \text{DRB\%} \times \text{BLK}$$
$$- \text{PF} \times \left(\frac{\text{lg\_FT}}{\text{lg\_PF}} - 0.44 \times \frac{\text{lg\_FTA}}{\text{lg\_PF}} \times \text{VOP}\right)$$
$$\Bigg)$$
Where individual player statistics are:
- MIN = Minutes played
- 3P = Three-point field goals made
- AST = Assists
- FG = Field goals made
- FT = Free throws made
- TOV = Turnovers
- FGA = Field goal attempts
- FTA = Free throw attempts
- TRB = Total rebounds
- ORB = Offensive rebounds
- STL = Steals
- BLK = Blocks
- PF = Personal fouls
And team statistics are:
- tm_AST = Team assists
- tm_FG = Team field goals made
Stage 3: Pace Adjustment and League Normalization
The final PER value requires two additional adjustments:
$$\text{aPER} = \text{uPER} \times \frac{\text{lg\_Pace}}{\text{tm\_Pace}}$$
$$\text{PER} = \text{aPER} \times \frac{15}{\text{lg\_aPER}}$$
The pace adjustment accounts for the fact that players on fast-paced teams have more possessions and therefore more statistical opportunities. The final normalization ensures that league-average PER equals 15.00, making interpretation consistent across seasons.
9.1.3 Interpreting PER Values
The league normalization to 15.00 provides a convenient interpretation scale:
| PER Range | Interpretation |
|---|---|
| 35+ | All-time great season |
| 30-35 | MVP-caliber season |
| 25-30 | All-NBA level |
| 20-25 | Borderline All-Star |
| 15-20 | Above average starter |
| 13-15 | League average |
| 10-13 | Below average |
| < 10 | Replacement level or below |
Historical context is valuable: the highest single-season PER belongs to Giannis Antetokounmpo (31.86 in 2019-20), followed closely by Wilt Chamberlain's remarkable 1962-63 season (31.82). Michael Jordan holds several of the top PER seasons, including 31.71 in 1987-88.
9.1.4 PER Limitations
Despite its widespread use, PER has significant limitations:
Offensive Bias: PER heavily weights scoring and other offensive contributions. Defense is only captured through steals and blocks, which represent a small fraction of overall defensive impact. A player can be an excellent defender while recording few steals or blocks.
Does Not Account for Shot Quality: A player making difficult contested shots receives the same credit as one making open layups. Similarly, creating open shots for teammates through gravity or screening is not captured.
Efficiency Paradox: While named "efficiency rating," PER does not truly measure efficiency. High-usage players who take many shots can post high PER values even with mediocre shooting percentages.
Position Blindness: PER does not account for positional context. A center recording 2 assists per game is performing differently than a point guard with the same number.
Minute Threshold Issues: PER can be volatile for players with limited minutes, as a few good or bad possessions can swing the metric substantially.
9.2 Game Score
9.2.1 Purpose and Design
Game Score was also developed by John Hollinger as a simpler alternative to PER for evaluating single-game performances. While PER requires league and team context that may not be immediately available, Game Score can be calculated using only the player's individual box score statistics from that game.
The metric was designed to approximate PER while being computationally simple enough for quick calculations. It provides a reasonable estimate of a player's productivity in a specific game.
9.2.2 The Game Score Formula
The Game Score formula assigns specific weights to each box score statistic:
$$\text{Game Score} = \text{PTS} + 0.4 \times \text{FG} - 0.7 \times \text{FGA} - 0.4 \times (\text{FTA} - \text{FT})$$
$$+ 0.7 \times \text{ORB} + 0.3 \times \text{DRB} + \text{STL} + 0.7 \times \text{AST}$$
$$+ 0.7 \times \text{BLK} - 0.4 \times \text{PF} - \text{TOV}$$
Where:
- PTS = Points scored
- FG = Field goals made
- FGA = Field goal attempts
- FTA = Free throw attempts
- FT = Free throws made
- ORB = Offensive rebounds
- DRB = Defensive rebounds
- STL = Steals
- AST = Assists
- BLK = Blocks
- PF = Personal fouls
- TOV = Turnovers
9.2.3 Interpreting Game Score
Game Score has no formal upper bound, but typical values fall within predictable ranges:
| Game Score | Interpretation |
|---|---|
| 40+ | Historic performance |
| 30-40 | Outstanding game |
| 20-30 | Excellent game |
| 15-20 | Good game |
| 10-15 | Average starter performance |
| 5-10 | Below average |
| 0-5 | Poor game |
| < 0 | Very poor game |
The highest Game Score in NBA history belongs to Kobe Bryant's 81-point game against Toronto on January 22, 2006, with a Game Score of 63.8. Other notable performances include Wilt Chamberlain's 100-point game (estimated at 64.0) and various performances by Michael Jordan and LeBron James in the 50+ range.
9.2.4 Advantages and Limitations
Advantages: - Simple to calculate with basic arithmetic - Requires only single-game data - Useful for comparing individual game performances - Provides quick assessment of game quality
Limitations: - Does not account for pace or minutes - No adjustment for opponent quality - Shares PER's offensive bias - Less sophisticated than full PER calculation - Cannot be meaningfully summed across games
9.3 Player Impact Estimate (PIE)
9.3.1 Background and Purpose
Player Impact Estimate (PIE) was developed by the NBA's official statistics partner and represents an attempt to create a more balanced metric than PER. PIE measures a player's overall statistical contribution as a percentage of total game statistics.
The intuition behind PIE is straightforward: if a player contributes a certain percentage of the combined statistical output of both teams, that percentage represents their "impact" on the game.
9.3.2 The PIE Formula
PIE is calculated as:
$$\text{PIE} = \frac{\text{Player Contribution}}{\text{Game Total}} \times 100$$
Where Player Contribution is:
$$\text{PTS} + \text{FG} + \text{FT} - \text{FGA} - \text{FTA} + \text{DRB} + 0.5 \times \text{ORB}$$ $$+ \text{AST} + \text{STL} + 0.5 \times \text{BLK} - \text{PF} - \text{TOV}$$
And Game Total is the same formula applied to all players in the game combined (both teams).
9.3.3 Interpreting PIE
Since PIE represents a percentage of game contribution, the theoretical maximum depends on how much of the game's total statistics a single player could accumulate. In practice:
| PIE | Interpretation |
|---|---|
| 20+ | Elite performance |
| 15-20 | All-Star caliber |
| 10-15 | Starter level |
| 5-10 | Rotation player |
| < 5 | Limited contributor |
League average PIE is approximately 10%, representing the expected contribution if all 10 players on the court contributed equally.
9.3.4 PIE Limitations
Zero-Sum Nature: PIE is inherently zero-sum within a game. One player's high PIE necessarily reduces others' PIE values.
Context Dependence: A player's PIE depends on the statistical output of the entire game, making comparisons across games with different paces or styles problematic.
Team Composition Effects: Playing with or against more or less productive teammates affects PIE in ways unrelated to individual performance.
9.4 Usage Rate (USG%)
9.4.1 Concept and Purpose
Usage Rate measures the percentage of team possessions a player "uses" while on the court. A possession is considered "used" by a player when it ends with that player taking a field goal attempt, shooting free throws, or committing a turnover.
Usage Rate answers a fundamental question: when this player is on the court, how often does the team's offense flow through them?
9.4.2 The Usage Rate Formula
The standard Usage Rate formula is:
$$\text{USG\%} = 100 \times \frac{(\text{FGA} + 0.44 \times \text{FTA} + \text{TOV}) \times (\text{Tm\_MIN} / 5)}{\text{MIN} \times (\text{Tm\_FGA} + 0.44 \times \text{Tm\_FTA} + \text{Tm\_TOV})}$$
Where:
- FGA = Player field goal attempts
- FTA = Player free throw attempts
- TOV = Player turnovers
- MIN = Player minutes played
- Tm_MIN = Team total minutes played
- Tm_FGA = Team field goal attempts
- Tm_FTA = Team free throw attempts
- Tm_TOV = Team turnovers
The factor 0.44 represents the estimated proportion of free throw attempts that constitute true possession-ending events (excluding and-ones and technical free throws).
9.4.3 Interpreting Usage Rate
Usage Rate is bounded between 0% and approximately 40% in practice:
| USG% | Interpretation |
|---|---|
| 35%+ | Extremely high volume (rare) |
| 30-35% | Primary scoring option |
| 25-30% | High-usage star |
| 20-25% | Secondary option |
| 15-20% | Role player |
| < 15% | Low-usage specialist |
League average Usage Rate is approximately 20%, representing the expected value if all five players on the court used possessions equally.
Historical leaders include Russell Westbrook (41.7% in 2016-17, the highest in the three-point era), Kobe Bryant (38.7% in 2005-06), and various seasons from Allen Iverson and Michael Jordan.
9.4.4 Usage Rate and Efficiency
Usage Rate must be interpreted alongside efficiency metrics. A high Usage Rate is only valuable if the player produces efficiently. The relationship between usage and efficiency is well-documented: as players take on more responsibility, their efficiency tends to decline.
This creates an interesting analytical challenge: how do we compare a player with 30% usage and 55% True Shooting to one with 20% usage and 60% True Shooting? The answer depends on context including team composition, opponent quality, and game situation.
9.4.5 Limitations
Does Not Capture Playmaking: A player who creates open shots for teammates through passing does not receive usage credit for those possessions.
Treats All Usage Equally: A difficult contested three-pointer counts the same as an open layup.
No Defensive Component: Usage Rate is purely offensive.
Context Blindness: Does not account for whether high usage is a product of team design or player ball-dominance.
9.5 Assist Rate and Assist Percentage
9.5.1 Assist Percentage (AST%)
Assist Percentage estimates the percentage of teammate field goals a player assisted while on the court.
$$\text{AST\%} = 100 \times \frac{\text{AST}}{\frac{\text{MIN}}{\text{Tm\_MIN} / 5} \times \text{Tm\_FG} - \text{FG}}$$
This formula calculates what proportion of teammate made field goals (excluding the player's own field goals) the player assisted on.
9.5.2 Assist Ratio
A related metric, Assist Ratio, measures assists per 100 possessions used:
$$\text{AST\_Ratio} = 100 \times \frac{\text{AST}}{\text{FGA} + 0.44 \times \text{FTA} + \text{AST} + \text{TOV}}$$
This provides a different perspective by relating assists to the player's total offensive involvement rather than team field goals.
9.5.3 Interpreting Assist Metrics
Assist Percentage varies significantly by position:
| Position | Typical AST% Range |
|---|---|
| Point Guard | 25-45% |
| Shooting Guard | 10-25% |
| Small Forward | 10-20% |
| Power Forward | 8-18% |
| Center | 5-15% |
Historical leaders in AST% include John Stockton (50.7% in 1990-91), Magic Johnson (multiple seasons above 45%), and Chris Paul (consistently above 40% throughout his career).
9.5.4 Limitations
Quality Agnostic: An assist on a wide-open corner three counts the same as an assist on a contested mid-range shot.
System Dependent: Some offensive systems generate more assist opportunities than others.
Does Not Capture Hockey Assists: The pass before the pass that leads to a basket goes uncredited.
9.6 Rebounding Percentages
9.6.1 Offensive Rebound Percentage (ORB%)
Offensive Rebound Percentage estimates the percentage of available offensive rebounds a player grabs while on the court:
$$\text{ORB\%} = 100 \times \frac{\text{ORB} \times (\text{Tm\_MIN} / 5)}{\text{MIN} \times (\text{Tm\_ORB} + \text{Opp\_DRB})}$$
The denominator represents total offensive rebounding opportunities: the team's offensive rebounds plus the opponent's defensive rebounds (which together account for all possible outcomes of missed shots by the team).
9.6.2 Defensive Rebound Percentage (DRB%)
Similarly, Defensive Rebound Percentage measures the share of available defensive rebounds:
$$\text{DRB\%} = 100 \times \frac{\text{DRB} \times (\text{Tm\_MIN} / 5)}{\text{MIN} \times (\text{Tm\_DRB} + \text{Opp\_ORB})}$$
9.6.3 Total Rebound Percentage (TRB%)
Total Rebound Percentage combines both:
$$\text{TRB\%} = 100 \times \frac{\text{TRB} \times (\text{Tm\_MIN} / 5)}{\text{MIN} \times (\text{Tm\_TRB} + \text{Opp\_TRB})}$$
9.6.4 Interpreting Rebounding Percentages
| Metric | Elite | Good | Average | Below Average |
|---|---|---|---|---|
| ORB% | > 10% | 7-10% | 4-7% | < 4% |
| DRB% | > 25% | 20-25% | 15-20% | < 15% |
| TRB% | > 15% | 12-15% | 8-12% | < 8% |
Notable historical values include Dennis Rodman's extraordinary offensive rebounding seasons (often exceeding 18% ORB%) and Andre Drummond's league-leading TRB% throughout the 2010s.
9.6.5 Positional Context
Rebounding percentages should be interpreted with positional context. A point guard with 10% TRB% is contributing exceptional rebounding for the position, while the same value from a center might indicate below-average performance.
9.6.6 Limitations
Does Not Capture Boxing Out: A player who boxes out opponents to allow teammates to grab rebounds provides value not reflected in their rebounding percentage.
Scheme Dependent: Some teams ask certain players to leak out in transition rather than crash the boards.
Contested vs. Uncontested: All rebounds count equally regardless of difficulty.
9.7 Steal Percentage (STL%)
9.7.1 Definition and Formula
Steal Percentage estimates the percentage of opponent possessions that end with a steal by the player:
$$\text{STL\%} = 100 \times \frac{\text{STL} \times (\text{Tm\_MIN} / 5)}{\text{MIN} \times \text{Opp\_Poss}}$$
Where Opp_Poss represents opponent possessions, typically estimated as:
$$\text{Opp\_Poss} = \text{Opp\_FGA} - \text{Opp\_ORB} + \text{Opp\_TOV} + 0.44 \times \text{Opp\_FTA}$$
9.7.2 Interpretation
| STL% | Interpretation |
|---|---|
| > 3.0% | Elite ball hawk |
| 2.5-3.0% | Excellent |
| 2.0-2.5% | Good |
| 1.5-2.0% | Average |
| < 1.5% | Below average |
Historical leaders include Alvin Robertson, Michael Jordan, and Chris Paul, all with seasons exceeding 3.5%.
9.7.3 Limitations
Gambling Risk: High steal rates often correlate with defensive gambling that can lead to breakdowns when steals are not obtained.
Position Dependent: Guards typically have more steal opportunities than big men.
Does Not Equal Defense: Many excellent defenders rarely record steals.
9.8 Block Percentage (BLK%)
9.8.1 Definition and Formula
Block Percentage estimates the percentage of opponent two-point field goal attempts blocked by the player:
$$\text{BLK\%} = 100 \times \frac{\text{BLK} \times (\text{Tm\_MIN} / 5)}{\text{MIN} \times (\text{Opp\_FGA} - \text{Opp\_3PA})}$$
Note that only two-point attempts are included in the denominator, as three-point shots are rarely blocked.
9.8.2 Interpretation
| BLK% | Interpretation |
|---|---|
| > 8% | Elite shot blocker |
| 5-8% | Excellent |
| 3-5% | Good |
| 1-3% | Average |
| < 1% | Rarely blocks shots |
Historical leaders include Mark Eaton (8.1% in 1984-85), Manute Bol (multiple seasons above 7%), and modern players like Hassan Whiteside and Myles Turner.
9.8.3 Limitations
Altered Shots: Many excellent rim protectors alter shots without blocking them, a contribution not captured here.
Position Dependent: Centers naturally have more blocking opportunities.
Does Not Account for Foul Rate: Some players foul frequently when attempting blocks.
9.9 Turnover Percentage (TOV%)
9.9.1 Definition and Formula
Turnover Percentage estimates turnovers committed per 100 plays:
$$\text{TOV\%} = 100 \times \frac{\text{TOV}}{\text{FGA} + 0.44 \times \text{FTA} + \text{TOV}}$$
This formula measures how often a player turns the ball over relative to their total offensive usage.
9.9.2 Interpretation
Lower values are better for Turnover Percentage:
| TOV% | Interpretation |
|---|---|
| < 8% | Excellent ball security |
| 8-12% | Good |
| 12-16% | Average |
| 16-20% | Below average |
| > 20% | Turnover prone |
Low-turnover players historically include point guards like Jose Calderon (career 9.3% TOV%) and off-ball players who rarely handle the ball.
9.9.3 Context Matters
Turnover Percentage must be interpreted alongside other metrics:
- Ball handlers naturally have higher TOV% due to passing responsibilities
- High AST% players may have acceptable higher TOV% due to risk/reward
- Post players with high TOV% may be indicating poor passing or decision-making
The Assist-to-Turnover ratio provides additional context by comparing positive (assists) to negative (turnovers) playmaking outcomes.
9.10 Critiques and Limitations of Composite Metrics
9.10.1 The Measurement Problem
All advanced box score metrics share a fundamental limitation: they can only measure what is recorded in the box score. Many crucial basketball contributions leave no statistical trace:
- Setting effective screens
- Drawing defensive attention (gravity)
- Making correct rotations on defense
- Communicating and organizing teammates
- Cutting at the right time to create spacing
- Contesting shots without blocking them
- Boxing out to allow teammates to rebound
This creates systematic biases. Players whose skills manifest in box score statistics appear more valuable than those whose contributions are equally important but statistically invisible.
9.10.2 The Weighting Problem
Every composite metric requires decisions about how to weight different statistical categories. These weights embed assumptions about basketball value that may not hold across all contexts:
- Is a steal worth more than a block?
- How much more valuable is a three-pointer than a two-pointer?
- What is the cost of a turnover?
- How should assists be valued relative to scoring?
Different metrics answer these questions differently, leading to divergent player rankings. PER, for example, values offensive rebounds more heavily than defensive rebounds, while other metrics treat all rebounds equally.
9.10.3 The Context Problem
Box score statistics lack context that is crucial for proper evaluation:
Shot Difficulty: A contested fadeaway jumper counts the same as an open layup.
Defensive Assignment: A player guarding the opponent's best scorer faces different challenges than one guarding a weak offensive player.
Game State: Late-game execution under pressure differs from early-game play.
Teammate Quality: Playing alongside excellent teammates can inflate or deflate individual statistics.
9.10.4 The Defensive Measurement Gap
Perhaps the most significant limitation of box score metrics is their inadequate treatment of defense. Steals and blocks capture only a fraction of defensive impact:
- Perimeter defenders who never get beaten provide immense value without recording defensive statistics
- Rim protectors who alter shots without blocking them change opponent behavior
- Defensive communication and rotation cannot be quantified from box scores
This creates a systematic bias toward offensive players in composite metrics, potentially undervaluing elite defenders.
9.10.5 Collinearity and Double-Counting
Box score statistics are not independent. Points are related to field goals and free throws. Assists are related to teammate field goals. This creates potential double-counting issues:
- PER credits both field goals made and points scored
- Rebounds are partly a function of teammate (and own) missed shots
- Assists depend on teammate shooting accuracy
Metric designers attempt to address these issues through formula construction, but some degree of redundancy inevitably remains.
9.10.6 Sample Size and Variance
Per-minute and per-possession metrics can be volatile with small samples. A player with limited playing time might have an outlier game that dramatically affects their seasonal metrics. This is particularly relevant for:
- Benchwarers with limited minutes
- Injured players
- Rookies early in the season
- Players changing roles mid-season
Most analysts apply minimum threshold requirements (often 500+ minutes or 25+ games) before considering these metrics reliable.
9.11 Best Practices for Using Advanced Box Score Metrics
9.11.1 Use Multiple Metrics
No single metric captures all aspects of basketball performance. Best practice involves examining multiple metrics and understanding where they agree and disagree:
Player A: High PER, Low USG% -> Efficient secondary option
Player B: High USG%, Moderate PER -> Volume scorer, possibly inefficient
Player C: Low PER, High AST% -> Facilitator whose value isn't captured by PER
9.11.2 Consider Positional Context
The same metric value can mean different things for different positions. A 15% TRB% is excellent for a guard, average for a forward, and poor for a center. Always interpret metrics relative to positional norms.
9.11.3 Establish Minimum Sample Sizes
Avoid drawing conclusions from small samples. Suggested minimums:
- Single-game metrics: Game Score can be meaningful
- Weekly/monthly trends: 200+ minutes
- Seasonal analysis: 500+ minutes or 25+ games
- Career analysis: 2,000+ minutes
9.11.4 Supplement with Video and Advanced Data
Box score metrics should be starting points, not conclusions. The best analyses combine statistical analysis with:
- Game film review
- Tracking data (spatial metrics)
- Play-by-play analysis
- Expert evaluation
9.11.5 Understand Metric Construction
Before using a metric, understand: - What it measures (and what it doesn't) - How it is calculated - What assumptions are embedded in the formula - What its known limitations are
This understanding prevents misapplication and overinterpretation.
9.12 Python Implementations
The following sections provide Python implementations for the metrics discussed in this chapter. These implementations are designed for clarity and educational purposes; production systems might require additional optimization.
9.12.1 Basic Setup
import numpy as np
import pandas as pd
from typing import Dict, Optional, Union
from dataclasses import dataclass
@dataclass
class PlayerStats:
"""Container for player box score statistics."""
minutes: float
points: int
field_goals_made: int
field_goals_attempted: int
three_pointers_made: int
three_pointers_attempted: int
free_throws_made: int
free_throws_attempted: int
offensive_rebounds: int
defensive_rebounds: int
total_rebounds: int
assists: int
steals: int
blocks: int
turnovers: int
personal_fouls: int
@dataclass
class TeamStats:
"""Container for team statistics."""
minutes: float
field_goals_made: int
field_goals_attempted: int
three_pointers_made: int
free_throws_made: int
free_throws_attempted: int
offensive_rebounds: int
defensive_rebounds: int
total_rebounds: int
assists: int
turnovers: int
points: int
personal_fouls: int
@dataclass
class LeagueStats:
"""Container for league-wide statistics."""
games: int
pace: float
field_goals_made: int
field_goals_attempted: int
free_throws_made: int
free_throws_attempted: int
offensive_rebounds: int
total_rebounds: int
assists: int
turnovers: int
points: int
personal_fouls: int
9.12.2 Game Score Implementation
def calculate_game_score(player: PlayerStats) -> float:
"""
Calculate John Hollinger's Game Score.
Game Score provides a quick estimate of a player's productivity
in a single game, using only that game's box score statistics.
Parameters
----------
player : PlayerStats
Player's box score statistics for the game
Returns
-------
float
The player's Game Score for the game
Examples
--------
>>> # Kobe Bryant's 81-point game
>>> kobe_81 = PlayerStats(
... minutes=42, points=81, field_goals_made=28,
... field_goals_attempted=46, three_pointers_made=7,
... three_pointers_attempted=13, free_throws_made=18,
... free_throws_attempted=20, offensive_rebounds=2,
... defensive_rebounds=4, total_rebounds=6, assists=2,
... steals=3, blocks=1, turnovers=1, personal_fouls=3
... )
>>> calculate_game_score(kobe_81)
63.8
"""
game_score = (
player.points
+ 0.4 * player.field_goals_made
- 0.7 * player.field_goals_attempted
- 0.4 * (player.free_throws_attempted - player.free_throws_made)
+ 0.7 * player.offensive_rebounds
+ 0.3 * player.defensive_rebounds
+ player.steals
+ 0.7 * player.assists
+ 0.7 * player.blocks
- 0.4 * player.personal_fouls
- player.turnovers
)
return round(game_score, 1)
9.12.3 Usage Rate Implementation
def calculate_usage_rate(
player: PlayerStats,
team: TeamStats
) -> float:
"""
Calculate Usage Rate (USG%).
Usage Rate estimates the percentage of team possessions
a player uses while on the court.
Parameters
----------
player : PlayerStats
Player's statistics
team : TeamStats
Team's statistics for the same period
Returns
-------
float
Usage Rate as a percentage
"""
# Estimate possessions used by player
player_possessions = (
player.field_goals_attempted
+ 0.44 * player.free_throws_attempted
+ player.turnovers
)
# Estimate team possessions
team_possessions = (
team.field_goals_attempted
+ 0.44 * team.free_throws_attempted
+ team.turnovers
)
# Calculate per-minute values and scale
minutes_factor = (team.minutes / 5) / player.minutes
usage_rate = 100 * (player_possessions * minutes_factor) / team_possessions
return round(usage_rate, 1)
9.12.4 Assist Percentage Implementation
def calculate_assist_percentage(
player: PlayerStats,
team: TeamStats
) -> float:
"""
Calculate Assist Percentage (AST%).
Estimates the percentage of teammate field goals assisted
by the player while on the court.
Parameters
----------
player : PlayerStats
Player's statistics
team : TeamStats
Team's statistics
Returns
-------
float
Assist Percentage
"""
# Teammate field goals (excluding player's own FGs)
teammate_fg = team.field_goals_made - player.field_goals_made
# Scale to minutes played
minutes_factor = player.minutes / (team.minutes / 5)
# Teammate FGs while player on court (estimated)
teammate_fg_on_court = teammate_fg * minutes_factor
if teammate_fg_on_court == 0:
return 0.0
assist_pct = 100 * player.assists / teammate_fg_on_court
return round(assist_pct, 1)
9.12.5 Rebounding Percentages Implementation
def calculate_rebounding_percentages(
player: PlayerStats,
team: TeamStats,
opponent: TeamStats
) -> Dict[str, float]:
"""
Calculate Offensive, Defensive, and Total Rebound Percentages.
Parameters
----------
player : PlayerStats
Player's statistics
team : TeamStats
Team's statistics
opponent : TeamStats
Opponent's statistics
Returns
-------
dict
Dictionary containing ORB%, DRB%, and TRB%
"""
minutes_factor = (team.minutes / 5) / player.minutes
# Offensive Rebound Percentage
orb_opportunities = team.offensive_rebounds + opponent.defensive_rebounds
orb_pct = 100 * (player.offensive_rebounds * minutes_factor) / orb_opportunities
# Defensive Rebound Percentage
drb_opportunities = team.defensive_rebounds + opponent.offensive_rebounds
drb_pct = 100 * (player.defensive_rebounds * minutes_factor) / drb_opportunities
# Total Rebound Percentage
trb_opportunities = team.total_rebounds + opponent.total_rebounds
trb_pct = 100 * (player.total_rebounds * minutes_factor) / trb_opportunities
return {
'ORB%': round(orb_pct, 1),
'DRB%': round(drb_pct, 1),
'TRB%': round(trb_pct, 1)
}
9.12.6 Steal and Block Percentages Implementation
def calculate_steal_percentage(
player: PlayerStats,
team: TeamStats,
opponent_possessions: float
) -> float:
"""
Calculate Steal Percentage (STL%).
Estimates the percentage of opponent possessions that end
with a steal by the player.
Parameters
----------
player : PlayerStats
Player's statistics
team : TeamStats
Team's statistics
opponent_possessions : float
Estimated opponent possessions
Returns
-------
float
Steal Percentage
"""
minutes_factor = (team.minutes / 5) / player.minutes
stl_pct = 100 * (player.steals * minutes_factor) / opponent_possessions
return round(stl_pct, 2)
def calculate_block_percentage(
player: PlayerStats,
team: TeamStats,
opponent_2pa: int
) -> float:
"""
Calculate Block Percentage (BLK%).
Estimates the percentage of opponent two-point attempts
blocked by the player.
Parameters
----------
player : PlayerStats
Player's statistics
team : TeamStats
Team's statistics
opponent_2pa : int
Opponent two-point field goal attempts
Returns
-------
float
Block Percentage
"""
minutes_factor = (team.minutes / 5) / player.minutes
blk_pct = 100 * (player.blocks * minutes_factor) / opponent_2pa
return round(blk_pct, 2)
9.12.7 Turnover Percentage Implementation
def calculate_turnover_percentage(player: PlayerStats) -> float:
"""
Calculate Turnover Percentage (TOV%).
Estimates turnovers per 100 plays.
Parameters
----------
player : PlayerStats
Player's statistics
Returns
-------
float
Turnover Percentage
"""
plays = (
player.field_goals_attempted
+ 0.44 * player.free_throws_attempted
+ player.turnovers
)
if plays == 0:
return 0.0
tov_pct = 100 * player.turnovers / plays
return round(tov_pct, 1)
9.12.8 Player Impact Estimate Implementation
def calculate_pie(
player: PlayerStats,
team: TeamStats,
opponent: TeamStats
) -> float:
"""
Calculate Player Impact Estimate (PIE).
PIE measures a player's overall statistical contribution
as a percentage of game totals.
Parameters
----------
player : PlayerStats
Player's statistics
team : TeamStats
Team's statistics
opponent : TeamStats
Opponent's statistics
Returns
-------
float
PIE as a percentage
"""
def contribution(p):
"""Calculate contribution for a stat line."""
return (
p.points
+ p.field_goals_made
+ p.free_throws_made
- p.field_goals_attempted
- p.free_throws_attempted
+ p.defensive_rebounds
+ 0.5 * p.offensive_rebounds
+ p.assists
+ p.steals
+ 0.5 * p.blocks
- p.personal_fouls
- p.turnovers
)
player_contribution = contribution(player)
game_contribution = contribution(team) + contribution(opponent)
if game_contribution == 0:
return 0.0
pie = 100 * player_contribution / game_contribution
return round(pie, 1)
9.12.9 Complete PER Implementation
def calculate_per(
player: PlayerStats,
team: TeamStats,
league: LeagueStats,
team_pace: float,
league_aper: float = None
) -> float:
"""
Calculate Player Efficiency Rating (PER).
This is the complete Hollinger PER formula including
pace adjustment and league normalization.
Parameters
----------
player : PlayerStats
Player's statistics
team : TeamStats
Team's statistics
league : LeagueStats
League-wide statistics
team_pace : float
Team's pace (possessions per 48 minutes)
league_aper : float, optional
League average aPER for normalization.
If None, final normalization is skipped.
Returns
-------
float
Player Efficiency Rating
"""
if player.minutes == 0:
return 0.0
# Calculate league factors
lg_fg = league.field_goals_made
lg_ft = league.free_throws_made
lg_ast = league.assists
lg_pts = league.points
lg_fga = league.field_goals_attempted
lg_fta = league.free_throws_attempted
lg_orb = league.offensive_rebounds
lg_trb = league.total_rebounds
lg_tov = league.turnovers
lg_pf = league.personal_fouls
# Factor calculation
factor = (2/3) - (0.5 * (lg_ast / lg_fg)) / (2 * (lg_fg / lg_ft))
# Value of Possession
vop = lg_pts / (lg_fga - lg_orb + lg_tov + 0.44 * lg_fta)
# League Defensive Rebound Percentage
lg_drb_pct = (lg_trb - lg_orb) / lg_trb
# Team assist ratio for individual adjustment
tm_ast_ratio = team.assists / team.field_goals_made
# Calculate unadjusted PER
uper = (1 / player.minutes) * (
# Three pointers
player.three_pointers_made
# Assists
+ (2/3) * player.assists
# Field goals
+ (2 - factor * tm_ast_ratio) * player.field_goals_made
# Free throws
+ (player.free_throws_made * 0.5 * (
1 + (1 - tm_ast_ratio) + (2/3) * tm_ast_ratio
))
# Turnovers (negative)
- vop * player.turnovers
# Missed field goals (negative)
- vop * lg_drb_pct * (
player.field_goals_attempted - player.field_goals_made
)
# Missed free throws (negative)
- vop * 0.44 * (0.44 + 0.56 * lg_drb_pct) * (
player.free_throws_attempted - player.free_throws_made
)
# Defensive rebounds (positive)
+ vop * (1 - lg_drb_pct) * (
player.total_rebounds - player.offensive_rebounds
)
# Offensive rebounds (positive)
+ vop * lg_drb_pct * player.offensive_rebounds
# Steals (positive)
+ vop * player.steals
# Blocks (positive)
+ vop * lg_drb_pct * player.blocks
# Personal fouls (negative)
- player.personal_fouls * (
(lg_ft / lg_pf) - 0.44 * (lg_fta / lg_pf) * vop
)
)
# Pace adjustment
aper = uper * (league.pace / team_pace)
# League normalization
if league_aper is not None:
per = aper * (15 / league_aper)
else:
per = aper
return round(per, 2)
9.13 Summary
Advanced box score metrics represent a significant advancement over simple counting statistics, providing more nuanced and contextualized assessments of player performance. This chapter examined the major metrics in this category:
Player Efficiency Rating (PER) attempts to capture total statistical contribution in a single number, normalized to a league average of 15.00. Despite its complexity, PER has significant limitations including offensive bias and inadequate treatment of defense.
Game Score offers a simpler alternative for single-game evaluation, requiring only individual box score data.
Player Impact Estimate (PIE) measures contribution as a percentage of total game statistics, providing a more intuitive but context-dependent measure.
Usage Rate quantifies offensive load, measuring the percentage of possessions a player uses while on court.
Assist metrics (AST%, Assist Ratio) capture playmaking contribution in different ways.
Rebounding percentages (ORB%, DRB%, TRB%) measure rebounding production relative to available opportunities.
Steal and Block percentages provide rate-based measures of these defensive statistics.
Turnover Percentage measures ball security relative to offensive involvement.
All these metrics share common limitations: they can only measure what appears in box scores, they require somewhat arbitrary weighting decisions, they lack context about shot difficulty and game state, and they systematically undervalue defense.
Best practice involves using multiple metrics in conjunction, understanding their construction and limitations, supplementing with video and advanced data, and applying appropriate sample size thresholds.
The Python implementations provided in this chapter enable you to calculate these metrics for your own analyses. In subsequent chapters, we will explore more sophisticated approaches that address some limitations of box score metrics, including spatial data analysis and plus-minus methodologies.
References
-
Hollinger, J. (2005). Pro Basketball Forecast. Potomac Books.
-
Kubatko, J., Oliver, D., Pelton, K., & Rosenbaum, D. T. (2007). A starting point for analyzing basketball statistics. Journal of Quantitative Analysis in Sports, 3(3).
-
Oliver, D. (2004). Basketball on Paper: Rules and Tools for Performance Analysis. Potomac Books.
-
Rosenbaum, D. T. (2004). Measuring how NBA players help their teams win. 82games.com.
-
Pelton, K. (2012). Counterpoints: Advanced basketball statistics FAQ. ESPN.com.
-
Berri, D. J., Schmidt, M. B., & Brook, S. L. (2006). The Wages of Wins: Taking Measure of the Many Myths in Modern Sport. Stanford Business Books.
-
Engelmann, J. (2017). Possession-based player performance analysis in basketball. MIT Sloan Sports Analytics Conference.
-
Fearnhead, P., & Taylor, B. M. (2011). On estimating the ability of NBA players. Journal of Quantitative Analysis in Sports, 7(3).