The Four Factors Framework
Dean Oliver's Four Factors represent the most influential analytical framework in basketball history. By identifying the key determinants of team success—effective field goal percentage, turnover rate, offensive/defensive rebounding rate, and free throw rate—Oliver provided a structure for understanding what actually drives winning basketball.
Factor Weights and Importance
Oliver's original research suggested approximate weights: eFG% matters most (~40% of variance), followed by turnover rate (~25%), rebounding rate (~20%), and free throw rate (~15%). These weights vary somewhat by sample and era, but shooting efficiency consistently emerges as the most important factor.
def calculate_four_factors(team_stats):
"""Calculate Dean Oliver's Four Factors"""
efg_pct = (team_stats['FGM'] + 0.5 * team_stats['FG3M']) / team_stats['FGA']
tov_pct = team_stats['TOV'] / (team_stats['FGA'] + 0.44 * team_stats['FTA'] + team_stats['TOV'])
orb_pct = team_stats['OREB'] / (team_stats['OREB'] + team_stats['OPP_DREB'])
ft_rate = team_stats['FTM'] / team_stats['FGA']
return {
'eFG_PCT': round(efg_pct * 100, 1),
'TOV_PCT': round(tov_pct * 100, 1),
'ORB_PCT': round(orb_pct * 100, 1),
'FT_RATE': round(ft_rate * 100, 1)
}
Practical Application
Four Factors analysis enables diagnostic evaluation of team performance. A team struggling offensively can examine which factors are below average to identify improvement areas. A team overperforming expectations might be unsustainably excellent in one factor that will regress. The framework provides actionable structure for performance analysis.
Offensive vs. Defensive Factors
Teams typically have different profiles on offense and defense. A team might emphasize three-point shooting on offense while defending the rim on defense. Comparing factor profiles across phases reveals strategic identity and helps predict how teams will match up against specific opponents.
Implementation in R
# Four Factors analysis and visualization
library(tidyverse)
library(ggplot2)
analyze_four_factors <- function(team_stats) {
team_stats %>%
mutate(
# Calculate all four factors
efg_pct = (fgm + 0.5 * fg3m) / fga,
tov_pct = tov / (fga + 0.44 * fta + tov),
orb_pct = oreb / (oreb + opp_dreb),
ft_rate = ftm / fga,
# Defensive four factors
opp_efg_pct = (opp_fgm + 0.5 * opp_fg3m) / opp_fga,
opp_tov_pct = opp_tov / (opp_fga + 0.44 * opp_fta + opp_tov),
drb_pct = dreb / (dreb + opp_oreb),
opp_ft_rate = opp_ftm / opp_fga,
# Net factors
net_efg = efg_pct - opp_efg_pct,
net_tov = opp_tov_pct - tov_pct,
net_reb = orb_pct - (1 - drb_pct),
net_ft = ft_rate - opp_ft_rate
)
}
team_stats <- read_csv("team_stats.csv")
four_factors <- analyze_four_factors(team_stats)
# Visualize four factors
four_factors_long <- four_factors %>%
select(team_name, net_efg, net_tov, net_reb, net_ft) %>%
pivot_longer(-team_name, names_to = "factor", values_to = "value")
ggplot(four_factors_long, aes(x = reorder(team_name, -value),
y = value, fill = factor)) +
geom_bar(stat = "identity", position = "dodge") +
coord_flip() +
labs(title = "Net Four Factors by Team") +
theme_minimal()
Implementation in R
# Four Factors analysis and visualization
library(tidyverse)
library(ggplot2)
analyze_four_factors <- function(team_stats) {
team_stats %>%
mutate(
# Calculate all four factors
efg_pct = (fgm + 0.5 * fg3m) / fga,
tov_pct = tov / (fga + 0.44 * fta + tov),
orb_pct = oreb / (oreb + opp_dreb),
ft_rate = ftm / fga,
# Defensive four factors
opp_efg_pct = (opp_fgm + 0.5 * opp_fg3m) / opp_fga,
opp_tov_pct = opp_tov / (opp_fga + 0.44 * opp_fta + opp_tov),
drb_pct = dreb / (dreb + opp_oreb),
opp_ft_rate = opp_ftm / opp_fga,
# Net factors
net_efg = efg_pct - opp_efg_pct,
net_tov = opp_tov_pct - tov_pct,
net_reb = orb_pct - (1 - drb_pct),
net_ft = ft_rate - opp_ft_rate
)
}
team_stats <- read_csv("team_stats.csv")
four_factors <- analyze_four_factors(team_stats)
# Visualize four factors
four_factors_long <- four_factors %>%
select(team_name, net_efg, net_tov, net_reb, net_ft) %>%
pivot_longer(-team_name, names_to = "factor", values_to = "value")
ggplot(four_factors_long, aes(x = reorder(team_name, -value),
y = value, fill = factor)) +
geom_bar(stat = "identity", position = "dodge") +
coord_flip() +
labs(title = "Net Four Factors by Team") +
theme_minimal()