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
- Code Quality: Use proper Python style and documentation
- Visualization: Include clear, labeled network visualizations
- Interpretation: Explain what network metrics mean in football context
- Testing: Demonstrate your code with sample data