-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnfl_examples.py
More file actions
802 lines (695 loc) · 26 KB
/
nfl_examples.py
File metadata and controls
802 lines (695 loc) · 26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
#!/usr/bin/env python3
"""
nflplotpy Examples - 2024 NFL Season
This script creates example plots using NFL data from nfl_data_py:
1. All 32 teams offensive vs defensive EPA per play (2024 season)
2. Division-by-division breakdown (8 subplots)
3. Conference comparison
Uses actual play-by-play data aggregated over the 2024 regular season.
Automatically caches processed data for faster subsequent runs.
"""
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec
import os
import sys
# Import nflplotpy and nfl_data_py
sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))
import nflplotpy as nflplot
import nfl_data_py as nfl
def load_and_process_2024_data():
"""Load 2024 NFL team EPA data - uses cached data if available, otherwise downloads fresh data."""
cache_file = os.path.join(os.path.dirname(__file__), "2024_nfl_team_epa_data.csv")
if os.path.exists(cache_file):
print(f"✅ Loading cached 2024 data from: {cache_file}")
team_stats = pd.read_csv(cache_file)
print(f"Loaded cached data for {len(team_stats)} teams")
else:
print(
"📥 Cached data not found. Downloading fresh 2024 NFL play-by-play data..."
)
# Load 2024 regular season data
pbp = nfl.import_pbp_data([2024])
print(f"Loaded {len(pbp):,} plays from 2024 season")
# Filter for regular season only
pbp_reg = pbp[pbp["season_type"] == "REG"].copy()
print(f"Regular season plays: {len(pbp_reg):,}")
# Remove plays with missing EPA or team data
pbp_clean = pbp_reg[
(pbp_reg["epa"].notna())
& (pbp_reg["posteam"].notna())
& (pbp_reg["defteam"].notna())
].copy()
print(f"Clean plays with EPA data: {len(pbp_clean):,}")
# Calculate offensive EPA per play by team
print("Calculating offensive EPA per play...")
offensive_stats = (
pbp_clean.groupby("posteam")
.agg({"epa": ["mean", "count", "sum"], "play_id": "count"})
.round(4)
)
offensive_stats.columns = [
"off_epa_per_play",
"off_epa_count",
"off_total_epa",
"off_total_plays",
]
offensive_stats = offensive_stats.reset_index()
offensive_stats.columns = [
"team",
"off_epa_per_play",
"off_epa_count",
"off_total_epa",
"off_total_plays",
]
# Calculate defensive EPA per play allowed by team
print("Calculating defensive EPA per play allowed...")
defensive_stats = (
pbp_clean.groupby("defteam")
.agg({"epa": ["mean", "count", "sum"], "play_id": "count"})
.round(4)
)
defensive_stats.columns = [
"def_epa_per_play",
"def_epa_count",
"def_total_epa",
"def_total_plays",
]
defensive_stats = defensive_stats.reset_index()
defensive_stats.columns = [
"team",
"def_epa_per_play",
"def_epa_count",
"def_total_epa",
"def_total_plays",
]
# Merge offensive and defensive stats
team_stats = pd.merge(offensive_stats, defensive_stats, on="team", how="inner")
# Filter for teams with reasonable play counts (removes weird edge cases)
min_plays = 800 # Reasonable threshold for a full season
team_stats = team_stats[
(team_stats["off_total_plays"] >= min_plays)
& (team_stats["def_total_plays"] >= min_plays)
]
# Save the processed data for next time
team_stats.to_csv(cache_file, index=False)
print(f"💾 Cached processed data to: {cache_file}")
# Display summary info
print(f"Final dataset: {len(team_stats)} teams")
print(
f"Offensive EPA range: {team_stats['off_epa_per_play'].min():.3f} to {team_stats['off_epa_per_play'].max():.3f}"
)
print(
f"Defensive EPA range: {team_stats['def_epa_per_play'].min():.3f} to {team_stats['def_epa_per_play'].max():.3f}"
)
# Display top/bottom teams
print("\nTop 5 Offensive Teams (EPA/play):")
top_off = team_stats.nlargest(5, "off_epa_per_play")[["team", "off_epa_per_play"]]
print(top_off.to_string(index=False))
print("\nTop 5 Defensive Teams (lowest EPA/play allowed):")
top_def = team_stats.nsmallest(5, "def_epa_per_play")[["team", "def_epa_per_play"]]
print(top_def.to_string(index=False))
return team_stats[["team", "off_epa_per_play", "def_epa_per_play"]]
def get_division_teams():
"""Get teams organized by division.
Uses the team abbreviations that match nfl_data_py output:
- JAX (not JAC) for Jacksonville Jaguars
- LA (not LAR) for Los Angeles Rams
"""
return {
"AFC East": ["BUF", "MIA", "NE", "NYJ"],
"AFC North": ["BAL", "CIN", "CLE", "PIT"],
"AFC South": ["HOU", "IND", "JAX", "TEN"], # JAX not JAC
"AFC West": ["DEN", "KC", "LV", "LAC"],
"NFC East": ["DAL", "NYG", "PHI", "WAS"],
"NFC North": ["CHI", "DET", "GB", "MIN"],
"NFC South": ["ATL", "CAR", "NO", "TB"],
"NFC West": ["ARI", "LA", "SEA", "SF"], # LA not LAR
}
def create_all_teams_plot(data, show_logos=True):
"""Create plot with all 32 teams - offensive vs defensive EPA."""
print("Creating all teams plot...")
fig, ax = plt.subplots(figsize=(18, 14))
# Get team colors - validate teams first
valid_teams = nflplot.validate_teams(data["team"].tolist(), allow_conferences=False)
colors = nflplot.get_team_colors(valid_teams, "primary")
if show_logos:
# Create invisible scatter plot for positioning
scatter = ax.scatter(
data["off_epa_per_play"],
data["def_epa_per_play"],
c="white",
s=1,
alpha=0.01, # Nearly invisible
zorder=1,
)
# Add NFL team logos
from nflplotpy.matplotlib.artists import add_nfl_logos
try:
logos = add_nfl_logos(
ax,
data["team"].tolist(),
data["off_epa_per_play"].values,
data["def_epa_per_play"].values,
target_width_pixels=60,
alpha=0.9,
)
successful_logos = len([l for l in logos if l is not None])
print(f"✅ Successfully added {successful_logos} team logos")
except Exception as e:
print(f"⚠️ Logo rendering had issues: {e}")
# Fall back to colored dots
show_logos = False
if not show_logos:
# Traditional scatter plot with team colors
scatter = ax.scatter(
data["off_epa_per_play"],
data["def_epa_per_play"],
c=colors,
s=200,
alpha=0.8,
edgecolors="white",
linewidth=2,
zorder=3,
)
# Add team labels for dots
for _, row in data.iterrows():
ax.annotate(
row["team"],
(row["off_epa_per_play"], row["def_epa_per_play"]),
xytext=(3, 3),
textcoords="offset points",
fontsize=9,
fontweight="bold",
color="white",
bbox=dict(boxstyle="round,pad=0.2", facecolor="black", alpha=0.7),
)
# Add reference lines at league averages
league_avg_off = data["off_epa_per_play"].mean()
league_avg_def = data["def_epa_per_play"].mean()
ax.axhline(
y=league_avg_def,
color="gray",
linestyle="--",
alpha=0.6,
zorder=1,
label=f"League Avg Defense ({league_avg_def:.3f})",
)
ax.axvline(
x=league_avg_off,
color="gray",
linestyle="--",
alpha=0.6,
zorder=1,
label=f"League Avg Offense ({league_avg_off:.3f})",
)
# Add quadrant background colors using rectangles
from matplotlib.patches import Rectangle
xlims = ax.get_xlim()
ylims = ax.get_ylim()
# Q1 (upper right): Good Offense, Poor Defense - Orange (barnburner games)
rect1 = Rectangle(
(league_avg_off, league_avg_def),
xlims[1] - league_avg_off,
ylims[1] - league_avg_def,
alpha=0.18,
color="darkorange",
zorder=0,
)
ax.add_patch(rect1)
# Q2 (lower right): Good Offense, Good Defense - Blue (blue chip teams)
rect2 = Rectangle(
(league_avg_off, ylims[0]),
xlims[1] - league_avg_off,
league_avg_def - ylims[0],
alpha=0.18,
color="royalblue",
zorder=0,
)
ax.add_patch(rect2)
# Q3 (lower left): Poor Offense, Good Defense - Yellow (grind 'em out games)
rect3 = Rectangle(
(xlims[0], ylims[0]),
league_avg_off - xlims[0],
league_avg_def - ylims[0],
alpha=0.18,
color="goldenrod",
zorder=0,
)
ax.add_patch(rect3)
# Q4 (upper left): Poor Offense, Poor Defense - Red (bad teams)
rect4 = Rectangle(
(xlims[0], league_avg_def),
league_avg_off - xlims[0],
ylims[1] - league_avg_def,
alpha=0.18,
color="crimson",
zorder=0,
)
ax.add_patch(rect4)
# Add quadrant labels with better contrast
ax.text(
0.02,
0.98,
'Poor Offense\nPoor Defense\n"Scouting the SEC"',
transform=ax.transAxes,
fontsize=12,
ha="left",
va="top",
alpha=1.0,
fontweight="bold",
color="white",
bbox=dict(
boxstyle="round,pad=0.5",
facecolor="darkred",
alpha=0.9,
edgecolor="white",
linewidth=2,
),
)
ax.text(
0.98,
0.98,
'Good Offense\nPoor Defense\n"Barn burners"',
transform=ax.transAxes,
fontsize=12,
ha="right",
va="top",
alpha=1.0,
fontweight="bold",
color="white",
bbox=dict(
boxstyle="round,pad=0.5",
facecolor="darkorange",
alpha=0.9,
edgecolor="white",
linewidth=2,
),
)
ax.text(
0.02,
0.02,
'Poor Offense\nGood Defense\n"Grind It Out"',
transform=ax.transAxes,
fontsize=12,
ha="left",
va="bottom",
alpha=1.0,
fontweight="bold",
color="black",
bbox=dict(
boxstyle="round,pad=0.5",
facecolor="gold",
alpha=0.9,
edgecolor="black",
linewidth=2,
),
)
ax.text(
0.98,
0.02,
'Good Offense\nGood Defense\n"Blue Chip"',
transform=ax.transAxes,
fontsize=12,
ha="right",
va="bottom",
alpha=1.0,
fontweight="bold",
color="white",
bbox=dict(
boxstyle="round,pad=0.5",
facecolor="navy",
alpha=0.9,
edgecolor="white",
linewidth=2,
),
)
# Styling
ax.set_xlabel("Offensive EPA per Play", fontsize=14, fontweight="bold")
ax.set_ylabel("Defensive EPA per Play Allowed", fontsize=14, fontweight="bold")
ax.set_title(
"2024 NFL Team EPA Analysis\nOffensive vs Defensive (allowed) EPA/Play (Regular Season)",
fontsize=16,
fontweight="bold",
pad=20,
)
# Apply NFL theme
nflplot.apply_nfl_theme(ax, style="default")
# Add grid
ax.grid(True, alpha=0.3, zorder=0)
plt.tight_layout(rect=[0, 0.05, 1, 1]) # Leave more space at bottom (1/4 height)
# Add explanatory text with more space
fig.text(
0.5,
0.04,
"Data: 2024 NFL Regular Season Play-by-Play • Lower defensive EPA is better • Higher offensive EPA is better",
ha="center",
fontsize=11,
style="italic",
alpha=0.7,
)
# Save in nflplotpy examples directory
output_dir = os.path.dirname(__file__)
output_path = os.path.join(output_dir, "2024_all_teams_epa.png")
plt.savefig(output_path, dpi=600, bbox_inches="tight", facecolor="white")
print(f"Saved: {output_path}")
return fig
def create_division_plots(data, show_logos=True):
"""Create 8 subplot figure showing each division separately."""
print("Creating division plots with 2024 pbp data...")
if show_logos:
print("🏈 Using team logos in division breakdown!")
divisions = get_division_teams()
# Create figure with 2x4 subplot grid
fig = plt.figure(figsize=(28, 16))
gs = GridSpec(2, 4, figure=fig, hspace=0.4, wspace=0.35)
for i, (division, teams) in enumerate(divisions.items()):
# Calculate subplot position
row = i // 4
col = i % 4
ax = fig.add_subplot(gs[row, col])
# Filter data for this division - only include teams present in our data
available_teams = [t for t in teams if t in data["team"].values]
div_data = data[data["team"].isin(available_teams)].copy()
if div_data.empty:
ax.text(
0.5,
0.5,
f"No data for\n{division}",
ha="center",
va="center",
transform=ax.transAxes,
)
ax.set_title(division, fontsize=14, fontweight="bold", pad=10)
continue
# Get team colors for this division
colors = nflplot.get_team_colors(div_data["team"].tolist(), "primary")
if show_logos:
# Create invisible scatter plot for positioning
ax.scatter(
div_data["off_epa_per_play"],
div_data["def_epa_per_play"],
c="white",
s=1,
alpha=0.01,
)
# Add team logos
from nflplotpy.matplotlib.artists import add_nfl_logos
try:
logos = add_nfl_logos(
ax,
div_data["team"].tolist(),
div_data["off_epa_per_play"].values,
div_data["def_epa_per_play"].values,
target_width_pixels=65, # Higher resolution for division plots
)
except Exception as e:
print(f"⚠️ Division {division} logo issues: {e}")
show_logos = False # Fall back for this division
if not show_logos:
# Traditional scatter plot
scatter = ax.scatter(
div_data["off_epa_per_play"],
div_data["def_epa_per_play"],
c=colors,
s=300,
alpha=0.8,
edgecolors="white",
linewidth=2,
)
# Add team labels for dots
for _, row in div_data.iterrows():
ax.annotate(
row["team"],
(row["off_epa_per_play"], row["def_epa_per_play"]),
xytext=(5, 5),
textcoords="offset points",
fontsize=12,
fontweight="bold",
color="white",
bbox=dict(boxstyle="round,pad=0.3", facecolor="black", alpha=0.8),
)
# Add reference lines - league and division averages
league_avg_off = data["off_epa_per_play"].mean()
league_avg_def = data["def_epa_per_play"].mean()
div_avg_off = div_data["off_epa_per_play"].mean()
div_avg_def = div_data["def_epa_per_play"].mean()
# League averages (gray dashed)
ax.axhline(
y=league_avg_def,
color="gray",
linestyle="--",
alpha=0.5,
label="League Avg",
)
ax.axvline(x=league_avg_off, color="gray", linestyle="--", alpha=0.5)
# Division averages with conference colors
div_color = "red" if "AFC" in division else "blue"
ax.axhline(
y=div_avg_def,
color=div_color,
linestyle=":",
alpha=0.8,
linewidth=2,
label=f"{division} Avg",
)
ax.axvline(
x=div_avg_off, color=div_color, linestyle=":", alpha=0.8, linewidth=2
)
# Styling
ax.set_title(f"{division}", fontsize=12, fontweight="bold", pad=10)
ax.set_xlabel("Offensive EPA/Play", fontsize=10)
ax.set_ylabel("Defensive EPA/Play", fontsize=10)
# Apply NFL theme
nflplot.apply_nfl_theme(ax, style="minimal")
ax.grid(True, alpha=0.2)
# Add legend for reference lines
ax.legend(loc="upper right", fontsize=8, framealpha=0.9)
# Set consistent axis limits with extra padding for logos
x_range = data["off_epa_per_play"].max() - data["off_epa_per_play"].min()
y_range = data["def_epa_per_play"].max() - data["def_epa_per_play"].min()
ax.set_xlim(
data["off_epa_per_play"].min() - x_range * 0.05,
data["off_epa_per_play"].max() + x_range * 0.05,
)
ax.set_ylim(
data["def_epa_per_play"].min() - y_range * 0.05,
data["def_epa_per_play"].max() + y_range * 0.05,
)
# Overall title
fig.suptitle(
"2024 NFL Team Performance by Division\nOffensive vs Defensive EPA per Play (Regular Season)",
fontsize=18,
fontweight="bold",
y=0.98,
)
# Add explanatory text
fig.text(
0.5,
0.01,
"Data: 2024 NFL Regular Season Play-by-Play • Lower defensive EPA = better defense • Higher offensive EPA = better offense",
ha="center",
fontsize=12,
style="italic",
alpha=0.7,
)
# Save in nflplotpy examples directory
output_dir = os.path.dirname(__file__)
output_path = os.path.join(output_dir, "2024_divisions_epa.png")
plt.savefig(output_path, dpi=600, bbox_inches="tight", facecolor="white")
print(f"Saved: {output_path}")
return fig
def create_conference_comparison(data, show_logos=True):
"""Create AFC vs NFC comparison."""
print("Creating conference comparison with 2024 pbp data...")
if show_logos:
print("🏈 Using team logos in conference comparison!")
# Define AFC teams (using correct abbreviations from data)
afc_teams = [
"BUF",
"MIA",
"NE",
"NYJ",
"BAL",
"CIN",
"CLE",
"PIT",
"HOU",
"IND",
"JAX",
"TEN",
"DEN",
"KC",
"LV",
"LAC",
] # JAX not JAC
# Add conference column
data_with_conf = data.copy()
data_with_conf["conference"] = data_with_conf["team"].apply(
lambda x: "AFC" if x in afc_teams else "NFC"
)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 12))
for i, conf in enumerate(["AFC", "NFC"]):
ax = ax1 if i == 0 else ax2
conf_data = data_with_conf[data_with_conf["conference"] == conf]
if conf_data.empty:
ax.text(
0.5,
0.5,
f"No data for {conf}",
ha="center",
va="center",
transform=ax.transAxes,
)
continue
# Get colors
colors = nflplot.get_team_colors(conf_data["team"].tolist(), "primary")
if show_logos:
# Create invisible scatter plot for positioning
ax.scatter(
conf_data["off_epa_per_play"],
conf_data["def_epa_per_play"],
c="white",
s=1,
alpha=0.01,
)
# Add team logos
from nflplotpy.matplotlib.artists import add_nfl_logos
try:
logos = add_nfl_logos(
ax,
conf_data["team"].tolist(),
conf_data["off_epa_per_play"].values,
conf_data["def_epa_per_play"].values,
target_width_pixels=62,
)
except Exception as e:
print(f"⚠️ Conference {conf} logo issues: {e}")
show_logos = False # Fall back for this conference
if not show_logos:
# Traditional scatter plot
scatter = ax.scatter(
conf_data["off_epa_per_play"],
conf_data["def_epa_per_play"],
c=colors,
s=200,
alpha=0.8,
edgecolors="white",
linewidth=2,
)
# Add team labels for dots
for _, row in conf_data.iterrows():
ax.annotate(
row["team"],
(row["off_epa_per_play"], row["def_epa_per_play"]),
xytext=(3, 3),
textcoords="offset points",
fontsize=9,
fontweight="bold",
color="white",
bbox=dict(boxstyle="round,pad=0.2", facecolor="black", alpha=0.7),
)
# Calculate conference averages
avg_off = conf_data["off_epa_per_play"].mean()
avg_def = conf_data["def_epa_per_play"].mean()
# Add reference lines - both league and conference averages
league_avg_off = data["off_epa_per_play"].mean()
league_avg_def = data["def_epa_per_play"].mean()
ax.axhline(
y=league_avg_def,
color="gray",
linestyle="--",
alpha=0.4,
label="League Avg",
)
ax.axvline(x=league_avg_off, color="gray", linestyle="--", alpha=0.4)
# Add conference-specific averages with proper colors
conf_color = "red" if conf == "AFC" else "blue"
ax.axhline(
y=avg_def,
color=conf_color,
linestyle=":",
alpha=0.8,
linewidth=2,
label=f"{conf} Avg",
)
ax.axvline(x=avg_off, color=conf_color, linestyle=":", alpha=0.8, linewidth=2)
# Styling
ax.set_title(f"{conf} Conference", fontsize=14, fontweight="bold")
ax.set_xlabel("Offensive EPA per Play", fontsize=12)
ax.set_ylabel("Defensive EPA per Play", fontsize=12)
# Add conference averages as text
ax.text(
0.02,
0.02,
f"Avg Offense: {avg_off:.3f}\nAvg Defense: {avg_def:.3f}",
transform=ax.transAxes,
fontsize=10,
bbox=dict(boxstyle="round,pad=0.3", facecolor="white", alpha=0.8),
)
# Apply NFL theme
nflplot.apply_nfl_theme(ax, style="default")
ax.grid(True, alpha=0.3)
# Add legend for reference lines
ax.legend(loc="upper right", fontsize=9, framealpha=0.9)
# Set consistent limits with extra padding for logos
x_range = data["off_epa_per_play"].max() - data["off_epa_per_play"].min()
y_range = data["def_epa_per_play"].max() - data["def_epa_per_play"].min()
ax.set_xlim(
data["off_epa_per_play"].min() - x_range * 0.05,
data["off_epa_per_play"].max() + x_range * 0.05,
)
ax.set_ylim(
data["def_epa_per_play"].min() - y_range * 0.05,
data["def_epa_per_play"].max() + y_range * 0.05,
)
plt.suptitle(
"2024 NFL AFC vs NFC Performance Comparison", fontsize=16, fontweight="bold"
)
plt.tight_layout()
# Save in nflplotpy examples directory
output_dir = os.path.dirname(__file__)
output_path = os.path.join(output_dir, "2024_conferences_epa.png")
plt.savefig(output_path, dpi=600, bbox_inches="tight", facecolor="white")
print(f"Saved: {output_path}")
return fig
def main():
"""Create all example plots using 2024 NFL data."""
print("nflplotpy Examples - 2024 NFL Season")
print("=" * 50)
try:
# Load and process data (cached if available)
data = load_and_process_2024_data()
print(f"\nSuccessfully processed data for {len(data)} teams")
# Create plots with team logos enabled!
print("\n" + "=" * 50)
print("🏈 Creating all plots with team logos enabled!")
show_logos = True # The key setting - enables logos instead of dots
fig1 = create_all_teams_plot(data, show_logos=show_logos)
plt.close(fig1)
fig2 = create_division_plots(data, show_logos=show_logos)
plt.close(fig2)
fig3 = create_conference_comparison(data, show_logos=show_logos)
plt.close(fig3)
print("\n" + "=" * 50)
print("✅ All plots created successfully using 2024 NFL data.")
print("\nGenerated files:")
print("- nflplotpy/examples/2024_all_teams_epa.png")
print("- nflplotpy/examples/2024_divisions_epa.png")
print("- nflplotpy/examples/2024_conferences_epa.png")
print("\n📊 These plots showcase:")
print("✓ 2024 NFL play-by-play data aggregated by team")
print("✓ Authentic team performance metrics (EPA per play)")
print("✓ Professional NFL visualization styling")
print("✓ Publication-quality output suitable for analysis")
print("\n🏈 Data source: nfl_data_py 2024 regular season play-by-play")
except Exception as e:
print(f"\n❌ Error creating plots: {e}")
print(
"Make sure you have nfl_data_py installed and working internet connection"
)
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()