Chapter 19: Exercises - Player Performance Forecasting

Exercise Overview

These exercises build progressively from basic projection methods to complete player forecasting systems.


Level 1: Conceptual Foundation

Exercise 1.1: Regression to the Mean

Task: A quarterback throws for 9.2 yards per attempt in his first season with 350 attempts. The league average is 7.5 yards per attempt.

  1. If his true talent is league average, what would we expect from luck alone?
  2. Using a stability factor of 0.4, what is his projected Y/A for next season?
  3. How would your projection change if he had 600 attempts instead?

Exercise 1.2: Sample Size Effects

Task: Compare these two running backs:

Player Carries Yards/Carry
RB A 50 6.2
RB B 200 5.1
  1. Which player has the more reliable sample?
  2. If league average is 4.3 Y/C, project each player's "true" Y/C
  3. Explain why the higher per-carry average might project lower

Exercise 1.3: Marcel Weights

Task: A receiver has these three seasons:

Year Targets Yards/Target
2023 100 9.5
2022 80 8.2
2021 40 7.0

Using Marcel weights (5/4/3): 1. Calculate the weighted yards per target 2. Apply 15% regression to league average (8.0) 3. Project total yards for 90 targets next season


Level 2: Building Projection Models

Exercise 2.1: Regression to Mean Projector

Task: Implement a basic regression projector.

class SimpleProjector:
    """
    Implement regression to the mean projection.
    """

    def __init__(self, league_averages: Dict[str, float]):
        """
        Parameters:
        -----------
        league_averages : Dict[str, float]
            League average for each stat
        """
        self.league_avg = league_averages

    def calculate_reliability(self, sample_size: int,
                             stabilization_point: int) -> float:
        """
        Calculate reliability weight.

        reliability = sample_size / (sample_size + stabilization_point)

        Parameters:
        -----------
        sample_size : int
            Number of opportunities
        stabilization_point : int
            Point at which stat is 50% reliable

        Returns:
        --------
        float : Reliability between 0 and 1
        """
        # YOUR CODE HERE
        pass

    def project(self, observed_rate: float, sample_size: int,
               stat_name: str, stabilization: int = 300) -> float:
        """
        Project a rate statistic.

        projected = reliability * observed + (1 - reliability) * league_avg

        Returns:
        --------
        float : Projected rate
        """
        # YOUR CODE HERE
        pass

Exercise 2.2: Marcel Projector

Task: Implement Marcel-style projections.

class MarcelProjector:
    """
    Marcel projection using multi-year weighted data.
    """

    def __init__(self, league_averages: Dict[str, float],
                 weights: Tuple[float, float, float] = (5, 4, 3)):
        self.league_avg = league_averages
        self.weights = weights

    def calculate_weighted_stat(self, seasons: List[Dict],
                               stat_name: str,
                               opp_name: str) -> Tuple[float, float]:
        """
        Calculate weighted average of a stat.

        Parameters:
        -----------
        seasons : List[Dict]
            List of season data (most recent first)
        stat_name : str
            Name of rate stat
        opp_name : str
            Name of opportunity stat (attempts, targets)

        Returns:
        --------
        Tuple[float, float] : (weighted_rate, total_weighted_opportunities)
        """
        # YOUR CODE HERE
        pass

    def project(self, seasons: List[Dict],
               regression_opportunities: int = 1200) -> Dict[str, float]:
        """
        Generate Marcel projection.

        1. Calculate weighted stats from past seasons
        2. Determine reliability based on weighted opportunities
        3. Regress toward league average

        Returns:
        --------
        Dict[str, float] : Projected statistics
        """
        # YOUR CODE HERE
        pass

Exercise 2.3: Confidence Intervals

Task: Add uncertainty quantification to projections.

def calculate_projection_interval(projected_value: float,
                                  sample_size: int,
                                  n_seasons: int,
                                  stat_variance: float) -> Tuple[float, float]:
    """
    Calculate 90% confidence interval for projection.

    Use the formula:
    interval_width = stat_variance * sqrt(1/sample_size + 1/n_baseline)

    Parameters:
    -----------
    projected_value : float
        Central projection
    sample_size : int
        Historical opportunities
    n_seasons : int
        Number of seasons of data
    stat_variance : float
        Historical variance of this stat

    Returns:
    --------
    Tuple[float, float] : (lower_bound, upper_bound) for 90% CI
    """
    # YOUR CODE HERE
    pass

Level 3: Position-Specific Models

Exercise 3.1: Quarterback Projector

Task: Build a complete QB projection model.

class QBProjector:
    """
    Quarterback projection model.
    """

    def __init__(self):
        self.league_avg = {
            'completion_pct': 0.62,
            'yards_per_attempt': 7.5,
            'td_rate': 0.045,
            'int_rate': 0.025
        }

        # Stabilization points (attempts needed for 50% reliability)
        self.stabilization = {
            'completion_pct': 200,
            'yards_per_attempt': 300,
            'td_rate': 500,
            'int_rate': 400
        }

    def project_efficiency(self, past_seasons: List[Dict]) -> Dict[str, float]:
        """
        Project QB efficiency metrics.

        Steps:
        1. Weight past seasons (Marcel style)
        2. Calculate reliability for each stat
        3. Regress appropriately
        4. Return projected rates

        Returns:
        --------
        Dict[str, float] : Projected efficiency rates
        """
        # YOUR CODE HERE
        pass

    def project_volume(self, efficiency: Dict[str, float],
                      games: int, dropbacks_per_game: float) -> Dict[str, int]:
        """
        Convert efficiency to counting stats.

        Returns:
        --------
        Dict[str, int] : Projected counting statistics
        """
        # YOUR CODE HERE
        pass

    def full_projection(self, player_data: Dict) -> Dict:
        """
        Generate complete QB projection.

        Returns:
        --------
        Dict : Complete projection with rates, counts, and intervals
        """
        # YOUR CODE HERE
        pass

Exercise 3.2: Running Back Projector

Task: Build RB projection handling both rushing and receiving.

class RBProjector:
    """
    Running back projection model.

    Must handle:
    - Rushing (yards per carry, TDs)
    - Receiving (targets, catches, yards)
    - Usage projection uncertainty
    """

    def __init__(self):
        # YOUR CODE: Define league averages and stabilization points
        pass

    def project_rushing(self, past_seasons: List[Dict]) -> Dict[str, float]:
        """Project rushing efficiency."""
        # YOUR CODE HERE
        pass

    def project_receiving(self, past_seasons: List[Dict]) -> Dict[str, float]:
        """Project receiving efficiency."""
        # YOUR CODE HERE
        pass

    def project_usage(self, past_seasons: List[Dict],
                     team_context: Dict = None) -> Dict[str, int]:
        """
        Project carries and targets.

        Consider:
        - Historical usage
        - Team context (committee vs bell cow)
        - Age/injury history
        """
        # YOUR CODE HERE
        pass

    def full_projection(self, player_data: Dict) -> Dict:
        """Generate complete RB projection."""
        # YOUR CODE HERE
        pass

Level 4: Advanced Techniques

Exercise 4.1: Aging Curves

Task: Implement position-specific aging adjustments.

class AgingModel:
    """
    Model player aging by position.
    """

    def __init__(self):
        # Define aging curves
        self.curves = {
            'QB': {'peak_age': 28, 'decline_rate': 0.015},
            'RB': {'peak_age': 25, 'decline_rate': 0.05},
            'WR': {'peak_age': 27, 'decline_rate': 0.025},
            'TE': {'peak_age': 28, 'decline_rate': 0.02}
        }

    def get_adjustment(self, position: str, current_age: int,
                      target_age: int) -> float:
        """
        Calculate aging adjustment factor.

        Before peak: Small improvement expected
        At peak: Factor = 1.0
        After peak: Decline by position-specific rate

        Returns:
        --------
        float : Adjustment factor (multiply projection by this)
        """
        # YOUR CODE HERE
        pass

    def project_multi_year(self, current_performance: float,
                          position: str, current_age: int,
                          years: int) -> pd.DataFrame:
        """
        Project performance over multiple years.

        Returns:
        --------
        pd.DataFrame : Year-by-year projected performance
        """
        # YOUR CODE HERE
        pass

Exercise 4.2: Comparable Players

Task: Find and use historical comparables.

class ComparablesProjector:
    """
    Project using historical comparable players.
    """

    def __init__(self, historical_db: pd.DataFrame):
        """
        Parameters:
        -----------
        historical_db : pd.DataFrame
            Historical player-seasons with outcomes
        """
        self.historical = historical_db
        self.scaler = StandardScaler()
        self._fit_model()

    def _fit_model(self):
        """Prepare the comparables model."""
        # YOUR CODE: Fit a nearest neighbors model on historical data
        pass

    def find_comparables(self, player_profile: Dict,
                        n: int = 10) -> pd.DataFrame:
        """
        Find n most similar historical players.

        Parameters:
        -----------
        player_profile : Dict
            Current player's statistics
        n : int
            Number of comparables

        Returns:
        --------
        pd.DataFrame : Similar players with similarity scores
        """
        # YOUR CODE HERE
        pass

    def project_from_comparables(self, player_profile: Dict,
                                 target_season: int = 1) -> Dict:
        """
        Project based on what similar players did.

        Parameters:
        -----------
        player_profile : Dict
            Current player statistics
        target_season : int
            Years ahead to project (1 = next season)

        Returns:
        --------
        Dict : Projection based on comparable outcomes
        """
        # YOUR CODE HERE
        pass

Level 5: Complete System

Exercise 5.1: Integrated Projection System

Task: Build a complete player projection pipeline.

class PlayerProjectionSystem:
    """
    Complete player projection system.

    Features:
    - Position-specific models
    - Aging adjustments
    - Comparable players
    - Confidence intervals
    - Uncertainty quantification
    """

    def __init__(self, historical_data: pd.DataFrame):
        """Initialize all component models."""
        # YOUR CODE: Initialize position projectors, aging model, comparables
        pass

    def project_player(self, player_data: Dict,
                      projection_years: int = 1) -> Dict:
        """
        Generate complete projection for any player.

        Returns:
        --------
        Dict with:
        - central_projection: Point estimates
        - confidence_intervals: 90% CIs
        - comparable_players: Similar historical players
        - risk_factors: Potential concerns
        - upside_factors: Potential for outperformance
        """
        # YOUR CODE HERE
        pass

    def batch_project(self, players: List[Dict]) -> pd.DataFrame:
        """Project multiple players."""
        # YOUR CODE HERE
        pass

    def evaluate_projections(self, past_projections: pd.DataFrame,
                            actual_results: pd.DataFrame) -> Dict:
        """
        Evaluate projection accuracy.

        Returns:
        --------
        Dict with:
        - mean_absolute_error
        - correlation
        - calibration (are 90% CIs correct?)
        - bias (over/under projecting?)
        """
        # YOUR CODE HERE
        pass

Exercise 5.2: Fantasy Football Projector

Task: Create fantasy-specific projections.

class FantasyProjector:
    """
    Fantasy football projection system.

    Scoring settings:
    - Passing: 0.04 pts/yard, 4 pts/TD, -2 pts/INT
    - Rushing: 0.1 pts/yard, 6 pts/TD
    - Receiving: 0.1 pts/yard, 6 pts/TD, 0.5 PPR
    """

    def __init__(self, base_projector: PlayerProjectionSystem):
        self.base = base_projector
        self.scoring = {
            'pass_yard': 0.04,
            'pass_td': 4,
            'interception': -2,
            'rush_yard': 0.1,
            'rush_td': 6,
            'rec_yard': 0.1,
            'rec_td': 6,
            'reception': 0.5
        }

    def project_fantasy_points(self, player_data: Dict) -> Dict:
        """
        Project fantasy points with variance.

        Returns:
        --------
        Dict with:
        - projected_points: Central estimate
        - floor: 10th percentile outcome
        - ceiling: 90th percentile outcome
        - position_rank: Projected rank at position
        - value_over_replacement: VORP
        """
        # YOUR CODE HERE
        pass

    def generate_rankings(self, player_pool: List[Dict],
                         format_type: str = 'ppr') -> pd.DataFrame:
        """
        Generate full fantasy rankings.

        Returns:
        --------
        pd.DataFrame : Ranked players with projections
        """
        # YOUR CODE HERE
        pass

    def auction_values(self, rankings: pd.DataFrame,
                      budget: int = 200,
                      roster_size: int = 15) -> pd.DataFrame:
        """
        Calculate auction values from projections.
        """
        # YOUR CODE HERE
        pass

Bonus Challenge: Breakout Prediction

Task: Build a model to identify players likely to break out.

A "breakout" is when a player significantly exceeds prior performance. Build a classifier that predicts breakouts before they happen.

class BreakoutPredictor:
    """
    Predict which players will break out.

    Breakout defined as:
    - 50%+ improvement in fantasy points
    - Or moving from outside top 50 to inside top 20

    Features to consider:
    - Age (young players break out more)
    - Usage trend (increasing targets/carries)
    - Efficiency trend (improving rates)
    - Opportunity (starter leaves, draft capital)
    - Historical breakout rate by profile
    """

    # YOUR IMPLEMENTATION HERE
    pass

Submission Guidelines

For each exercise: 1. Include complete, runnable code 2. Add docstrings and comments 3. Provide test cases with expected outputs 4. Include sample output demonstrating functionality

For Level 4-5 exercises: - Include evaluation metrics - Document assumptions - Compare to baseline approaches - Provide visualization of results