# chickenstats library and utilities
from chickenstats.chicken_nhl._helpers import charts_directory
import nflreadpy as nfl
import polars as pl
# plotting library and utilities
import matplotlib.pyplot as plt
import seaborn as sns
# miscellaneous utilities
from pathlib import PathHow does Cam Ward stack up?
tennessee titans
How does Cam Ward compare to this year’s rookies and historical Titans QBs
Housekeeping
Import dependencies and set options
pl.Config.set_tbl_cols(-1)
pl.Config.set_tbl_rows(30)polars.config.Config
Create directory for charts
charts_directory()chickenstats matplotlib styles
plt.style.use("chickenstats") # this is available when you import chickenstats.utilitiesDownloading and prepping data
Player stats
seasons = list(range(2002, 2026))
player_stats = nfl.load_player_stats(seasons=seasons)conditions = (pl.col("player_id") != "0", pl.col("player_name").is_not_null())
player_stats = player_stats.filter(conditions)Rosters
rosters = nfl.load_rosters_weekly(seasons=seasons)rosters = rosters.filter(pl.col("status") == "ACT")Adding years of experience
columns = (pl.col("season"), pl.col("gsis_id").alias("player_id"), pl.col("week"), pl.col("years_exp"))
merge_columns = ["season", "player_id", "week"]
player_stats = player_stats.join(rosters.select(columns), on=merge_columns)Aggregating to season level
group_columns = [
"player_id",
"player_name",
"player_display_name",
"position",
"position_group",
"season",
"season_type",
"team",
"years_exp",
]
group_columns = (pl.col(x) for x in group_columns)
sum_columns = [
"completions",
"attempts",
"passing_yards",
"passing_tds",
"passing_interceptions",
"sacks_suffered",
"sack_yards_lost",
"sack_fumbles",
"sack_fumbles_lost",
"passing_air_yards",
"passing_yards_after_catch",
"passing_first_downs",
"passing_epa",
"passing_2pt_conversions",
"carries",
"rushing_yards",
"rushing_tds",
"rushing_fumbles",
"rushing_fumbles_lost",
"rushing_first_downs",
"rushing_epa",
"rushing_2pt_conversions",
"receptions",
"targets",
"receiving_yards",
"receiving_tds",
"receiving_fumbles",
"receiving_fumbles_lost",
"receiving_air_yards",
"receiving_yards_after_catch",
"receiving_first_downs",
"receiving_epa",
"receiving_2pt_conversions",
"special_teams_tds",
"def_tackles_solo",
"def_tackles_with_assist",
"def_tackle_assists",
"def_tackles_for_loss",
"def_tackles_for_loss_yards",
"def_fumbles_forced",
"def_sacks",
"def_sack_yards",
"def_qb_hits",
"def_interceptions",
"def_interception_yards",
"def_pass_defended",
"def_tds",
"def_fumbles",
"def_safeties",
"misc_yards",
"fumble_recovery_own",
"fumble_recovery_yards_own",
"fumble_recovery_opp",
"fumble_recovery_yards_opp",
"fumble_recovery_tds",
"penalties",
"penalty_yards",
"punt_returns",
"punt_return_yards",
"kickoff_returns",
"kickoff_return_yards",
"fg_made",
"fg_att",
"fg_missed",
"fg_blocked",
"fg_long",
"fg_made_0_19",
"fg_made_20_29",
"fg_made_30_39",
"fg_made_40_49",
"fg_made_50_59",
"fg_made_60_",
"fg_missed_0_19",
"fg_missed_20_29",
"fg_missed_30_39",
"fg_missed_40_49",
"fg_missed_50_59",
"fg_missed_60_",
"pat_made",
"pat_att",
"pat_missed",
"pat_blocked",
"pat_pct",
"gwfg_made",
"gwfg_att",
"gwfg_missed",
"gwfg_blocked",
"gwfg_distance",
"fantasy_points",
"fantasy_points_ppr",
]
sum_columns = (pl.col(x).sum().alias(f"{x}_sum") for x in sum_columns)
mean_columns = ["passing_cpoe", "target_share", "air_yards_share", "pacr", "racr", "wopr"]
mean_columns = (pl.col(x).mean().alias(f"{x}_mean") for x in mean_columns)
agg_columns = (*sum_columns, *mean_columns)
player_stats = player_stats.group_by(group_columns).agg(agg_columns)Getting QB stats
conditions = (
pl.col("player_id") != "0",
pl.col("player_name").is_not_null(),
pl.col("position") == "QB",
pl.col("attempts_sum") >= 1,
pl.col("season_type") == "REG",
pl.col("years_exp") == 0,
)
qb_stats = player_stats.filter(conditions)qb_stats = qb_stats.with_columns(epa_per_attempt=pl.col("passing_epa_sum") / pl.col("attempts_sum"))Plotting data
Setting team and other values
team = "TEN"
light_blue = "#4B92DB"
navy = "#002244"
red = "#C60C30"
gray = "#D3D3D3"Scatter plot
# Setting overall figures
fig, ax = plt.subplots(dpi=650, figsize=(8, 5))
# Aesthetics, likes the tight layout and despining axes
fig.tight_layout()
sns.despine()
# Setting the x, y, and z values
x_col = "attempts_sum"
y_col = "passing_epa_sum"
size_col = "passing_tds_sum"
# Getting the averages and drawing the average lines
x_mean = qb_stats.select(pl.col(x_col)).mean().item(0, 0)
y_mean = qb_stats.select(pl.col(y_col)).mean().item(0, 0)
ax.axvline(x=x_mean, zorder=-1, alpha=0.5)
ax.axhline(y=y_mean, zorder=-1, alpha=0.5)
# Setting the size norm so bubbles are consistent across figures
size_min = qb_stats.select(pl.col(size_col)).min().item(0, 0)
size_max = qb_stats.select(pl.col(size_col)).max().item(0, 0)
size_norm = (size_min, size_max)
sizes = (20, 150)
# Filtering data and plotting the non-selected teams first
plot_data = qb_stats.filter(pl.col("team") != team)
# They all get gray colors
facecolor = gray
edgecolor = "white"
# Plotting the non-selected teams' data
sns.scatterplot(
x=plot_data[x_col],
y=plot_data[y_col],
size=plot_data[size_col],
sizes=sizes,
size_norm=size_norm,
lw=1.5,
facecolor=facecolor,
edgecolor=edgecolor,
alpha=0.5,
legend=False,
)
# Filtering the data and plotting the selected team
plot_data = qb_stats.filter(pl.col("team") == team)
# Setting the colors
facecolor = light_blue
edgecolor = navy
# Plotting the selected teams' data
sns.scatterplot(
x=plot_data[x_col],
y=plot_data[y_col],
size=plot_data[size_col],
sizes=sizes,
size_norm=size_norm,
lw=1.5,
facecolor=facecolor,
edgecolor=edgecolor,
alpha=0.5,
legend=False,
)
# Labeling Cam Ward
player_list = [
"C.Ward",
"P.Mahomes",
"R.Wilson",
"J.Burrow",
"D.Prescott",
"J.Herbert",
"R.Griffin",
"M.Ryan",
"C.Stroud",
"J.Daniels",
"B.Roethlisberger",
"M.Jones",
"C.Newton",
"J.Burrow",
"J.Winston",
"B.Purdy",
"B.Nix",
"B.Mayfield",
"D.Watson",
"C.Kessler",
"M.Penix",
"D.Lock",
"A.Luck",
"K.Allen",
"K.Cousins",
"J.Milton",
"T.Lance",
"T.Tebow",
"J.Locker",
"M.Mariota",
"W.Levis",
"R.Smith",
"Z.Mettenberger",
"M.Willis",
"V.Young",
"B.Young",
"J.Rosen",
"D.Carr",
"B.Bortles",
"D.Kizer",
]
label_data = qb_stats.filter(pl.col("player_name").is_in(player_list), pl.col("years_exp") == 0)
x_offset = -50
y_offset = -15
for player_data in label_data.iter_rows(named=True):
x_position = player_data[x_col] + x_offset
y_position = player_data[y_col] + y_offset
arrow_props = {"arrowstyle": "simple", "linewidth": 0.25, "color": "tab:gray"}
# Plotting the annotation
ax.annotate(
text=f"{player_data['player_display_name']}",
xy=(player_data[x_col], player_data[y_col]),
xytext=(x_position, y_position),
fontsize=6,
bbox={"facecolor": "white", "alpha": 0.5, "edgecolor": "white", "pad": 0},
arrowprops=arrow_props,
)
# Iterating through the dataframe to label the bubbles
# for row, player in plot_data.iterrows():
# # Setting x and y positions that are slightly offset from the data they point to
# x_position = player.xga_p60 + 0.25
# y_position = player.xgf_p60 - 0.25
# # Annotation options
# arrow_props = {"arrowstyle": "simple", "linewidth": 0.25, "color": "tab:gray"}
# # Plotting the annotation
# ax.annotate(
# text=f"{player.player}",
# xy=(player.xga_p60, player.xgf_p60),
# xytext=(x_position, y_position),
# fontsize=6,
# bbox={"facecolor": "white", "alpha": 0.5, "edgecolor": "white", "pad": 0},
# arrowprops=arrow_props,
# )
# Setting axis lables
ax.axes.set_xlabel("Total Passing Attempts")
ax.axes.set_ylabel("Total Passing EPA")
# Setting figure suptitle and subtitle
fig_suptitle = "Title"
fig.suptitle(fig_suptitle, x=0.01, y=1.08, fontsize=11, fontweight="bold", horizontalalignment="left")
subtitle = "Total Passing EPA and Attempts, Sized for Passing TDs | NFL Rookie Seasons 2002-25"
fig.text(s=subtitle, x=0.01, y=1.02, fontsize=10, horizontalalignment="left")
# Attribution
attribution = "Data & EPA model: nflreadpy python package | Viz @chickenandstats"
fig.text(s=attribution, x=0.99, y=-0.05, fontsize=8, horizontalalignment="right", style="italic")
# Save figure
savepath = Path(f"./charts/epa_attempt_attempts_{team}.png")
# fig.savefig(savepath, transparent=False, bbox_inches="tight")qb_stats.filter(pl.col("player_name").str.contains("V.Young"))
# qb_stats.sort("passing_epa_sum", descending=True).head(25)["player_name"].unique().to_list()
qb_stats.filter(pl.col("years_exp") == 0).sort("passing_epa_sum", descending=True).tail(25)
shape: (25, 106)
| player_id | player_name | player_display_name | position | position_group | season | season_type | team | years_exp | completions_sum | attempts_sum | passing_yards_sum | passing_tds_sum | passing_interceptions_sum | sacks_suffered_sum | sack_yards_lost_sum | sack_fumbles_sum | sack_fumbles_lost_sum | passing_air_yards_sum | passing_yards_after_catch_sum | passing_first_downs_sum | passing_epa_sum | passing_2pt_conversions_sum | carries_sum | rushing_yards_sum | rushing_tds_sum | rushing_fumbles_sum | rushing_fumbles_lost_sum | rushing_first_downs_sum | rushing_epa_sum | rushing_2pt_conversions_sum | receptions_sum | targets_sum | receiving_yards_sum | receiving_tds_sum | receiving_fumbles_sum | receiving_fumbles_lost_sum | receiving_air_yards_sum | receiving_yards_after_catch_sum | receiving_first_downs_sum | receiving_epa_sum | receiving_2pt_conversions_sum | special_teams_tds_sum | def_tackles_solo_sum | def_tackles_with_assist_sum | def_tackle_assists_sum | def_tackles_for_loss_sum | def_tackles_for_loss_yards_sum | def_fumbles_forced_sum | def_sacks_sum | def_sack_yards_sum | def_qb_hits_sum | def_interceptions_sum | def_interception_yards_sum | def_pass_defended_sum | def_tds_sum | def_fumbles_sum | def_safeties_sum | misc_yards_sum | fumble_recovery_own_sum | fumble_recovery_yards_own_sum | fumble_recovery_opp_sum | fumble_recovery_yards_opp_sum | fumble_recovery_tds_sum | penalties_sum | penalty_yards_sum | punt_returns_sum | punt_return_yards_sum | kickoff_returns_sum | kickoff_return_yards_sum | fg_made_sum | fg_att_sum | fg_missed_sum | fg_blocked_sum | fg_long_sum | fg_made_0_19_sum | fg_made_20_29_sum | fg_made_30_39_sum | fg_made_40_49_sum | fg_made_50_59_sum | fg_made_60__sum | fg_missed_0_19_sum | fg_missed_20_29_sum | fg_missed_30_39_sum | fg_missed_40_49_sum | fg_missed_50_59_sum | fg_missed_60__sum | pat_made_sum | pat_att_sum | pat_missed_sum | pat_blocked_sum | pat_pct_sum | gwfg_made_sum | gwfg_att_sum | gwfg_missed_sum | gwfg_blocked_sum | gwfg_distance_sum | fantasy_points_sum | fantasy_points_ppr_sum | passing_cpoe_mean | target_share_mean | air_yards_share_mean | pacr_mean | racr_mean | wopr_mean | epa_per_attempt |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| str | str | str | str | str | i32 | str | str | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | f64 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | f64 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | f64 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | f64 | f64 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | i32 | f64 | i32 | i32 | i32 | i32 | i32 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 | f64 |
| "00-0035652" | "R.Finley" | "Ryan Finley" | "QB" | "QB" | 2019 | "REG" | "CIN" | 0 | 41 | 87 | 474 | 2 | 2 | 11 | -93 | 4 | 3 | 721 | 203 | 20 | -52.636759 | 0 | 10 | 77 | 0 | 0 | 0 | 5 | 0.280104 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 24.66 | 24.66 | -15.687838 | 0.0 | 0.0 | 0.65507 | null | 0.0 | -0.60502 |
| "00-0027950" | "C.Ponder" | "Christian Ponder" | "QB" | "QB" | 2011 | "REG" | "MIN" | 0 | 158 | 291 | 1853 | 13 | 13 | 30 | -164 | 5 | 2 | 2667 | 831 | 91 | -56.446926 | 0 | 28 | 219 | 0 | 1 | 0 | 15 | 17.573881 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 2 | 25 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 118.02 | 118.02 | -7.464352 | 0.0 | 0.0 | 0.691941 | null | 0.0 | -0.193976 |
| "00-0026993" | "J.Freeman" | "Josh Freeman" | "QB" | "QB" | 2009 | "REG" | "TB" | 0 | 159 | 291 | 1857 | 10 | 18 | 19 | -102 | 5 | 1 | 2649 | 658 | 89 | -58.254535 | 1 | 31 | 162 | 0 | 4 | 1 | 15 | 2.430892 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 5 | 7 | 0 | 0 | 0 | 3 | 15 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 94.58 | 94.58 | -4.683177 | 0.0 | 0.0 | 0.733723 | 0.0 | 0.0 | -0.200187 |
| "00-0030565" | "G.Smith" | "Geno Smith" | "QB" | "QB" | 2013 | "REG" | "NYJ" | 0 | 247 | 443 | 3046 | 12 | 21 | 43 | -315 | 4 | 3 | 3963 | 1260 | 143 | -59.77581 | 0 | 72 | 366 | 6 | 4 | 1 | 28 | 14.142214 | 0 | 1 | 1 | 13 | 0 | 0 | 0 | 13 | 0 | 1 | 1.309993 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 4 | 0 | 0 | 0 | 5 | 21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 193.74 | 194.74 | -4.97212 | 0.002604 | 0.002871 | 0.874442 | 1.0 | 0.005916 | -0.134934 |
| "00-0036971" | "T.Lawrence" | "Trevor Lawrence" | "QB" | "QB" | 2021 | "REG" | "JAX" | 0 | 359 | 602 | 3641 | 12 | 17 | 32 | -238 | 6 | 5 | 4738 | 1634 | 178 | -60.74786 | 2 | 73 | 334 | 2 | 2 | 0 | 23 | 11.314142 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 199.04 | 199.04 | -4.281167 | 0.0 | 0.0 | 0.822605 | null | 0.0 | -0.10091 |
| "00-0031280" | "D.Carr" | "Derek Carr" | "QB" | "QB" | 2014 | "REG" | "LV" | 0 | 348 | 599 | 3270 | 21 | 12 | 24 | -149 | 4 | 2 | 4664 | 1462 | 165 | -61.50931 | 0 | 29 | 92 | 0 | 5 | 2 | 15 | -11.191634 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 17 | 0 | 0 | -0.504618 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 8 | 0 | 0 | 0 | 9 | 65 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 192.0 | 192.0 | -5.982209 | 0.001179 | 0.002426 | 0.730652 | 0.0 | 0.003467 | -0.102687 |
| "00-0040398" | "B.Cook" | "Brady Cook" | "QB" | "QB" | 2025 | "REG" | "NYJ" | 0 | 77 | 131 | 679 | 1 | 7 | 18 | -118 | 2 | 1 | 794 | 385 | 30 | -62.011141 | 0 | 10 | 42 | 0 | 1 | 0 | 5 | -3.80431 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 3 | 0 | 0 | 0 | 1 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 19.36 | 19.36 | -8.473645 | 0.0 | 0.0 | 0.903789 | null | 0.0 | -0.473367 |
| "00-0036945" | "J.Fields" | "Justin Fields" | "QB" | "QB" | 2021 | "REG" | "CHI" | 0 | 159 | 270 | 1870 | 7 | 10 | 36 | -264 | 8 | 4 | 2645 | 683 | 83 | -64.928772 | 0 | 72 | 420 | 2 | 4 | 1 | 26 | 6.727855 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 6 | 0 | 0 | 0 | 2 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 126.8 | 126.8 | -1.590425 | 0.0 | 0.0 | 0.768913 | null | 0.0 | -0.240477 |
| "00-0026898" | "M.Sanchez" | "Mark Sanchez" | "QB" | "QB" | 2009 | "REG" | "NYJ" | 0 | 196 | 363 | 2444 | 12 | 20 | 26 | -195 | 8 | 3 | 3407 | 997 | 123 | -71.400733 | 2 | 36 | 106 | 3 | 2 | 0 | 18 | 7.100561 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 2 | 0 | 0 | 0 | 8 | 72 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 132.36 | 132.36 | -5.202597 | 0.0 | 0.0 | 0.775397 | null | 0.0 | -0.196696 |
| "00-0039376" | "S.Rattler" | "Spencer Rattler" | "QB" | "QB" | 2024 | "REG" | "NO" | 0 | 130 | 228 | 1317 | 4 | 5 | 22 | -136 | 4 | 3 | 1834 | 642 | 63 | -75.739327 | 0 | 18 | 146 | 0 | 1 | 0 | 8 | 13.575403 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 67.28 | 67.28 | -10.31886 | 0.0 | 0.0 | 0.750064 | null | 0.0 | -0.33219 |
| "00-0024408" | "B.Gradkowski" | "Bruce Gradkowski" | "QB" | "QB" | 2006 | "REG" | "TB" | 0 | 177 | 328 | 1661 | 9 | 9 | 25 | -146 | 7 | 3 | 2241 | 794 | 79 | -84.508159 | 0 | 41 | 161 | 0 | 4 | 3 | 14 | -7.189951 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 4 | 0 | 0 | 0 | 2 | 25 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 88.54 | 88.54 | -4.785349 | NaN | 0.0 | 1.13587 | null | NaN | -0.257647 |
| "00-0023436" | "A.Smith" | "Alex Smith" | "QB" | "QB" | 2005 | "REG" | "SF" | 0 | 84 | 165 | 875 | 1 | 11 | 29 | -185 | 9 | 2 | 0 | 875 | 37 | -85.504377 | 0 | 30 | 103 | 0 | 2 | 1 | 5 | -11.978286 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 6 | 8 | 0 | 0 | 0 | 4 | 19 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 21.3 | 21.3 | null | NaN | NaN | 0.0 | null | NaN | -0.518208 |
| "00-0033106" | "J.Goff" | "Jared Goff" | "QB" | "QB" | 2016 | "REG" | "LA" | 0 | 112 | 205 | 1089 | 5 | 7 | 26 | -222 | 5 | 2 | 1409 | 572 | 47 | -86.709483 | 0 | 8 | 16 | 1 | 0 | 0 | 2 | 0.238419 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 53.16 | 53.16 | -11.053336 | 0.0 | 0.0 | 0.837851 | null | 0.0 | -0.422973 |
| "00-0021141" | "J.Harrington" | "Joey Harrington" | "QB" | "QB" | 2002 | "REG" | "DET" | 0 | 215 | 429 | 2294 | 12 | 16 | 8 | -75 | 1 | 1 | 166 | 2294 | 110 | -86.779541 | 0 | 7 | 4 | 0 | 1 | 0 | 1 | -8.771017 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 106.16 | 106.16 | null | 0.0 | NaN | 24.639077 | null | NaN | -0.202283 |
| "00-0029531" | "R.Lindley" | "Ryan Lindley" | "QB" | "QB" | 2012 | "REG" | "ARI" | 0 | 89 | 171 | 752 | 0 | 7 | 12 | -91 | 2 | 2 | 1406 | 262 | 38 | -87.941983 | 0 | 4 | 7 | 0 | 1 | 0 | 2 | -2.622497 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 12.78 | 12.78 | -12.828982 | 0.0 | 0.0 | 0.574158 | null | 0.0 | -0.514281 |
| "00-0027659" | "J.Clausen" | "Jimmy Clausen" | "QB" | "QB" | 2010 | "REG" | "CAR" | 0 | 157 | 299 | 1558 | 3 | 9 | 33 | -223 | 1 | 0 | 1974 | 813 | 73 | -92.992759 | 0 | 23 | 57 | 0 | 7 | 2 | 10 | -22.119157 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 8 | 0 | 0 | 0 | 2 | 23 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 58.02 | 58.02 | -12.385229 | 0.0 | 0.0 | 0.798837 | null | 0.0 | -0.311013 |
| "00-0020562" | "C.Hutchinson" | "Chad Hutchinson" | "QB" | "QB" | 2002 | "REG" | "DAL" | 0 | 127 | 250 | 1555 | 7 | 8 | 34 | -265 | 12 | 8 | 137 | 1555 | 63 | -94.5604 | 0 | 18 | 74 | 0 | 0 | 0 | 3 | -4.270921 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 65.6 | 65.6 | null | 0.0 | NaN | 18.174022 | null | NaN | -0.378242 |
| "00-0037013" | "Z.Wilson" | "Zach Wilson" | "QB" | "QB" | 2021 | "REG" | "NYJ" | 0 | 213 | 383 | 2334 | 9 | 11 | 44 | -370 | 4 | 1 | 2885 | 1099 | 112 | -95.573186 | 1 | 29 | 185 | 4 | 0 | 0 | 10 | 7.612972 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 8 | 0 | 0 | -0.740859 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 3 | 0 | 0 | 0 | 1 | 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 151.86 | 151.86 | -9.820057 | 0.003497 | 0.004558 | 0.896501 | 0.0 | 0.008436 | -0.249538 |
| "00-0027948" | "B.Gabbert" | "Blaine Gabbert" | "QB" | "QB" | 2011 | "REG" | "JAX" | 0 | 210 | 413 | 2214 | 12 | 11 | 40 | -293 | 5 | 3 | 3216 | 1135 | 110 | -101.764242 | 0 | 48 | 98 | 0 | 8 | 1 | 17 | -14.178523 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 10 | 0 | 0 | 0 | 3 | 26 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 116.36 | 116.36 | -8.846946 | 0.0 | 0.0 | 0.803466 | null | 0.0 | -0.246403 |
| "00-0033899" | "D.Kizer" | "DeShone Kizer" | "QB" | "QB" | 2017 | "REG" | "CLE" | 0 | 255 | 476 | 2894 | 11 | 22 | 38 | -226 | 6 | 5 | 4272 | 1440 | 130 | -112.868852 | 0 | 77 | 419 | 5 | 3 | 1 | 34 | 18.41577 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 | 5 | 0 | 0 | 0 | 3 | 15 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 175.66 | 175.66 | -5.950583 | 0.0 | 0.0 | 0.722045 | null | 0.0 | -0.237119 |
| "00-0040676" | "C.Ward" | "Cam Ward" | "QB" | "QB" | 2025 | "REG" | "TEN" | 0 | 320 | 537 | 3117 | 15 | 7 | 55 | -410 | 9 | 7 | 3887 | 1513 | 151 | -113.529241 | 0 | 37 | 148 | 1 | 1 | 0 | 15 | 7.221071 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2 | 0 | 0 | 0 | 1 | 16 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 177.48 | 177.48 | -3.848776 | 0.0 | 0.0 | 0.84424 | null | 0.0 | -0.211414 |
| "00-0031407" | "B.Bortles" | "Blake Bortles" | "QB" | "QB" | 2014 | "REG" | "JAX" | 0 | 280 | 475 | 2908 | 11 | 17 | 55 | -345 | 4 | 0 | 3326 | 1620 | 132 | -113.703585 | 1 | 56 | 419 | 0 | 3 | 1 | 30 | 14.894275 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 4 | 0 | 0 | 0 | 6 | 35 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 168.22 | 168.22 | -7.378702 | 0.0 | 0.0 | 0.961041 | null | 0.0 | -0.239376 |
| "00-0020608" | "D.Carr" | "David Carr" | "QB" | "QB" | 2002 | "REG" | "HOU" | 0 | 233 | 444 | 2592 | 9 | 15 | 76 | -411 | 8 | 3 | 211 | 2592 | 117 | -132.459271 | 2 | 59 | 282 | 3 | 13 | 4 | 28 | -8.950429 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 12 | 20 | 0 | 0 | 0 | 12 | 89 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 145.88 | 145.88 | null | 0.0 | NaN | 5.264255 | null | NaN | -0.298332 |
| "00-0034343" | "J.Rosen" | "Josh Rosen" | "QB" | "QB" | 2018 | "REG" | "ARI" | 0 | 217 | 393 | 2278 | 11 | 14 | 45 | -320 | 7 | 5 | 3262 | 977 | 112 | -150.632712 | 1 | 23 | 138 | 0 | 3 | 0 | 10 | 3.882554 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 7 | 8 | 0 | 0 | 0 | 1 | 8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 112.92 | 112.92 | -8.322272 | 0.0 | 0.0 | 0.748037 | null | 0.0 | -0.383289 |
| "00-0039150" | "B.Young" | "Bryce Young" | "QB" | "QB" | 2023 | "REG" | "CAR" | 0 | 315 | 527 | 2877 | 11 | 10 | 62 | -477 | 9 | 6 | 4009 | 1300 | 135 | -159.273442 | 1 | 39 | 253 | 0 | 2 | 0 | 20 | 22.6693 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | 4 | 0 | 0 | 0 | 2 | 25 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.0 | 0 | 0 | 0 | 0 | 0 | 156.38 | 156.38 | -1.251331 | 0.0 | 0.0 | 0.799165 | null | 0.0 | -0.302227 |
qb_stats["season"].unique().to_list()[2002,
2003,
2004,
2005,
2006,
2007,
2008,
2009,
2010,
2011,
2012,
2013,
2014,
2015,
2016,
2017,
2018,
2019,
2020,
2021,
2022,
2023,
2024,
2025]

