WNBA Efficiency Metrics

Beginner 10 min read 1 views Nov 27, 2025

WNBA Efficiency Metrics: Advanced Statistical Analysis

Efficiency metrics in the WNBA measure player and team performance per possession or per minute, accounting for the league's unique pace, shot distribution, and strategic differences from the NBA. This guide explores advanced efficiency calculations adapted specifically for women's basketball.

Core Efficiency Metrics for WNBA

1. True Shooting Percentage (TS%)

True Shooting adjusts for three-pointers and free throws to provide a comprehensive shooting efficiency metric:

TS% = PTS / (2 × TSA)
where TSA = FGA + 0.44 × FTA
WNBA Benchmarks (2024):
  • Elite: 60%+ (A'ja Wilson: 61.2%)
  • Above Average: 55-60%
  • Average: 50-55%
  • Below Average: <50%

2. Player Efficiency Rating (PER)

PER is a comprehensive per-minute rating developed by John Hollinger, adjusted for WNBA pace:

uPER = (1/MP) × [3PM + (2/3)×AST + (2-factor×(team_AST/team_FG))×FG + (FT×0.5×(1+(1-(team_AST/team_FG))+(2/3)×(team_AST/team_FG))) - VOP×TO - VOP×DRBP×(FGA-FG) - VOP×0.44×(0.44+(0.56×DRBP))×(FTA-FT) + VOP×(1-DRBP)×(TRB-ORB) + VOP×DRBP×ORB + VOP×STL + VOP×DRBP×BLK - PF×((lgFT/lgPF)-0.44×(lgFTA/lgPF)×VOP)]
WNBA PER Benchmarks:
  • MVP Level: 25+ (A'ja Wilson: 31.5 in 2024)
  • All-Star: 20-25
  • Starter Quality: 15-20
  • League Average: 15.0 (by definition)
  • Role Player: 10-15

3. Offensive & Defensive Rating

Points produced or allowed per 100 possessions:

Offensive Rating (ORtg) = 100 × (Points Produced / Possessions)

Defensive Rating (DRtg) = 100 × (Opponent Points / Possessions)
2024 WNBA League Averages:
  • League ORtg: ~104.5
  • Elite ORtg: 115+ (offensive stars)
  • Elite DRtg: <95 (top defenders)

4. Effective Field Goal Percentage (eFG%)

eFG% = (FG + 0.5 × 3PM) / FGA

Adjusts for the added value of three-point shots. WNBA average: ~48-49%

5. Usage Rate (USG%)

USG% = 100 × [(FGA + 0.44 × FTA + TO) × (Team MP / 5)] / [MP × (Team FGA + 0.44 × Team FTA + Team TO)]

Percentage of team plays used by a player while on the court.

Python Implementation: WNBA Efficiency Analysis

import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import seaborn as sns

class WNBAEfficiencyCalculator:
    """Calculate advanced efficiency metrics for WNBA players"""

    def __init__(self, player_stats, team_stats, league_stats):
        """
        Parameters:
        -----------
        player_stats : DataFrame with player statistics
        team_stats : DataFrame with team statistics
        league_stats : dict with league-wide averages
        """
        self.player_stats = player_stats
        self.team_stats = team_stats
        self.league_stats = league_stats

    def true_shooting_percentage(self, pts, fga, fta):
        """Calculate True Shooting Percentage"""
        tsa = fga + (0.44 * fta)
        if tsa == 0:
            return 0
        return pts / (2 * tsa)

    def effective_fg_percentage(self, fg, threes_made, fga):
        """Calculate Effective Field Goal Percentage"""
        if fga == 0:
            return 0
        return (fg + 0.5 * threes_made) / fga

    def usage_rate(self, fga, fta, tov, mp, team_fga, team_fta,
                   team_tov, team_mp):
        """Calculate Usage Rate"""
        if mp == 0:
            return 0

        player_possessions = fga + (0.44 * fta) + tov
        team_possessions = team_fga + (0.44 * team_fta) + team_tov

        return 100 * ((player_possessions * (team_mp / 5)) /
                      (mp * team_possessions))

    def offensive_rating(self, pts, fga, fta, tov, orb, ast,
                        team_pace, team_fg, team_ast):
        """
        Simplified Offensive Rating calculation
        Based on Dean Oliver's formula adapted for WNBA
        """
        # Calculate scoring possessions
        scoring_poss = (fga - orb + tov + (0.4 * fta))

        if scoring_poss == 0:
            return 0

        # Points produced per possession
        qAST = ((ast * team_fg) / team_ast) if team_ast > 0 else 0
        fg = (fga * self.league_stats['fg_pct'])  # Estimate

        points_produced = (pts + qAST * 0.5)

        # Scale to per 100 possessions
        return 100 * (points_produced / scoring_poss)

    def player_efficiency_rating(self, mp, threes, ast, fg, fga, ft, fta,
                                 orb, drb, stl, blk, pf, tov,
                                 team_stats, league_stats):
        """
        Calculate Player Efficiency Rating (PER)
        Adjusted for WNBA
        """
        if mp == 0:
            return 0

        # League factors
        lg_pace = league_stats['pace']
        lg_ast = league_stats['ast']
        lg_fg = league_stats['fg']
        lg_ft = league_stats['ft']
        lg_fta = league_stats['fta']
        lg_pf = league_stats['pf']
        lg_pts = league_stats['pts']
        lg_fga = league_stats['fga']
        lg_orb = league_stats['orb']
        lg_tov = league_stats['tov']
        lg_drb = league_stats['drb']

        # Team factors
        tm_ast = team_stats['ast']
        tm_fg = team_stats['fg']

        # Calculate VOP (Value of Possession)
        vop = lg_pts / (lg_fga - lg_orb + lg_tov + 0.44 * lg_fta)

        # Calculate DRBP (Defensive Rebound Percentage)
        drbp = (lg_drb) / (lg_drb + lg_orb)

        # Factor
        factor = (2/3) - ((0.5 * lg_ast / lg_fg) / (2 * lg_fg / lg_ft))

        # Unadjusted PER
        uper = (1/mp) * (
            threes +
            (2/3) * ast +
            (2 - factor * (tm_ast/tm_fg)) * fg +
            (ft * 0.5 * (1 + (1 - (tm_ast/tm_fg)) + (2/3) * (tm_ast/tm_fg))) -
            vop * tov -
            vop * drbp * (fga - fg) -
            vop * 0.44 * (0.44 + (0.56 * drbp)) * (fta - ft) +
            vop * (1 - drbp) * (orb + drb) +
            vop * drbp * orb +
            vop * stl +
            vop * drbp * blk -
            pf * ((lg_ft/lg_pf) - 0.44 * (lg_fta/lg_pf) * vop)
        )

        # Pace adjustment
        pace_adj = lg_pace / team_stats['pace']

        # Adjusted PER
        aper = uper * pace_adj

        # Scale to league average of 15
        lg_aper = league_stats['avg_per']
        per = aper * (15 / lg_aper)

        return per

    def calculate_all_metrics(self):
        """Calculate all efficiency metrics for all players"""
        results = []

        for idx, player in self.player_stats.iterrows():
            # Get team stats for this player's team
            team = self.team_stats[
                self.team_stats['team'] == player['team']
            ].iloc[0]

            metrics = {
                'player': player['name'],
                'team': player['team'],
                'ts_pct': self.true_shooting_percentage(
                    player['pts'], player['fga'], player['fta']
                ),
                'efg_pct': self.effective_fg_percentage(
                    player['fg'], player['3pm'], player['fga']
                ),
                'usg_rate': self.usage_rate(
                    player['fga'], player['fta'], player['tov'],
                    player['mp'], team['fga'], team['fta'],
                    team['tov'], team['mp']
                ),
                'ortg': self.offensive_rating(
                    player['pts'], player['fga'], player['fta'],
                    player['tov'], player['orb'], player['ast'],
                    team['pace'], team['fg'], team['ast']
                ),
                'per': self.player_efficiency_rating(
                    player['mp'], player['3pm'], player['ast'],
                    player['fg'], player['fga'], player['ft'],
                    player['fta'], player['orb'], player['drb'],
                    player['stl'], player['blk'], player['pf'],
                    player['tov'], team, self.league_stats
                )
            }

            results.append(metrics)

        return pd.DataFrame(results)

# Example usage with 2024 WNBA data
def analyze_wnba_efficiency():
    """Example analysis of WNBA efficiency metrics"""

    # Sample player data (top performers 2024)
    player_data = pd.DataFrame([
        {
            'name': "A'ja Wilson", 'team': 'LVA', 'mp': 1275,
            'pts': 886, 'fga': 666, 'fta': 313, 'fg': 450,
            '3pm': 14, 'ft': 254, 'ast': 134, 'orb': 120,
            'drb': 430, 'stl': 64, 'blk': 82, 'tov': 112, 'pf': 77
        },
        {
            'name': 'Breanna Stewart', 'team': 'NYL', 'mp': 1312,
            'pts': 832, 'fga': 643, 'fta': 241, 'fg': 415,
            '3pm': 87, 'ft': 191, 'ast': 162, 'orb': 68,
            'drb': 324, 'stl': 51, 'blk': 59, 'tov': 119, 'pf': 62
        },
        {
            'name': 'Napheesa Collier', 'team': 'MIN', 'mp': 1357,
            'pts': 794, 'fga': 611, 'fta': 200, 'fg': 422,
            '3pm': 42, 'ft': 157, 'ast': 159, 'orb': 91,
            'drb': 338, 'stl': 73, 'blk': 45, 'tov': 85, 'pf': 61
        }
    ])

    # Team stats (simplified)
    team_data = pd.DataFrame([
        {'team': 'LVA', 'pace': 83.5, 'fga': 2589, 'fta': 798,
         'tov': 463, 'mp': 9680, 'fg': 1745, 'ast': 694},
        {'team': 'NYL', 'pace': 85.2, 'fga': 2678, 'fta': 745,
         'tov': 481, 'mp': 9680, 'fg': 1789, 'ast': 712},
        {'team': 'MIN', 'pace': 84.1, 'fga': 2634, 'fta': 721,
         'tov': 458, 'mp': 9680, 'fg': 1768, 'ast': 703}
    ])

    # League averages (2024 estimates)
    league_stats = {
        'pace': 84.3,
        'pts': 83.6,
        'fga': 64.2,
        'fg': 26.8,
        'fg_pct': 0.442,
        'fta': 19.8,
        'ft': 14.9,
        'ast': 19.2,
        'orb': 8.9,
        'drb': 25.4,
        'tov': 13.8,
        'pf': 18.6,
        'avg_per': 15.0
    }

    # Calculate metrics
    calculator = WNBAEfficiencyCalculator(
        player_data, team_data, league_stats
    )
    efficiency_metrics = calculator.calculate_all_metrics()

    print("WNBA Efficiency Metrics - 2024 Top Performers")
    print("=" * 70)
    print(efficiency_metrics.to_string(index=False))

    # Visualize efficiency comparison
    fig, axes = plt.subplots(2, 2, figsize=(14, 10))

    # TS% comparison
    axes[0, 0].barh(efficiency_metrics['player'],
                     efficiency_metrics['ts_pct'] * 100)
    axes[0, 0].set_xlabel('True Shooting %')
    axes[0, 0].set_title('True Shooting Percentage')
    axes[0, 0].axvline(x=55, color='r', linestyle='--',
                       label='League Avg')
    axes[0, 0].legend()

    # PER comparison
    axes[0, 1].barh(efficiency_metrics['player'],
                     efficiency_metrics['per'])
    axes[0, 1].set_xlabel('Player Efficiency Rating')
    axes[0, 1].set_title('Player Efficiency Rating (PER)')
    axes[0, 1].axvline(x=15, color='r', linestyle='--',
                       label='League Avg')
    axes[0, 1].legend()

    # Usage vs Efficiency scatter
    axes[1, 0].scatter(efficiency_metrics['usg_rate'],
                       efficiency_metrics['ts_pct'] * 100, s=100)
    for idx, row in efficiency_metrics.iterrows():
        axes[1, 0].annotate(row['player'],
                           (row['usg_rate'], row['ts_pct'] * 100),
                           fontsize=8, ha='right')
    axes[1, 0].set_xlabel('Usage Rate %')
    axes[1, 0].set_ylabel('True Shooting %')
    axes[1, 0].set_title('Usage vs Efficiency')
    axes[1, 0].grid(True, alpha=0.3)

    # Offensive Rating
    axes[1, 1].barh(efficiency_metrics['player'],
                     efficiency_metrics['ortg'])
    axes[1, 1].set_xlabel('Offensive Rating')
    axes[1, 1].set_title('Offensive Rating (per 100 poss)')
    axes[1, 1].axvline(x=104.5, color='r', linestyle='--',
                       label='League Avg')
    axes[1, 1].legend()

    plt.tight_layout()
    plt.savefig('wnba_efficiency_analysis.png', dpi=300,
                bbox_inches='tight')
    print("\nVisualization saved as 'wnba_efficiency_analysis.png'")

    return efficiency_metrics

if __name__ == "__main__":
    metrics = analyze_wnba_efficiency()

R Implementation: Advanced WNBA Metrics with wehoop

library(wehoop)
library(tidyverse)
library(ggplot2)
library(scales)

# WNBA Efficiency Metrics Calculator
calculate_wnba_efficiency <- function(season = 2024) {

  # Load WNBA player stats
  player_box <- load_wnba_player_box(seasons = season)
  team_box <- load_wnba_team_box(seasons = season)

  # Calculate league averages
  league_stats <- player_box %>%
    summarize(
      avg_pace = mean(pace, na.rm = TRUE),
      avg_pts = mean(pts, na.rm = TRUE),
      avg_fga = mean(fga, na.rm = TRUE),
      avg_fg = mean(fgm, na.rm = TRUE),
      avg_fta = mean(fta, na.rm = TRUE),
      avg_ft = mean(ftm, na.rm = TRUE),
      avg_ast = mean(ast, na.rm = TRUE),
      avg_orb = mean(oreb, na.rm = TRUE),
      avg_drb = mean(dreb, na.rm = TRUE),
      avg_tov = mean(to, na.rm = TRUE),
      avg_pf = mean(pf, na.rm = TRUE)
    )

  # Calculate efficiency metrics
  efficiency_data <- player_box %>%
    filter(min >= 400) %>%  # Minimum 400 minutes
    mutate(
      # True Shooting %
      tsa = fga + 0.44 * fta,
      ts_pct = ifelse(tsa > 0, pts / (2 * tsa), 0),

      # Effective FG%
      efg_pct = ifelse(fga > 0, (fgm + 0.5 * fg3m) / fga, 0),

      # Usage Rate (simplified)
      player_poss = fga + 0.44 * fta + to,
      usg_rate = player_poss / min * 100,

      # Points per shot attempt
      pps = pts / fga,

      # Assist to Turnover Ratio
      ast_to_ratio = ifelse(to > 0, ast / to, ast),

      # Rebound Rate
      reb_rate = (oreb + dreb) / min * 36,

      # Stock Rate (Steals + Blocks)
      stock_rate = (stl + blk) / min * 36
    )

  return(list(
    efficiency = efficiency_data,
    league_avg = league_stats
  ))
}

# Calculate Player Efficiency Rating
calculate_per <- function(player_stats, team_stats, league_stats) {

  player_stats %>%
    left_join(team_stats, by = c("team_id", "season")) %>%
    mutate(
      # Value of Possession
      vop = league_stats$avg_pts /
            (league_stats$avg_fga - league_stats$avg_orb +
             league_stats$avg_tov + 0.44 * league_stats$avg_fta),

      # Defensive Rebound Percentage
      drbp = league_stats$avg_drb /
             (league_stats$avg_drb + league_stats$avg_orb),

      # Factor
      factor = (2/3) - ((0.5 * league_stats$avg_ast / league_stats$avg_fg) /
                        (2 * league_stats$avg_fg / league_stats$avg_ft)),

      # Team AST/FG ratio
      team_ast_fg = team_ast / team_fg,

      # Unadjusted PER
      uper = (1/min) * (
        fg3m +
        (2/3) * ast +
        (2 - factor * team_ast_fg) * fgm +
        (ftm * 0.5 * (1 + (1 - team_ast_fg) + (2/3) * team_ast_fg)) -
        vop * to -
        vop * drbp * (fga - fgm) -
        vop * 0.44 * (0.44 + (0.56 * drbp)) * (fta - ftm) +
        vop * (1 - drbp) * (oreb + dreb) +
        vop * drbp * oreb +
        vop * stl +
        vop * drbp * blk -
        pf * ((league_stats$avg_ft / league_stats$avg_pf) -
              0.44 * (league_stats$avg_fta / league_stats$avg_pf) * vop)
      ),

      # Pace adjustment
      pace_adj = league_stats$avg_pace / team_pace,

      # Adjusted PER
      aper = uper * pace_adj
    ) %>%
    mutate(
      # Scale to league average of 15
      per = aper * (15 / mean(aper, na.rm = TRUE))
    )
}

# Calculate Offensive and Defensive Ratings
calculate_ratings <- function(player_stats, team_stats) {

  player_stats %>%
    left_join(team_stats, by = c("team_id", "season")) %>%
    mutate(
      # Possessions (simplified)
      poss = fga - oreb + to + (0.44 * fta),

      # Offensive Rating (simplified)
      ortg = ifelse(poss > 0, (pts / poss) * 100, 0),

      # Defensive Rating (estimated from team defense)
      # Adjusted by player's defensive stats
      def_contribution = (stl + blk + dreb) / min,
      drtg_adj = team_drtg * (1 - (def_contribution -
                              mean(def_contribution, na.rm = TRUE)) / 10)
    )
}

# Analyze WNBA efficiency leaders
analyze_efficiency_leaders <- function(season = 2024, min_minutes = 400) {

  # Get efficiency data
  wnba_data <- calculate_wnba_efficiency(season)
  efficiency <- wnba_data$efficiency
  league_avg <- wnba_data$league_avg

  # Top performers by metric
  top_ts <- efficiency %>%
    arrange(desc(ts_pct)) %>%
    select(athlete_display_name, team_display_name, min,
           pts, ts_pct, efg_pct) %>%
    head(10)

  cat("\nTop 10 True Shooting % Leaders (min 400 min):\n")
  print(top_ts)

  top_efficiency <- efficiency %>%
    filter(ts_pct >= 0.55, usg_rate >= 20) %>%
    arrange(desc(ts_pct))

  cat("\nHigh Usage, High Efficiency Players (TS% >= 55%, USG >= 20%):\n")
  print(top_efficiency %>%
        select(athlete_display_name, team_display_name,
               ts_pct, usg_rate, pts))

  # Visualizations
  create_efficiency_plots(efficiency, league_avg)

  return(efficiency)
}

# Create comprehensive efficiency visualizations
create_efficiency_plots <- function(data, league_avg) {

  # 1. Usage vs Efficiency scatter
  p1 <- ggplot(data %>% filter(min >= 400),
               aes(x = usg_rate, y = ts_pct * 100)) +
    geom_point(aes(size = pts, color = team_display_name),
               alpha = 0.6) +
    geom_hline(yintercept = 55, linetype = "dashed",
               color = "red", alpha = 0.5) +
    geom_vline(xintercept = 20, linetype = "dashed",
               color = "red", alpha = 0.5) +
    geom_text(aes(label = ifelse(ts_pct > 0.58 | usg_rate > 25,
                                  athlete_display_name, "")),
              hjust = -0.1, vjust = -0.5, size = 3) +
    labs(
      title = "WNBA Usage Rate vs True Shooting %",
      subtitle = paste("2024 Season | Min 400 minutes"),
      x = "Usage Rate %",
      y = "True Shooting %",
      size = "Points",
      color = "Team"
    ) +
    theme_minimal() +
    theme(legend.position = "bottom")

  ggsave("wnba_usage_vs_efficiency.png", p1,
         width = 12, height = 8, dpi = 300)

  # 2. Efficiency distribution
  p2 <- ggplot(data %>% filter(min >= 400),
               aes(x = ts_pct * 100)) +
    geom_histogram(aes(y = ..density..), binwidth = 2,
                   fill = "steelblue", alpha = 0.7) +
    geom_density(color = "darkred", size = 1) +
    geom_vline(xintercept = mean(data$ts_pct * 100, na.rm = TRUE),
               linetype = "dashed", color = "red", size = 1) +
    annotate("text",
             x = mean(data$ts_pct * 100, na.rm = TRUE) + 2,
             y = 0.08,
             label = paste("League Avg:",
                          round(mean(data$ts_pct * 100, na.rm = TRUE), 1), "%"),
             color = "red") +
    labs(
      title = "WNBA True Shooting % Distribution",
      subtitle = "2024 Season | Min 400 minutes",
      x = "True Shooting %",
      y = "Density"
    ) +
    theme_minimal()

  ggsave("wnba_ts_distribution.png", p2,
         width = 10, height = 6, dpi = 300)

  # 3. Multi-metric comparison for top scorers
  top_scorers <- data %>%
    filter(min >= 600) %>%
    arrange(desc(pts)) %>%
    head(15) %>%
    select(athlete_display_name, ts_pct, efg_pct,
           usg_rate, ast_to_ratio) %>%
    pivot_longer(cols = -athlete_display_name,
                 names_to = "metric",
                 values_to = "value")

  p3 <- ggplot(top_scorers, aes(x = athlete_display_name,
                                y = value, fill = metric)) +
    geom_bar(stat = "identity", position = "dodge") +
    coord_flip() +
    scale_fill_brewer(palette = "Set2",
                      labels = c("AST/TO Ratio", "eFG%",
                                "TS%", "Usage Rate")) +
    labs(
      title = "Top 15 Scorers: Multi-Metric Efficiency",
      subtitle = "2024 WNBA Season",
      x = NULL,
      y = "Value",
      fill = "Metric"
    ) +
    theme_minimal() +
    theme(legend.position = "bottom")

  ggsave("wnba_top_scorers_metrics.png", p3,
         width = 12, height = 10, dpi = 300)

  cat("\nVisualization plots saved successfully!\n")
}

# Compare WNBA vs NBA efficiency standards
compare_wnba_nba_efficiency <- function() {

  comparison <- data.frame(
    Metric = c("Average TS%", "Elite TS%", "Average eFG%",
               "Average ORtg", "Average Pace", "Average PER"),
    WNBA_2024 = c("52-54%", "60%+", "48-49%", "104-105",
                  "83-85", "15.0"),
    NBA_2024 = c("57-58%", "65%+", "54-55%", "114-116",
                 "99-100", "15.0"),
    Difference = c("-5%", "-5%", "-6%", "-10 pts",
                   "-16 poss", "Same (scaled)")
  )

  print(comparison)

  cat("\nKey Differences:\n")
  cat("1. Lower shooting efficiency due to shorter shot clock (24s)\n")
  cat("2. Slower pace creates different rhythm and shot selection\n")
  cat("3. Different three-point line distance (22'1.75\" vs 23'9\")\n")
  cat("4. Lower overall scoring environment affects rating scales\n")
  cat("5. PER scaled to 15.0 in both leagues for comparison\n")
}

# Main execution
if (interactive()) {

  cat("WNBA Advanced Efficiency Metrics Analysis\n")
  cat("==========================================\n\n")

  # Analyze 2024 season
  efficiency_data <- analyze_efficiency_leaders(season = 2024)

  # Compare to NBA
  cat("\n\nWNBA vs NBA Efficiency Comparison:\n")
  cat("===================================\n")
  compare_wnba_nba_efficiency()

  # Summary statistics
  cat("\n\nSummary Statistics:\n")
  cat("===================\n")
  summary(efficiency_data %>%
          select(ts_pct, efg_pct, usg_rate, ast_to_ratio))
}

2024 WNBA Efficiency Leaders

True Shooting Percentage (min 400 minutes)

Rank Player Team TS% PPG eFG%
1 A'ja Wilson Las Vegas 61.2% 26.9 56.8%
2 Jonquel Jones New York 60.1% 14.2 55.3%
3 DeWanna Bonner Connecticut 58.9% 14.7 54.1%
4 Napheesa Collier Minnesota 58.3% 20.2 54.7%
5 Breanna Stewart New York 57.8% 20.4 52.6%

Player Efficiency Rating (PER) Leaders

Rank Player Team PER Usage% ORtg
1 A'ja Wilson Las Vegas 31.5 29.8 118.4
2 Napheesa Collier Minnesota 25.9 25.2 115.7
3 Breanna Stewart New York 24.8 27.1 113.9
4 Arike Ogunbowale Dallas 22.3 32.4 107.2
5 Sabrina Ionescu New York 21.7 24.6 112.4

WNBA vs NBA Efficiency Differences

Shooting Efficiency

  • WNBA TS% Average: 52-54%
  • NBA TS% Average: 57-58%
  • Gap: ~5 percentage points

Reasons: Shorter shot clock (24s vs 24s), different ball size, shorter three-point line in corners

Pace Factors

  • WNBA Pace: 83-85 possessions/40 min
  • NBA Pace: 99-100 possessions/48 min
  • Per-40 Pace Gap: ~13-14 possessions

Impact: Lower pace affects counting stats, requires possession-based adjustments

Three-Point Shooting

  • WNBA 3P%: ~35-36%
  • NBA 3P%: ~36-37%
  • WNBA 3PA Rate: 32% of FGA
  • NBA 3PA Rate: 39% of FGA

Difference: NBA takes more threes, slightly better accuracy

Offensive Rating Scale

  • WNBA Average: 104-105
  • NBA Average: 114-116
  • Elite WNBA: 115+
  • Elite NBA: 122+

Note: Different baseline; compare within league only

Practical Applications

1. Player Evaluation & Scouting

Use Case: Identifying efficient scorers beyond raw points

  • Compare TS% among players with similar usage rates
  • Look for high-efficiency, low-usage players for role identification
  • Evaluate scoring efficiency relative to shot difficulty
# Find high-efficiency role players
role_players = efficiency_df[
    (efficiency_df['usg_rate'] < 20) &
    (efficiency_df['ts_pct'] > 0.56)
].sort_values('ts_pct', ascending=False)

2. Lineup Optimization

Use Case: Building balanced offensive lineups

  • Combine high-usage stars with efficient complementary players
  • Balance shooters (high TS%) with playmakers (high AST)
  • Optimize spacing using eFG% from different floor areas
# Optimal lineup balance
high_usage = efficiency_df[efficiency_df['usg_rate'] > 25]
high_efficiency = efficiency_df[
    (efficiency_df['ts_pct'] > 0.55) &
    (efficiency_df['usg_rate'].between(15, 22))
]

3. Contract & Salary Analysis

Use Case: Evaluating player value per dollar

  • Calculate PER per salary dollar
  • Identify undervalued efficient players
  • Project future performance based on efficiency trends

4. Coaching Strategy

Use Case: Shot selection and play calling

  • Identify players who maintain efficiency at high usage
  • Optimize late-game possessions for best TS%
  • Adjust offensive sets based on efficiency by play type

5. Fantasy Basketball

Use Case: Drafting and lineup decisions

  • Target players with rising usage and stable efficiency
  • Avoid players with declining TS% trends
  • Identify breakout candidates through efficiency improvements

Advanced Analysis Techniques

1. Efficiency Consistency

Measure game-to-game variability in efficiency:

# Calculate rolling efficiency
player_games['rolling_ts'] = player_games.groupby('player_id')['ts_pct'].transform(
    lambda x: x.rolling(window=10, min_periods=5).mean()
)

# Efficiency standard deviation (consistency metric)
player_consistency = player_games.groupby('player_id').agg({
    'ts_pct': ['mean', 'std'],
    'per': ['mean', 'std']
})

# Lower std = more consistent
player_consistency['ts_consistency'] = (
    player_consistency[('ts_pct', 'mean')] /
    player_consistency[('ts_pct', 'std')]
)

2. Clutch Efficiency

Compare efficiency in clutch situations (last 5 min, score within 5):

# Filter clutch possessions
clutch_stats = player_games[
    (player_games['time_remaining'] <= 300) &
    (player_games['score_diff'].abs() <= 5)
]

# Calculate clutch efficiency
clutch_efficiency = clutch_stats.groupby('player_id').apply(
    lambda x: pd.Series({
        'clutch_ts': calculate_ts(x['pts'].sum(), x['fga'].sum(), x['fta'].sum()),
        'regular_ts': x['ts_pct'].mean(),
        'clutch_games': len(x)
    })
)

clutch_efficiency['clutch_improvement'] = (
    clutch_efficiency['clutch_ts'] - clutch_efficiency['regular_ts']
)

3. Efficiency by Shot Type

Break down efficiency by shot location and type:

# Efficiency by zone
shot_zones = ['rim', 'short_mid', 'long_mid', 'corner_3', 'above_break_3']

zone_efficiency = {}
for zone in shot_zones:
    zone_data = shots_df[shots_df['zone'] == zone]
    zone_efficiency[zone] = {
        'fg_pct': zone_data['made'].mean(),
        'frequency': len(zone_data) / len(shots_df),
        'pts_per_shot': zone_data['points'].mean()
    }

# Identify most efficient shot for each player
player_best_zone = shots_df.groupby(['player_id', 'zone']).agg({
    'made': 'mean',
    'shot_value': 'mean'
}).reset_index().sort_values('shot_value', ascending=False)

4. Usage-Adjusted Efficiency

Account for difficulty increase at higher usage:

# Model efficiency decay with usage
from sklearn.linear_model import LinearRegression

# Fit relationship between usage and efficiency
X = efficiency_df[['usg_rate']].values
y = efficiency_df['ts_pct'].values

model = LinearRegression()
model.fit(X, y)

# Predict expected efficiency at usage rate
efficiency_df['expected_ts'] = model.predict(X)
efficiency_df['ts_above_expected'] = (
    efficiency_df['ts_pct'] - efficiency_df['expected_ts']
)

# Players who maintain efficiency despite high usage
elite_efficiency = efficiency_df[
    (efficiency_df['usg_rate'] > 25) &
    (efficiency_df['ts_above_expected'] > 0.02)
]

Key Insights & Benchmarks

Elite Efficiency Threshold

Players with TS% > 58% at Usage > 25% are rare and extremely valuable

Only ~5% of players achieve this combination

PER Context

WNBA PER typically ranges 8-32, with MVP candidates above 25

A'ja Wilson's 31.5 PER in 2024 was historically dominant

Usage Optimization

Most players see efficiency decline when usage exceeds 28-30%

Stars who maintain 55%+ TS at high usage are franchise cornerstones

Team Success Correlation

Teams with 3+ players above 18 PER win 65%+ of games

Balanced efficiency distribution beats top-heavy talent

Resources & Further Reading

Practice Exercises

  1. Calculate TS% and eFG% for your favorite WNBA player across multiple seasons
  2. Compare efficiency metrics between backcourt and frontcourt players
  3. Identify players whose efficiency improved most with usage changes
  4. Build a "most efficient lineup" using players from different teams
  5. Analyze how efficiency correlates with team win percentage
  6. Create visualizations showing efficiency trends throughout a season
  7. Calculate clutch vs regular season efficiency differences
  8. Model the relationship between pace and team efficiency

Discussion

Have questions or feedback? Join our community discussion on Discord or GitHub Discussions.