Chapter 39 Advanced ~55 min read

Lineup Analysis and Optimization

Evaluating lineup performance and optimizing rotation patterns for maximum team success.

The Lineup Revolution

Every NBA team plays multiple lineups during games, and lineup composition dramatically affects performance. The same players can produce very different results in different combinations. Lineup analysis examines which combinations work, why they work, and how to optimize playing time distribution to maximize wins.

Lineup Plus-Minus

The basic measure of lineup effectiveness is plus-minus: points scored minus points allowed during a lineup's minutes together. However, raw plus-minus carries substantial noise—small samples, opponent variation, and random shooting fluctuation can produce misleading results. Analysts apply various adjustments to extract signal from lineup data.

def analyze_lineup_performance(lineup_data, min_minutes=50):
    """Analyze lineup effectiveness with minimum sample filter"""

    lineup_summary = lineup_data.groupby('LINEUP').agg({
        'PLUS_MINUS': 'sum',
        'MINUTES': 'sum',
        'POSS': 'sum',
        'PTS': 'sum',
        'OPP_PTS': 'sum'
    })

    # Filter to minimum minutes
    lineup_summary = lineup_summary[lineup_summary['MINUTES'] >= min_minutes]

    # Calculate per-100 rates
    lineup_summary['NET_RTG'] = (
        (lineup_summary['PTS'] - lineup_summary['OPP_PTS']) /
        lineup_summary['POSS'] * 100
    )

    return lineup_summary.sort_values('NET_RTG', ascending=False)

Small Sample Challenges

Lineup analysis faces severe small sample problems. Most lineups play only a few hundred possessions per season—far too few for reliable statistical inference. Even starting lineups rarely exceed 1000 possessions. This limitation means lineup data is suggestive rather than definitive, requiring Bayesian thinking that incorporates prior expectations about player quality.

Lineup Synergies and Conflicts

Some player combinations produce results better than individual contributions would predict; others underperform expectations. Identifying these synergies and conflicts helps optimize rotations. Players might synergize through complementary skills (shooter + playmaker), spacing (multiple threats create driving lanes), or defensive compatibility (switching versatility).

Rotation Optimization

Given lineup performance data and player constraints (minutes limits, rest requirements, matchup considerations), rotation optimization seeks the playing time distribution that maximizes expected wins. This is a complex optimization problem that teams increasingly address through analytics. The goal is getting the best lineups on court for the most minutes without exceeding individual player limits.

Implementation in R

# Lineup performance analysis
library(tidyverse)

analyze_lineups <- function(lineup_data) {
  lineup_data %>%
    group_by(lineup_id, players) %>%
    summarise(
      minutes = sum(minutes),
      possessions = sum(possessions),
      plus_minus = sum(plus_minus),
      pts_scored = sum(pts),
      pts_allowed = sum(opp_pts),
      .groups = "drop"
    ) %>%
    mutate(
      net_rtg = round((pts_scored - pts_allowed) / possessions * 100, 1),
      off_rtg = round(pts_scored / possessions * 100, 1),
      def_rtg = round(pts_allowed / possessions * 100, 1)
    ) %>%
    filter(minutes >= 50) %>%
    arrange(desc(net_rtg))
}

lineups <- read_csv("lineup_data.csv")
lineup_analysis <- analyze_lineups(lineups)

# Best lineups
best_lineups <- lineup_analysis %>%
  select(players, minutes, net_rtg, off_rtg, def_rtg) %>%
  head(15)

print(best_lineups)
# Lineup optimization model
library(tidyverse)
library(lpSolve)

optimize_lineup <- function(player_stats, max_minutes = 48 * 5) {
  # Simple optimization: maximize net rating weighted by minutes
  n_players <- nrow(player_stats)

  # Objective: maximize net rating contribution
  objective <- player_stats$net_rtg * player_stats$avg_minutes

  # Constraint: total minutes <= max_minutes
  constraint_matrix <- matrix(player_stats$avg_minutes, nrow = 1)
  constraint_dir <- "<="
  constraint_rhs <- max_minutes

  # Solve
  solution <- lp("max", objective, constraint_matrix,
                 constraint_dir, constraint_rhs, binary.vec = 1:n_players)

  player_stats[solution$solution == 1, ]
}

players <- read_csv("player_net_ratings.csv")
optimal <- optimize_lineup(players)
print(optimal)

Implementation in R

# Lineup performance analysis
library(tidyverse)

analyze_lineups <- function(lineup_data) {
  lineup_data %>%
    group_by(lineup_id, players) %>%
    summarise(
      minutes = sum(minutes),
      possessions = sum(possessions),
      plus_minus = sum(plus_minus),
      pts_scored = sum(pts),
      pts_allowed = sum(opp_pts),
      .groups = "drop"
    ) %>%
    mutate(
      net_rtg = round((pts_scored - pts_allowed) / possessions * 100, 1),
      off_rtg = round(pts_scored / possessions * 100, 1),
      def_rtg = round(pts_allowed / possessions * 100, 1)
    ) %>%
    filter(minutes >= 50) %>%
    arrange(desc(net_rtg))
}

lineups <- read_csv("lineup_data.csv")
lineup_analysis <- analyze_lineups(lineups)

# Best lineups
best_lineups <- lineup_analysis %>%
  select(players, minutes, net_rtg, off_rtg, def_rtg) %>%
  head(15)

print(best_lineups)
# Lineup optimization model
library(tidyverse)
library(lpSolve)

optimize_lineup <- function(player_stats, max_minutes = 48 * 5) {
  # Simple optimization: maximize net rating weighted by minutes
  n_players <- nrow(player_stats)

  # Objective: maximize net rating contribution
  objective <- player_stats$net_rtg * player_stats$avg_minutes

  # Constraint: total minutes <= max_minutes
  constraint_matrix <- matrix(player_stats$avg_minutes, nrow = 1)
  constraint_dir <- "<="
  constraint_rhs <- max_minutes

  # Solve
  solution <- lp("max", objective, constraint_matrix,
                 constraint_dir, constraint_rhs, binary.vec = 1:n_players)

  player_stats[solution$solution == 1, ]
}

players <- read_csv("player_net_ratings.csv")
optimal <- optimize_lineup(players)
print(optimal)
Chapter Summary

You've completed Chapter 39: Lineup Analysis and Optimization.