# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3 (ipykernel)
#     language: python
#     name: python3
# ---

import nflfastpy as npy
import urllib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import matplotlib.patheffects as pe
from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, AnnotationBbox
from PIL import Image

# +
font_paths = [
    "../../fonts/Poppins-Regular.ttf",
    "../../fonts/Poppins-Medium.ttf",
    "../../fonts/Poppins-SemiBold.ttf",
    "../../fonts/Poppins-Bold.ttf"
]

for path in font_paths:
    fm.fontManager.addfont(path)
    fm.FontProperties(fname=path)
# -

df_2021 = npy.load_pbp_data(2021)
df_roster_2021 = npy.load_roster_data(2021)
df_team = npy.load_team_logo_data()
df_schedule = npy.load_schedule_data(2021)

# +
df_home = (
    df_schedule
    .groupby("home_team", as_index=False)
    .agg(net_home=("home_result", "sum"), pf_home=("home_score", "sum"))
)

df_away = (
    df_schedule
    .groupby("away_team", as_index=False)
    .agg(net_away=("home_result", "sum"), pf_away=("away_score", "sum"))
)

df_net = df_home.merge(df_away, left_on="home_team", right_on="away_team")
df_net["net_pts"] = df_net["net_home"] - df_net["net_away"]
df_net["pf"] = df_net["pf_home"] + df_net["pf_away"]
df_net = (
    df_net
    .drop(columns=["net_home", "away_team", "net_away", "pf_home", "pf_away"])
    .rename(columns={"home_team": "team"})
)
df_net
# -

df_passing = df_2021[(df_2021["pass_attempt"] == 1) & (df_2021["two_point_attempt"] == 0)].copy()
df_passing = (
    df_passing.groupby(["passer_player_id", "week"], as_index=False)
    .agg(
        team=("posteam", "first"),
        opponent=("defteam", "first"),
        completions=("complete_pass", "sum"),
        dropbacks=("pass_attempt", "sum"),
        yards=("passing_yards", "sum"),
        total_yards=("yards_gained", "sum"),
        tds=("pass_touchdown", "sum"),
        ints=("interception", "sum"),
        sacks=("sack", "sum")
    )
    .merge(df_roster_2021[["full_name", "gsis_id"]], left_on="passer_player_id", right_on="gsis_id")
)

# +
# Sort by Passer and Week
df_passing_cum = df_passing.sort_values(["full_name", "week"]).copy().reset_index(drop=True)

# Get Cumulative Stats
df_passing_cum["completions"] = df_passing_cum.groupby("full_name")["completions"].cumsum()
df_passing_cum["dropbacks"] = df_passing_cum.groupby("full_name")["dropbacks"].cumsum()
df_passing_cum["yards"] = df_passing_cum.groupby("full_name")["yards"].cumsum()
df_passing_cum["total_yards"] = df_passing_cum.groupby("full_name")["total_yards"].cumsum()
df_passing_cum["tds"] = df_passing_cum.groupby("full_name")["tds"].cumsum()
df_passing_cum["ints"] = df_passing_cum.groupby("full_name")["ints"].cumsum()
df_passing_cum["sacks"] = df_passing_cum.groupby("full_name")["sacks"].cumsum()
# -

# Calculate ANY/A
df_passing_cum["attempts"] = df_passing_cum["dropbacks"] - df_passing_cum["sacks"]
df_passing_cum["sack_yards_lost"] = df_passing_cum["yards"] - df_passing_cum["total_yards"]
df_passing_cum["any_a"] = (
    (df_passing_cum["yards"] + (20 * df_passing_cum["tds"]) - (45 * df_passing_cum["ints"]) - df_passing_cum["sack_yards_lost"]) / 
    df_passing_cum["dropbacks"]
)
df_passing_cum["y_a"] = df_passing_cum["yards"] / df_passing_cum["attempts"]

df_passing_cum

df_passing_cum[df_passing_cum["full_name"] == "<NAME>"]

# +
# Get List of Passers with > 1000 total yards
qb_list = (
    df_passing.groupby(["full_name", "team"], as_index=False)
    .agg(
        {
            "completions": "sum",
            "yards": "sum",
            "dropbacks": "sum",
            "week": "max"
        }
    )
)

rookies = [
    "<NAME>",
    "<NAME>",
    "<NAME>",
    "<NAME>",
    "<NAME>"
]

qb_list = (
    qb_list[qb_list["yards"] >= 1000]
    .merge(df_passing_cum[["full_name", "week", "any_a"]])
    .reset_index(drop=True)
)

# Get Team Colors
qb_list = (
    qb_list.merge(df_team[["team_abbr", "team_color", "team_color2"]].rename(columns={"team_abbr": "team"}))
)

qb_list.loc[qb_list["full_name"].isin(rookies), "rookie"] = True
qb_list["rookie"] = qb_list["rookie"].fillna(False)
qb_list = qb_list.sort_values("any_a", ascending=False).reset_index(drop=True)
qb_list

# +
league_avg = df_passing[["completions", "dropbacks", "yards", "total_yards", "tds", "ints", "sacks"]].sum()
league_avg["attempts"] = league_avg["dropbacks"] - league_avg["sacks"]
league_avg["sack_yards_lost"] = league_avg["yards"] - league_avg["total_yards"]

# Calculate ANY/A
league_avg["any_a"] = (
    (league_avg["yards"] + (20 * league_avg["tds"]) - (45 * league_avg["ints"]) - league_avg["sack_yards_lost"]) / 
    league_avg["dropbacks"]
)
# -

league_avg

# ### Get Overall Team ANY/A

# +
df_team_stats = (
    df_passing.groupby("team", as_index=False)
    .sum()
)

df_team_stats["attempts"] = df_team_stats["dropbacks"] - df_team_stats["sacks"]
df_team_stats["sack_yards_lost"] = df_team_stats["yards"] - df_team_stats["total_yards"]

# Calculate ANY/A
df_team_stats["any_a"] = (
    (df_team_stats["yards"] + (20 * df_team_stats["tds"]) - (45 * df_team_stats["ints"]) - df_team_stats["sack_yards_lost"]) / 
    df_team_stats["dropbacks"]
)

# Merge with point differential
df_team_stats = df_team_stats.merge(df_net)

df_team_stats = df_team_stats.sort_values("any_a", ascending=False).reset_index(drop=True)
df_team_stats
# -

df_net["net_pts"].sum()

# Get all the logos
for i, team in enumerate(df_team_stats.team):
    im = Image.open(urllib.request.urlopen(df_team[df_team["team_abbr"] == team].iloc[0].team_logo_espn))
    im.save(f"./logo-{team}.png")

# +
plt.rcParams["font.family"] = "Poppins"
plt.rcParams["font.size"] = 16
plt.rcParams["xtick.major.size"] = 0
plt.rcParams["ytick.major.size"] = 0

fig = plt.figure(figsize=(20, 15), facecolor="#fbfbf0")
gs = fig.add_gridspec(nrows=3, ncols=2, hspace=0.02, wspace=0.03, height_ratios=[0.1, 0.8, 1])
ax_title = fig.add_subplot(gs[0, :], frame_on=False)
ax_formula = fig.add_subplot(gs[1, 0], frame_on=False)
ax_team_stats = fig.add_subplot(gs[2, 0], frame_on=False)
ax_league = fig.add_subplot(gs[1:3, 1], frame_on=False)

# Formula
ax_formula.text(
    0.01, 0.7,
    "Adjusted Net Yards per Attempt (ANY/A) takes the basic yards per \n" +
    "attempt calculation (Y/A) and adjusts for passing touchdowns, in-\n" +
    "-terceptions, and sacks. To do this, sack yards are subtracted from\n" +
    "passing yards, passing touchdowns are given as +20 yards, and \n" +
    "interceptions are -45 yards. Also, instead of passing attempts,\n" +
    "total dropbacks (attempts + sacks) are used.",
    ha="left",
    va="top",
    linespacing=1.4
)

ax_formula.text(
    0.5, 0.25,
    "$\mathregular{\\frac{Passing\;Yards - Sack\;Yards + (20 \\times Passing\;TDs) - (45 \\times INTs)}{Passing\;Attempts + Sacks}}$",
    ha="center",
    va="top",
    fontsize=25
)

ax_formula.set_xticks([])
ax_formula.set_yticks([])

# Performances
ax_team_stats.grid()
ax_team_stats.set_axisbelow(True)
ax_team_stats.plot([-2.5, 2.5], [0, 0], color="gray", lw=2, zorder=1)
ax_team_stats.plot([0, 0], [-15, 15], color="gray", lw=2, zorder=1)
ax_team_stats.set_xlabel("Relative ANY/A to League Average", labelpad=5)
ax_team_stats.set_ylabel("Net Points per Game", labelpad=5)
ax_team_stats.text(
    0.75, 0.95,
    "Above Average Passing Offense",
    ha="center",
    va="top",
    size=14, 
    transform=ax_team_stats.transAxes,
    path_effects=[pe.withStroke(linewidth=4, foreground="white")]
)
ax_team_stats.text(
    0.25, 0.95,
    "Below Average Passing Offense",
    ha="center",
    va="top",
    size=14,
    transform=ax_team_stats.transAxes,
    path_effects=[pe.withStroke(linewidth=4, foreground="white")]
)

# Add logos as scatter points
playoff_teams = ["GB", "TB", "DAL", "LA", "ARI", "SF", "PHI", "TEN", "KC", "BUF", "CIN", "LV", "NE", "PIT"]
for row in df_team_stats.itertuples(): 
    # Logos
    im = np.array(Image.open(f"./logo-{row.team}.png"))
    xy = (row.any_a - league_avg.any_a, row.net_pts / 17)
    
    if row.team not in playoff_teams:
        imbox = OffsetImage(im, zoom=0.05, alpha=0.25)
    else:
        imbox = OffsetImage(im, zoom=0.05)
    imbox.image.axes = ax_team_stats
    
    ab = AnnotationBbox(
        imbox, xy,
        frameon=False
    )
    
    ax_team_stats.add_artist(ab)

# League Average
ax_league.plot([3, 18], [league_avg.any_a, league_avg.any_a], linestyle="--", linewidth=3, color="gray", zorder=1)
ax_league.text(
    3, league_avg.any_a + 0.1, 
    f"League Average ({league_avg.any_a.round(2)})", 
    ha="left", 
    va="baseline",
    path_effects=[pe.withStroke(linewidth=4, foreground="white")]
)

# Dataframe to merge on all weeks and ffill
df_weeks = pd.DataFrame({"week": range(1, 19)})

# Plot Top 5 QBs
top_5_qbs = qb_list[:5]["full_name"]
midpoint_top_5 = (
    df_passing_cum[df_passing_cum["full_name"].isin(top_5_qbs)]
    .groupby("full_name", as_index=False)
    .apply(lambda grp: grp[grp["week"] == grp["week"].max()])["any_a"]
    .mean()
)
for i, qb in enumerate(top_5_qbs[::-1]):
    df_temp = (
        df_passing_cum[(df_passing_cum["full_name"] == qb) & (df_passing_cum["yards"] >= 1000)]
        .reset_index(drop=True)
        .merge(df_weeks, how="right")
        .ffill()
        .copy()
    )
    ax_league.plot(
        df_temp["week"], df_temp["any_a"], 
        color=qb_list[qb_list["full_name"] == qb].team_color.iloc[0], 
        linewidth=3, 
        markevery=[-1], 
        marker="o", 
        markersize=10, 
        markeredgewidth=2, 
        path_effects=[pe.withStroke(linewidth=6, foreground="white")],
        zorder=2
    )
    coords = (df_temp.iloc[-1].week, df_temp.iloc[-1].any_a)
    offset = (i - 2) * 0.3
    ax_league.annotate(
        f"{qb} ({df_temp.iloc[-1].any_a.round(2)})", 
        xy=coords, 
        xytext=(18.8, midpoint_top_5 + offset),
        color=qb_list[qb_list["full_name"] == qb].team_color.iloc[0],
        ha="left",
        va="center",
        arrowprops=dict(arrowstyle="-", relpos=(0, 0.5), linewidth=2, color=qb_list[qb_list["full_name"] == qb].team_color.iloc[0]),
        zorder=1
    )
    
ax_league.text(
    18.8, midpoint_top_5 + 0.9,
    "Top 5 ANY/A",
    ha="left",
    va="center",
    fontweight=600
)
    
# Plot rookie QBs
rookie_qbs = qb_list[qb_list["rookie"] == True]["full_name"]
midpoint_rookie = (
    df_passing_cum[df_passing_cum["full_name"].isin(rookie_qbs)]
    .groupby("full_name", as_index=False)
    .apply(lambda grp: grp[grp["week"] == grp["week"].max()])["any_a"]
    .mean()
)
for i, qb in enumerate(rookie_qbs[::-1]):
    df_temp = (
        df_passing_cum[(df_passing_cum["full_name"] == qb) & (df_passing_cum["yards"] >= 1000)]
        .reset_index(drop=True)
        .merge(df_weeks, how="right")
        .ffill()
        .copy()
    )
    ax_league.plot(
        df_temp["week"], df_temp["any_a"], 
        color=qb_list[qb_list["full_name"] == qb].team_color.iloc[0], 
        linewidth=3, 
        markevery=[-1], 
        marker="o", 
        markersize=10, 
        markeredgewidth=2, 
        path_effects=[pe.withStroke(linewidth=6, foreground="white")],
        zorder=2
    )
    coords = (df_temp.iloc[-1].week, df_temp.iloc[-1].any_a)
    offset = (i - 2) * 0.4
    ax_league.annotate(
        f"{qb} ({df_temp.iloc[-1].any_a.round(2)})", 
        xy=coords, 
        xytext=(18.8, midpoint_rookie + offset),
        color=qb_list[qb_list["full_name"] == qb].team_color.iloc[0],
        ha="left",
        va="center",
        arrowprops=dict(arrowstyle="-", relpos=(0, 0.5), linewidth=2, color=qb_list[qb_list["full_name"] == qb].team_color.iloc[0]),
        zorder=1
    )
    
ax_league.text(
    18.8, midpoint_rookie + 1.1,
    "Rookie ANY/A",
    ha="left",
    va="center",
    fontweight=600
)
    
# Plot rest of league in gray
for qb in qb_list["full_name"][5:]:
    df_temp = (
        df_passing_cum[(df_passing_cum["full_name"] == qb) & (df_passing_cum["yards"] >= 1000)]
        .reset_index(drop=True)
        .merge(df_weeks, how="right")
        .ffill()
        .copy()
    )
    ax_league.plot(df_temp["week"], df_temp["any_a"], linewidth=2, color="gray", alpha=0.2, zorder=1)

ax_league.set_xticklabels([])
ax_league.set_xticks(range(3, 19))
ax_league.set_xlim(2.5, 18.5)
ax_league.annotate(
    "Week", 
    xy=(9, 2.9), xytext=(12, 2.9), 
    ha="center", va="center", 
    fontsize=18, 
    color="#4e4e4e",
    arrowprops=dict(arrowstyle="-", relpos=(0, 0.5), linewidth=2, color="#4e4e4e"), 
    annotation_clip=False
)
ax_league.annotate(
    "", 
    xy=(15, 2.9), xytext=(12.8, 2.9), 
    arrowprops=dict(arrowstyle="->", linewidth=2, color="#4e4e4e"), 
    annotation_clip=False
)

ax_league.set_yticklabels([])
ax_league.set_ylim(3, 10)

# Add Title
ax_title.text(0.55, 0.5, "Ranking Passers During the 2021-22 NFL Season by ANY/A", ha="center", va="center", size=30, fontweight=600)
ax_title.set(xticks=[], yticks=[])

# Add Source
ax_league.text(
    1.4, -0.1,
    "@naveen_v92\nSource: github.com/fantasydatapros/nflfastpy",
    ha="right",
    va="baseline",
    size=14,
    transform=ax_league.transAxes
)

plt.savefig("adjusted_net_yards_pts.png", dpi=300, bbox_inches="tight")
plt.show()
# -



StarcoderdataJupyterScriptsDedupFiltered