Chapter 23: Exercises - Network Analysis in Football

Exercise Overview

These exercises progress from basic network construction to advanced analysis of coaching trees and recruiting patterns.


Level 1: Foundational Exercises

Exercise 1.1: Basic Network Construction

Build a simple passing network from play-by-play data.

import networkx as nx
import pandas as pd
import numpy as np

# Sample passing data
np.random.seed(42)
passes = pd.DataFrame({
    'play_id': range(100),
    'passer': ['QB1'] * 100,
    'receiver': np.random.choice(['WR1', 'WR2', 'RB1', 'TE1'], 100,
                                  p=[0.35, 0.30, 0.20, 0.15]),
    'yards': np.random.randint(0, 25, 100),
    'complete': np.random.choice([0, 1], 100, p=[0.35, 0.65])
})

# TODO: Build a directed graph from this data
# - Nodes: QB and receivers
# - Edges: Passes (weight = number of targets)

def build_passing_network(passes: pd.DataFrame) -> nx.DiGraph:
    """Build passing network from play data."""
    # Your code here
    pass

# Test your implementation
network = build_passing_network(passes)
print(f"Nodes: {network.number_of_nodes()}")
print(f"Edges: {network.number_of_edges()}")

Exercise 1.2: Calculate Target Share

Calculate target share for each receiver.

def calculate_target_share(network: nx.DiGraph, qb: str) -> pd.DataFrame:
    """
    Calculate target share for each receiver from a QB.

    Returns DataFrame with:
    - receiver
    - targets
    - target_share (0-1)
    """
    # TODO: Get outgoing edges from QB
    # TODO: Calculate share for each receiver
    pass

# Test
shares = calculate_target_share(network, 'QB1')
print(shares)

Exercise 1.3: Network Visualization

Create a basic visualization of the passing network.

import matplotlib.pyplot as plt

def visualize_passing_network(network: nx.DiGraph) -> plt.Figure:
    """
    Create visualization of passing network.

    Requirements:
    - QB in different color than receivers
    - Edge width proportional to targets
    - Labels on nodes
    """
    fig, ax = plt.subplots(figsize=(10, 8))

    # TODO: Set node positions (QB at center/top)
    # TODO: Color nodes by type
    # TODO: Size edges by weight
    # TODO: Draw network

    return fig

# Test
fig = visualize_passing_network(network)
plt.show()

Level 2: Intermediate Exercises

Exercise 2.1: Centrality Analysis

Calculate and interpret centrality metrics.

class CentralityCalculator:
    """Calculate centrality metrics for passing network."""

    def __init__(self, network: nx.DiGraph):
        self.network = network

    def calculate_all_metrics(self) -> pd.DataFrame:
        """
        Calculate:
        - In-degree centrality (targets received)
        - PageRank (weighted importance)
        - Betweenness centrality
        """
        # TODO: Implement
        pass

    def identify_key_receivers(self, top_n: int = 3) -> list:
        """Identify most central receivers."""
        # TODO: Implement
        pass

# Test with larger network
# Build network from multiple games
# Calculate centrality
# Interpret results

Exercise 2.2: Team Network Comparison

Compare passing network structures across teams.

def compare_team_networks(team_a_passes: pd.DataFrame,
                          team_b_passes: pd.DataFrame) -> dict:
    """
    Compare two teams' passing network structures.

    Return metrics:
    - Number of receivers used
    - Target concentration (HHI)
    - Network density
    - Most targeted receiver share
    """
    # TODO: Build networks for both teams
    # TODO: Calculate comparison metrics
    pass

# Create sample data for two teams
# Compare structures
# Interpret differences

Exercise 2.3: Coaching Tree Builder

Build a basic coaching tree.

class CoachingTreeBuilder:
    """Build coaching relationship networks."""

    def __init__(self):
        self.network = nx.DiGraph()

    def add_relationship(self, mentor: str, mentee: str,
                         years: int, team: str):
        """Add mentor-mentee relationship."""
        # TODO: Add nodes if needed
        # TODO: Add or update edge
        pass

    def get_direct_mentees(self, coach: str) -> list:
        """Get list of direct mentees."""
        # TODO: Implement
        pass

    def get_full_tree(self, root_coach: str, max_depth: int = 3) -> dict:
        """Get full coaching tree structure."""
        # TODO: Implement BFS to get tree
        pass

# Test with sample coaching data
builder = CoachingTreeBuilder()
builder.add_relationship('Coach A', 'Coach B', 3, 'Team 1')
builder.add_relationship('Coach A', 'Coach C', 2, 'Team 1')
builder.add_relationship('Coach B', 'Coach D', 4, 'Team 2')
# Get and visualize tree

Level 3: Advanced Exercises

Exercise 3.1: Recruiting Pipeline Analysis

Analyze recruiting networks to identify pipelines.

class RecruitingPipelineAnalyzer:
    """Analyze recruiting pipelines from high schools to colleges."""

    def __init__(self):
        self.network = nx.DiGraph()

    def build_from_data(self, recruits: pd.DataFrame):
        """
        Build network from recruiting data.

        Expected columns:
        - high_school
        - state
        - college
        - star_rating
        """
        # TODO: Add high school and college nodes
        # TODO: Add edges with recruit count as weight
        pass

    def identify_pipelines(self, college: str,
                           min_recruits: int = 3) -> pd.DataFrame:
        """
        Identify pipeline schools for a college.

        Return DataFrame with:
        - high_school
        - state
        - recruit_count
        - avg_star_rating
        """
        # TODO: Implement
        pass

    def find_recruiting_rivals(self, college: str) -> pd.DataFrame:
        """Find colleges that recruit from same high schools."""
        # TODO: Implement
        pass

    def analyze_geographic_reach(self, college: str) -> dict:
        """Analyze geographic distribution of recruiting."""
        # TODO: Implement
        pass

# Test with synthetic recruiting data
np.random.seed(42)
n_recruits = 500

recruits = pd.DataFrame({
    'player': [f'Player_{i}' for i in range(n_recruits)],
    'high_school': np.random.choice(
        ['HS_' + str(i) for i in range(50)], n_recruits
    ),
    'state': np.random.choice(
        ['TX', 'CA', 'FL', 'GA', 'OH'], n_recruits,
        p=[0.25, 0.20, 0.20, 0.20, 0.15]
    ),
    'college': np.random.choice(
        ['Alabama', 'Ohio State', 'Georgia', 'Clemson', 'Oklahoma'],
        n_recruits, p=[0.25, 0.20, 0.20, 0.20, 0.15]
    ),
    'star_rating': np.random.choice([3, 4, 5], n_recruits, p=[0.50, 0.35, 0.15])
})

analyzer = RecruitingPipelineAnalyzer()
analyzer.build_from_data(recruits)
pipelines = analyzer.identify_pipelines('Alabama')
print(pipelines)

Exercise 3.2: Community Detection in Play Networks

Find clusters of related plays.

class PlayCommunityAnalyzer:
    """Detect communities in play-calling networks."""

    def __init__(self):
        self.network = nx.Graph()

    def build_play_sequence_network(self, plays: pd.DataFrame,
                                     window: int = 2):
        """
        Build network of play sequences.

        Nodes: Play types
        Edges: Co-occurrence within window
        """
        # TODO: Implement
        pass

    def detect_communities(self) -> dict:
        """Detect play communities using Louvain."""
        # TODO: Implement
        pass

    def analyze_community_transitions(self) -> pd.DataFrame:
        """Analyze how often plays transition between communities."""
        # TODO: Implement
        pass

# Create sample play sequence data
plays = pd.DataFrame({
    'game_id': np.repeat(range(10), 80),
    'drive_id': np.repeat(range(100), 8),
    'play_id': range(800),
    'play_type': np.random.choice(
        ['inside_run', 'outside_run', 'short_pass', 'deep_pass',
         'screen', 'play_action'], 800,
        p=[0.20, 0.15, 0.30, 0.15, 0.10, 0.10]
    )
})

analyzer = PlayCommunityAnalyzer()
analyzer.build_play_sequence_network(plays)
communities = analyzer.detect_communities()

Exercise 3.3: Temporal Network Analysis

Analyze how networks change over time.

class TemporalPassingNetworkAnalyzer:
    """Analyze passing network evolution over a season."""

    def __init__(self):
        self.networks_by_week = {}

    def build_weekly_networks(self, plays: pd.DataFrame):
        """Build separate networks for each week."""
        # TODO: Implement
        pass

    def calculate_network_evolution(self) -> pd.DataFrame:
        """
        Track network metrics over time.

        Metrics:
        - Target concentration (weekly)
        - Network density
        - Number of active receivers
        - Top receiver share
        """
        # TODO: Implement
        pass

    def identify_usage_changes(self, player: str) -> pd.DataFrame:
        """Track a player's network position over time."""
        # TODO: Implement
        pass

    def detect_scheme_shifts(self) -> list:
        """Detect weeks where network structure changed significantly."""
        # TODO: Implement
        pass

Level 4: Expert Challenges

Exercise 4.1: Multi-Layer Football Network

Build a multi-layer network combining passing, blocking, and personnel.

class MultiLayerFootballNetwork:
    """
    Multi-layer network combining:
    - Layer 1: Passing connections
    - Layer 2: Blocking assignments
    - Layer 3: Personnel groupings
    """

    def __init__(self):
        self.layers = {
            'passing': nx.DiGraph(),
            'blocking': nx.Graph(),
            'personnel': nx.Graph()
        }

    def build_all_layers(self,
                         passing_data: pd.DataFrame,
                         blocking_data: pd.DataFrame,
                         personnel_data: pd.DataFrame):
        """Build all network layers."""
        # TODO: Implement
        pass

    def calculate_multi_layer_centrality(self) -> pd.DataFrame:
        """Calculate centrality across all layers."""
        # TODO: Combine centrality from each layer
        pass

    def identify_key_connectors(self) -> list:
        """Find players important across multiple layers."""
        # TODO: Implement
        pass

    def analyze_layer_correlations(self) -> pd.DataFrame:
        """Analyze how layers relate to each other."""
        # TODO: Implement
        pass

Exercise 4.2: Coaching Influence Network Analysis

Comprehensive analysis of coaching influence.

class CoachingInfluenceAnalyzer:
    """Analyze how coaching ideas spread through networks."""

    def __init__(self, coaching_network: nx.DiGraph):
        self.network = coaching_network

    def trace_scheme_origins(self, scheme_coaches: list) -> dict:
        """
        Trace the spread of a scheme through coaching tree.

        Given list of coaches known for a scheme,
        find common ancestors and spread patterns.
        """
        # TODO: Implement
        pass

    def calculate_influence_reach(self) -> pd.DataFrame:
        """
        Calculate total influence reach for each coach.

        Metrics:
        - Direct mentees
        - Total descendants
        - Combined winning percentage of tree
        - Championships in tree
        """
        # TODO: Implement
        pass

    def find_coaching_clusters(self) -> dict:
        """Find clusters of related coaching philosophies."""
        # TODO: Implement community detection
        pass

    def predict_hiring_connections(self) -> pd.DataFrame:
        """Predict likely future hires based on network structure."""
        # TODO: Use link prediction
        pass

Exercise 4.3: Complete Network Analytics System

Build a production-ready network analytics system.

class FootballNetworkAnalyticsSystem:
    """
    Complete network analytics system for football.

    Features:
    - Multiple network types
    - Automated analysis
    - Visualization generation
    - Report creation
    """

    def __init__(self):
        self.passing_networks = {}
        self.coaching_network = None
        self.recruiting_network = None
        self.results_cache = {}

    def ingest_season_data(self,
                           plays: pd.DataFrame,
                           coaching: pd.DataFrame,
                           recruiting: pd.DataFrame):
        """Ingest all data and build networks."""
        # TODO: Implement
        pass

    def run_full_analysis(self, team: str) -> dict:
        """Run complete analysis for a team."""
        # TODO: Passing analysis
        # TODO: Coaching tree analysis
        # TODO: Recruiting pipeline analysis
        # TODO: Combine results
        pass

    def generate_network_report(self, team: str) -> str:
        """Generate comprehensive network report."""
        # TODO: Implement
        pass

    def create_interactive_dashboard(self, team: str):
        """Create interactive network dashboard."""
        # TODO: Use plotly/dash for interactivity
        pass

    def compare_teams(self, team_a: str, team_b: str) -> dict:
        """Compare network structures between teams."""
        # TODO: Implement
        pass

Solutions

Solutions are provided in the accompanying exercise-solutions.py file.

Submission Guidelines

  1. Code Quality: Use proper Python style and documentation
  2. Visualization: Include clear, labeled network visualizations
  3. Interpretation: Explain what network metrics mean in football context
  4. Testing: Demonstrate your code with sample data