Code Examples

Copy-paste ready R and Python code for NFL analytics. From data loading to machine learning models.

122 Examples
R & Python Support: All examples include both R and Python versions. Click the tabs to switch between languages. Use the copy button to copy code to clipboard.

Special Teams

Analyze kicking, punting, and return game performance

Field Goal Analysis
Analyze kicker accuracy by distance and conditions.
Intermediate
library(nflfastR)
library(tidyverse)

pbp <- load_pbp(2023)

# Field goal analysis
fg_analysis <- pbp %>%
  filter(play_type == "field_goal", !is.na(kick_distance)) %>%
  mutate(
    distance_bucket = case_when(
      kick_distance < 30 ~ "Under 30",
      kick_distance < 40 ~ "30-39",
      kick_distance < 50 ~ "40-49",
      TRUE ~ "50+"
    )
  ) %>%
  group_by(distance_bucket) %>%
  summarize(
    attempts = n(),
    makes = sum(field_goal_result == "made"),
    pct = mean(field_goal_result == "made") * 100,
    .groups = "drop"
  )

print(fg_analysis)

# Team kicking rankings
team_fg <- pbp %>%
  filter(play_type == "field_goal") %>%
  group_by(posteam) %>%
  summarize(
    attempts = n(),
    makes = sum(field_goal_result == "made"),
    pct = mean(field_goal_result == "made") * 100,
    avg_distance = mean(kick_distance, na.rm = TRUE)
  ) %>%
  arrange(desc(pct))

print(team_fg)
import nfl_data_py as nfl
import pandas as pd

pbp = nfl.import_pbp_data([2023])

# Field goal analysis
fgs = pbp[(pbp["play_type"] == "field_goal") & (pbp["kick_distance"].notna())]

def distance_bucket(d):
    if d < 30: return "Under 30"
    elif d < 40: return "30-39"
    elif d < 50: return "40-49"
    else: return "50+"

fgs["distance_bucket"] = fgs["kick_distance"].apply(distance_bucket)

fg_analysis = (fgs.groupby("distance_bucket")
    .agg(
        attempts=("field_goal_result", "count"),
        makes=("field_goal_result", lambda x: (x == "made").sum()),
        pct=("field_goal_result", lambda x: (x == "made").mean() * 100)
    )
    .reset_index())

print("Field Goal Accuracy by Distance:")
print(fg_analysis)
Packages: nflfastR tidyverse nfl_data_py pandas
Punt Analysis
Analyze punter performance and net yardage.
Intermediate
library(nflfastR)
library(tidyverse)

pbp <- load_pbp(2023)

# Punter analysis
punt_analysis <- pbp %>%
  filter(play_type == "punt", !is.na(kick_distance)) %>%
  group_by(punter_player_name) %>%
  summarize(
    punts = n(),
    avg_gross = mean(kick_distance),
    inside_20 = sum(yardline_100 <= 20, na.rm = TRUE),
    touchbacks = sum(touchback, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  filter(punts >= 20) %>%
  arrange(desc(avg_gross))

print(punt_analysis)
import nfl_data_py as nfl
import pandas as pd

pbp = nfl.import_pbp_data([2023])

# Punt analysis
punts = pbp[(pbp["play_type"] == "punt") & (pbp["kick_distance"].notna())]

punt_analysis = (punts.groupby("punter_player_name")
    .agg(
        punts=("kick_distance", "count"),
        avg_gross=("kick_distance", "mean"),
        touchbacks=("touchback", "sum")
    )
    .reset_index())

punt_analysis = punt_analysis[punt_analysis["punts"] >= 20].sort_values(
    "avg_gross", ascending=False)

print("Punter Rankings:")
print(punt_analysis)
Packages: nflfastR tidyverse nfl_data_py pandas
Kickoff Return Analysis
Analyze kickoff return performance by team.
Intermediate
library(nflfastR)
library(tidyverse)

pbp <- load_pbp(2023)

# Kickoff return analysis
ko_returns <- pbp %>%
  filter(play_type == "kickoff", !is.na(return_yards)) %>%
  group_by(posteam) %>%
  summarize(
    returns = n(),
    total_yards = sum(return_yards),
    avg_return = mean(return_yards),
    touchbacks = sum(touchback, na.rm = TRUE),
    return_tds = sum(return_touchdown, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(avg_return))

print(ko_returns)
import nfl_data_py as nfl
import pandas as pd

pbp = nfl.import_pbp_data([2023])

# Kickoff return analysis
kickoffs = pbp[(pbp["play_type"] == "kickoff") & (pbp["return_yards"].notna())]

ko_returns = (kickoffs.groupby("posteam")
    .agg(
        returns=("return_yards", "count"),
        total_yards=("return_yards", "sum"),
        avg_return=("return_yards", "mean"),
        tds=("return_touchdown", "sum")
    )
    .reset_index()
    .sort_values("avg_return", ascending=False))

print("Kickoff Return Rankings:")
print(ko_returns)
Packages: nflfastR tidyverse nfl_data_py pandas
Punt Return Analysis
Analyze punt return efficiency and big plays.
Intermediate
library(nflfastR)
library(tidyverse)

pbp <- load_pbp(2023)

# Punt return analysis
punt_returns <- pbp %>%
  filter(play_type == "punt", !is.na(return_yards), return_yards > 0) %>%
  group_by(posteam) %>%
  summarize(
    returns = n(),
    total_yards = sum(return_yards),
    avg_return = mean(return_yards),
    return_tds = sum(return_touchdown, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(avg_return))

print(punt_returns)
import nfl_data_py as nfl
import pandas as pd

pbp = nfl.import_pbp_data([2023])

# Punt return analysis
punt_returns = pbp[(pbp["play_type"] == "punt") &
                   (pbp["return_yards"].notna()) &
                   (pbp["return_yards"] > 0)]

pr_analysis = (punt_returns.groupby("posteam")
    .agg(
        returns=("return_yards", "count"),
        total_yards=("return_yards", "sum"),
        avg_return=("return_yards", "mean")
    )
    .reset_index()
    .sort_values("avg_return", ascending=False))

print("Punt Return Rankings:")
print(pr_analysis)
Packages: nflfastR tidyverse nfl_data_py pandas
Special Teams EPA
Calculate EPA impact of special teams plays by type.
Advanced
library(nflfastR)
library(tidyverse)

pbp <- load_pbp(2023)

# Special teams EPA by play type
st_epa <- pbp %>%
  filter(special_teams_play == 1, !is.na(epa)) %>%
  mutate(
    st_play_type = case_when(
      play_type == "kickoff" ~ "Kickoff",
      play_type == "punt" ~ "Punt",
      play_type == "field_goal" ~ "Field Goal",
      play_type == "extra_point" ~ "Extra Point",
      TRUE ~ "Other"
    )
  ) %>%
  group_by(posteam, st_play_type) %>%
  summarize(
    plays = n(),
    total_epa = sum(epa),
    avg_epa = mean(epa),
    .groups = "drop"
  )

# Team special teams rankings
team_st_epa <- pbp %>%
  filter(special_teams_play == 1, !is.na(epa)) %>%
  group_by(posteam) %>%
  summarize(
    total_st_epa = sum(epa),
    avg_st_epa = mean(epa),
    st_plays = n()
  ) %>%
  arrange(desc(total_st_epa))

print(team_st_epa)
import nfl_data_py as nfl
import pandas as pd

pbp = nfl.import_pbp_data([2023])

# Filter special teams plays
st_plays = pbp[(pbp["special_teams_play"] == 1) & (pbp["epa"].notna())]

# Map play types
def st_type(play):
    if play == "kickoff": return "Kickoff"
    elif play == "punt": return "Punt"
    elif play == "field_goal": return "Field Goal"
    elif play == "extra_point": return "Extra Point"
    else: return "Other"

st_plays["st_play_type"] = st_plays["play_type"].apply(st_type)

# Team special teams EPA
team_st_epa = (st_plays.groupby("posteam")
    .agg(
        total_st_epa=("epa", "sum"),
        avg_st_epa=("epa", "mean"),
        st_plays=("epa", "count")
    )
    .reset_index()
    .sort_values("total_st_epa", ascending=False))

print("Special Teams EPA Rankings:")
print(team_st_epa)
Packages: nflfastR tidyverse nfl_data_py pandas
Blocked Kicks Analysis
Analyze blocked field goals, punts, and extra points.
Intermediate
library(nflfastR)
library(tidyverse)

pbp <- load_pbp(2020:2023)

# Find blocked kicks
blocked_kicks <- pbp %>%
  filter(
    play_type %in% c("field_goal", "punt", "extra_point"),
    str_detect(tolower(desc), "block")
  ) %>%
  mutate(
    blocked_type = play_type
  )

# Blocked kicks by team (blocking team)
team_blocks <- blocked_kicks %>%
  group_by(defteam, blocked_type) %>%
  summarize(blocks = n(), .groups = "drop") %>%
  pivot_wider(names_from = blocked_type, values_from = blocks, values_fill = 0) %>%
  mutate(total_blocks = rowSums(across(where(is.numeric)))) %>%
  arrange(desc(total_blocks))

print("Teams with Most Blocked Kicks (2020-2023):")
print(team_blocks)

# Blocked kicks allowed
blocks_allowed <- blocked_kicks %>%
  group_by(posteam, blocked_type) %>%
  summarize(blocked = n(), .groups = "drop") %>%
  pivot_wider(names_from = blocked_type, values_from = blocked, values_fill = 0) %>%
  mutate(total_blocked = rowSums(across(where(is.numeric)))) %>%
  arrange(desc(total_blocked))

print("\nTeams with Most Kicks Blocked Against:")
print(blocks_allowed)
import nfl_data_py as nfl
import pandas as pd

pbp = nfl.import_pbp_data([2020, 2021, 2022, 2023])

# Find blocked kicks
st_plays = pbp[pbp["play_type"].isin(["field_goal", "punt", "extra_point"])]
blocked = st_plays[st_plays["desc"].str.lower().str.contains("block", na=False)]

# Blocked kicks by blocking team
team_blocks = (blocked.groupby(["defteam", "play_type"])
    .size()
    .reset_index(name="blocks"))

# Pivot to get blocks by type
blocks_pivot = team_blocks.pivot_table(
    index="defteam",
    columns="play_type",
    values="blocks",
    fill_value=0
).reset_index()

blocks_pivot["total_blocks"] = blocks_pivot.select_dtypes(include="number").sum(axis=1)
blocks_pivot = blocks_pivot.sort_values("total_blocks", ascending=False)

print("Teams with Most Blocked Kicks (2020-2023):")
print(blocks_pivot.head(10))
Packages: nflfastR tidyverse nfl_data_py pandas
Quick Package Reference
R Packages
  • nflfastR - Play-by-play data with EPA
  • nflplotR - NFL team logos & plotting
  • tidyverse - Data manipulation & visualization
  • ggplot2 - Advanced visualizations
Python Packages
  • nfl_data_py - NFL data (nflverse compatible)
  • pandas - Data manipulation
  • matplotlib - Visualizations
  • scikit-learn - Machine learning

Ready to Dive Deeper?

Learn the theory behind these techniques in our comprehensive tutorial series

Browse Tutorials