Net Rating and Point Differential
Beginner
10 min read
0 views
Nov 27, 2025
# Net Rating
## Definition
**Net Rating** is one of the most straightforward yet powerful metrics in basketball analytics. It represents the point differential per 100 possessions:
```
Net Rating = Offensive Rating - Defensive Rating
NetRtg = ORtg - DRtg
```
Where:
- **Offensive Rating (ORtg)**: Points scored per 100 possessions
- **Defensive Rating (DRtg)**: Points allowed per 100 possessions
A positive Net Rating indicates a team or player scores more points than they allow (per 100 possessions), while a negative Net Rating indicates the opposite.
## Team vs Individual Net Rating
### Team Net Rating
Team Net Rating is calculated using the entire team's performance:
```
Team ORtg = (Total Points Scored / Total Possessions) × 100
Team DRtg = (Total Points Allowed / Total Possessions) × 100
Team NetRtg = Team ORtg - Team DRtg
```
**Example**: If a team scores 112 points on 98 possessions and allows 106 points:
- ORtg = (112 / 98) × 100 = 114.3
- DRtg = (106 / 98) × 100 = 108.2
- NetRtg = 114.3 - 108.2 = +6.1
### Individual Net Rating
Individual (player) Net Rating measures the point differential per 100 possessions when a specific player is on the court. It's calculated by tracking:
```
Player NetRtg = (Team Points - Opponent Points) / Possessions × 100
(while player is on court)
```
**On/Off Net Rating** compares performance with and without a player:
```
On/Off NetRtg = Team NetRtg (player on) - Team NetRtg (player off)
```
**Important Considerations**:
- Individual Net Rating is heavily influenced by teammates and opponents
- Small sample sizes can be misleading
- Context matters (strength of opponents, lineup combinations, garbage time)
## Interpretation
### What Makes a Good Net Rating?
| Net Rating | Team Level | Player Impact |
|-----------|-----------|---------------|
| +10 or higher | Elite championship contender | Superstar/franchise player |
| +5 to +10 | Strong playoff team | All-Star level impact |
| +2 to +5 | Playoff contender | Solid contributor |
| -2 to +2 | Average/bubble team | Neutral impact |
| -5 to -2 | Below average | Negative impact |
| -10 or worse | Lottery team | Significant liability |
### Context is Critical
1. **Lineup Strength**: A player's Net Rating depends on who they play with
2. **Opposition Quality**: Facing weaker opponents inflates Net Rating
3. **Sample Size**: Need sufficient minutes/games for reliability
4. **Role**: Bench players against second units may have inflated numbers
5. **Game Context**: Blowouts and garbage time can skew individual ratings
## Historical Leaders
### Best Team Net Ratings (Single Season)
1. **2015-16 Golden State Warriors**: +11.6 (73-9 record)
- ORtg: 114.5, DRtg: 103.8
2. **2016-17 Golden State Warriors**: +11.4 (67-15 record)
- ORtg: 115.6, DRtg: 104.2
3. **1996-97 Chicago Bulls**: +10.8 (69-13 record)
- ORtg: 115.0, DRtg: 104.2
4. **1995-96 Chicago Bulls**: +10.7 (72-10 record)
- ORtg: 115.2, DRtg: 104.5
5. **2007-08 Boston Celtics**: +10.3 (66-16 record)
- ORtg: 110.2, DRtg: 99.9
### Top Individual Net Ratings (Single Season, min 1000 minutes)
**Recent Era (2020s)**:
- Nikola Jokic (2021-22): +13.7
- Stephen Curry (2020-21): +13.3
- Giannis Antetokounmpo (2019-20): +12.8
**Historical**:
- LeBron James (2012-13): +14.0
- Chris Paul (2007-08): +13.8
- Kevin Garnett (2007-08): +13.2
**Note**: Individual Net Ratings should be evaluated alongside on/off splits and lineup data for proper context.
## Relationship with Wins
Net Rating has one of the strongest correlations with winning in basketball analytics.
### Pythagorean Win Expectation
The **Pythagorean expectation** formula estimates wins based on Net Rating:
```
Expected Win% = Points Scored^14 / (Points Scored^14 + Points Allowed^14)
```
A simpler approximation using Net Rating:
```
Expected Win% ≈ 0.5 + (NetRtg × 2.7) / 100
```
Where the coefficient (2.7) is derived empirically from NBA data.
### Rule of Thumb
**For every +1.0 Net Rating, a team wins approximately 2.7 more games over an 82-game season.**
Examples:
- Team with +5.0 NetRtg: Expected ~54-55 wins
- Team with +10.0 NetRtg: Expected ~68-69 wins
- Team with -5.0 NetRtg: Expected ~28-29 wins
### Correlation Analysis
Studies consistently show:
- **R² ≈ 0.90-0.94** between Net Rating and win percentage
- This is higher than almost any other single statistic
- Point differential (raw, not per 100 possessions) has similar correlation
### Why Net Rating Predicts Wins
1. **Pace-Adjusted**: Unlike raw point differential, it accounts for game tempo
2. **Consistent**: Based on possessions, which are countable and objective
3. **Complete**: Captures both offensive and defensive performance
4. **Predictive**: Better predictor of future performance than win-loss record alone
## Code Examples
### Python: Calculate Net Rating
```python
import pandas as pd
import numpy as np
def calculate_team_net_rating(df):
"""
Calculate team net rating from game data.
Parameters:
df: DataFrame with columns ['points_scored', 'points_allowed', 'possessions']
Returns:
Dictionary with ORtg, DRtg, and NetRtg
"""
total_points_scored = df['points_scored'].sum()
total_points_allowed = df['points_allowed'].sum()
total_possessions = df['possessions'].sum()
ortg = (total_points_scored / total_possessions) * 100
drtg = (total_points_allowed / total_possessions) * 100
net_rtg = ortg - drtg
return {
'ORtg': round(ortg, 1),
'DRtg': round(drtg, 1),
'NetRtg': round(net_rtg, 1)
}
# Example usage
games = pd.DataFrame({
'points_scored': [112, 108, 115, 103, 110],
'points_allowed': [105, 110, 108, 98, 106],
'possessions': [98, 102, 100, 95, 99]
})
ratings = calculate_team_net_rating(games)
print(f"Offensive Rating: {ratings['ORtg']}")
print(f"Defensive Rating: {ratings['DRtg']}")
print(f"Net Rating: {ratings['NetRtg']}")
```
### Python: Player On/Off Net Rating
```python
def calculate_player_on_off(player_on_df, player_off_df):
"""
Calculate player's on/off court impact.
Parameters:
player_on_df: DataFrame of team performance when player is on court
player_off_df: DataFrame of team performance when player is off court
Returns:
Dictionary with on-court, off-court, and differential ratings
"""
def calc_net_rating(df):
points_for = df['team_points'].sum()
points_against = df['opp_points'].sum()
possessions = df['possessions'].sum()
return ((points_for - points_against) / possessions) * 100
net_rating_on = calc_net_rating(player_on_df)
net_rating_off = calc_net_rating(player_off_df)
on_off_diff = net_rating_on - net_rating_off
return {
'NetRtg_On': round(net_rating_on, 1),
'NetRtg_Off': round(net_rating_off, 1),
'On_Off_Diff': round(on_off_diff, 1)
}
# Example: LeBron James impact
lebron_on = pd.DataFrame({
'team_points': [28, 32, 25, 30],
'opp_points': [24, 26, 23, 28],
'possessions': [25, 28, 22, 26]
})
lebron_off = pd.DataFrame({
'team_points': [18, 20, 16],
'opp_points': [22, 24, 20],
'possessions': [18, 20, 17]
})
impact = calculate_player_on_off(lebron_on, lebron_off)
print(f"Net Rating (On): {impact['NetRtg_On']}")
print(f"Net Rating (Off): {impact['NetRtg_Off']}")
print(f"On/Off Differential: {impact['On_Off_Diff']}")
```
### Python: Win Projection from Net Rating
```python
def predict_wins_from_net_rating(net_rating, games=82):
"""
Predict wins based on Net Rating using empirical formula.
Parameters:
net_rating: Team's net rating (points per 100 possessions)
games: Number of games in season (default 82)
Returns:
Projected number of wins
"""
# Formula: Win% = 0.5 + (NetRtg * 2.7) / 100
win_pct = 0.5 + (net_rating * 2.7) / 100
# Bound between 0 and 1
win_pct = max(0, min(1, win_pct))
projected_wins = win_pct * games
return {
'Win_Percentage': round(win_pct, 3),
'Projected_Wins': round(projected_wins, 1),
'Projected_Losses': round(games - projected_wins, 1)
}
# Examples
teams = [
('Elite Championship Team', 10.5),
('Strong Playoff Team', 6.2),
('Average Team', 0.0),
('Lottery Team', -8.3)
]
for team_name, net_rtg in teams:
projection = predict_wins_from_net_rating(net_rtg)
print(f"\n{team_name} (NetRtg: {net_rtg:+.1f})")
print(f" Projected Record: {projection['Projected_Wins']}-{projection['Projected_Losses']}")
print(f" Win%: {projection['Win_Percentage']:.1%}")
```
### R: Net Rating Analysis and Visualization
```r
library(tidyverse)
library(ggplot2)
# Calculate team net rating
calculate_net_rating <- function(points_scored, points_allowed, possessions) {
ortg <- (points_scored / possessions) * 100
drtg <- (points_allowed / possessions) * 100
net_rtg <- ortg - drtg
return(list(
ORtg = round(ortg, 1),
DRtg = round(drtg, 1),
NetRtg = round(net_rtg, 1)
))
}
# Example data
games <- tibble(
game = 1:10,
points_scored = c(112, 108, 115, 103, 110, 118, 106, 113, 109, 116),
points_allowed = c(105, 110, 108, 98, 106, 112, 103, 109, 111, 108),
possessions = c(98, 102, 100, 95, 99, 103, 97, 101, 100, 102)
)
# Calculate cumulative net rating
games <- games %>%
mutate(
cum_points_scored = cumsum(points_scored),
cum_points_allowed = cumsum(points_allowed),
cum_possessions = cumsum(possessions),
ortg = (cum_points_scored / cum_possessions) * 100,
drtg = (cum_points_allowed / cum_possessions) * 100,
net_rtg = ortg - drtg
)
# Season totals
season_rating <- calculate_net_rating(
sum(games$points_scored),
sum(games$points_allowed),
sum(games$possessions)
)
print(paste("Season ORtg:", season_rating$ORtg))
print(paste("Season DRtg:", season_rating$DRtg))
print(paste("Season NetRtg:", season_rating$NetRtg))
# Visualize net rating progression
ggplot(games, aes(x = game, y = net_rtg)) +
geom_line(color = "blue", size = 1) +
geom_point(color = "blue", size = 2) +
geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
labs(
title = "Team Net Rating Progression",
x = "Game Number",
y = "Net Rating (per 100 possessions)",
caption = "Cumulative Net Rating over season"
) +
theme_minimal()
```
### R: Net Rating vs Wins Correlation
```r
# Simulate NBA season data
set.seed(123)
n_teams <- 30
nba_teams <- tibble(
team = paste("Team", 1:n_teams),
net_rating = rnorm(n_teams, mean = 0, sd = 5),
# Add some noise to make it realistic
wins = round(41 + (net_rating * 2.7) + rnorm(n_teams, 0, 3))
) %>%
mutate(
wins = pmin(pmax(wins, 10), 72), # Bound wins between 10 and 72
win_pct = wins / 82
)
# Calculate correlation
correlation <- cor(nba_teams$net_rating, nba_teams$win_pct)
r_squared <- correlation^2
# Linear model
model <- lm(win_pct ~ net_rating, data = nba_teams)
summary(model)
# Visualization
ggplot(nba_teams, aes(x = net_rating, y = wins)) +
geom_point(size = 3, alpha = 0.6, color = "darkblue") +
geom_smooth(method = "lm", se = TRUE, color = "red", fill = "pink", alpha = 0.2) +
geom_hline(yintercept = 41, linetype = "dashed", alpha = 0.5) +
geom_vline(xintercept = 0, linetype = "dashed", alpha = 0.5) +
annotate(
"text", x = max(nba_teams$net_rating) * 0.7, y = 20,
label = paste0("R² = ", round(r_squared, 3)),
size = 5
) +
labs(
title = "Net Rating vs Wins in NBA",
subtitle = "Strong correlation between efficiency and success",
x = "Net Rating (per 100 possessions)",
y = "Wins (82-game season)",
caption = "Each point represents one team"
) +
theme_minimal() +
theme(plot.title = element_text(face = "bold", size = 14))
```
### R: Four Factors by Net Rating Tier
```r
# Analyze how teams in different Net Rating tiers perform
nba_teams <- nba_teams %>%
mutate(
tier = case_when(
net_rating >= 7 ~ "Elite (+7 or better)",
net_rating >= 3 ~ "Good (+3 to +7)",
net_rating >= -3 ~ "Average (-3 to +3)",
TRUE ~ "Poor (below -3)"
),
tier = factor(tier, levels = c("Elite (+7 or better)", "Good (+3 to +7)",
"Average (-3 to +3)", "Poor (below -3)"))
)
# Summary by tier
tier_summary <- nba_teams %>%
group_by(tier) %>%
summarise(
n_teams = n(),
avg_net_rating = mean(net_rating),
avg_wins = mean(wins),
min_wins = min(wins),
max_wins = max(wins)
)
print(tier_summary)
# Visualize distribution
ggplot(nba_teams, aes(x = tier, y = net_rating, fill = tier)) +
geom_boxplot(alpha = 0.7) +
geom_jitter(width = 0.2, alpha = 0.5) +
scale_fill_brewer(palette = "RdYlGn") +
labs(
title = "Net Rating Distribution by Team Tier",
x = "Team Tier",
y = "Net Rating"
) +
theme_minimal() +
theme(legend.position = "none",
axis.text.x = element_text(angle = 15, hjust = 1))
```
## Advanced Concepts
### Garbage Time Adjustment
Net Rating can be inflated or deflated by garbage time (when games are decided). Advanced metrics filter out possessions when the game margin exceeds a threshold in the final minutes.
```python
def filter_garbage_time(df, time_threshold=4, margin_threshold=10):
"""
Remove garbage time possessions from Net Rating calculation.
Parameters:
df: DataFrame with 'time_remaining', 'margin' columns
time_threshold: Minutes remaining when game is considered decided
margin_threshold: Point margin that defines a decided game
Returns:
Filtered DataFrame
"""
return df[
~((df['time_remaining'] <= time_threshold) &
(abs(df['margin']) >= margin_threshold))
]
```
### Clutch Net Rating
Net Rating in clutch situations (score within 5 points, last 5 minutes) reveals team performance under pressure:
```python
def calculate_clutch_net_rating(df):
"""Calculate Net Rating in clutch situations only."""
clutch_df = df[
(df['time_remaining'] <= 5) &
(abs(df['score_diff']) <= 5)
]
return calculate_team_net_rating(clutch_df)
```
## Limitations
1. **Sample Size Dependency**: Small samples produce unreliable ratings
2. **Context Blind**: Doesn't account for opponent strength, injuries, rest
3. **Luck Factor**: Short-term ratings influenced by shooting variance
4. **Lineup Effects**: Individual ratings heavily influenced by teammates
5. **Not Predictive of Close Games**: Expected wins assume average luck in close games
## Best Practices
1. **Use Large Samples**: Minimum 500-1000 possessions for player ratings
2. **Adjust for Context**: Consider strength of schedule and lineup combinations
3. **Compare with On/Off**: Individual Net Rating should be evaluated with on/off splits
4. **Track Trends**: Watch how Net Rating changes over time
5. **Complement with Other Metrics**: Use alongside Win Shares, BPM, and individual stats
## Conclusion
Net Rating is the foundational metric in basketball analytics, providing a pace-adjusted measure of team and player effectiveness. Its strong correlation with wins (R² > 0.90) makes it essential for evaluating team quality, and its simplicity makes it accessible to analysts at all levels.
While individual Net Rating requires careful interpretation due to lineup and opponent effects, team Net Rating remains one of the most reliable indicators of success in basketball. Combined with supporting metrics and proper context, Net Rating is an indispensable tool for understanding the game.
Discussion
Have questions or feedback? Join our community discussion on
Discord or
GitHub Discussions.
Table of Contents
Related Topics
Quick Actions