Skip to contents

Preface

nflseedR essentially performs two tasks:

  1. Calculation of NFL standings with nfl_standings() based on game results of one or more seasons, especially taking into account the comprehensive and sometimes complicated tie-breaking procedures for division ranks, conference seeds and the draft order. Read this article for further information on the implemented tie-breakers.
  2. Running thousands of simulations (Monte Carlo style) of an NFL season with nfl_simulations(). The standings from point 1 and especially the conference seeds are needed to determine playoff participants. Basically, the first point only exists because we need it to carry out the simulations.

The actual core of a simulation is the generation of game results based on any information that the user deems important. This is why nflseedR is virtually extensible. By default, a simple ELO model is implemented that works with initial starting ELO values and updates them from week to week based on game results. However, the user can write their own function for calculating game results and pass it to nflseedR together with any additional data that may be required.

Usage

Standings

We need real or simulated match data to determine standings. The required variables are specified in the documentation of the function nfl_standings().

Here are games data from the 2023 and 2024 seasons.

games <- nflreadr::load_schedules(2023:2024)

We can pass this data directly to nflseedR and calculate standings. It defaults to compute division ranks as well as conference ranks for all teams and it applies tiebreakers through strength of schedule.

standings <- nflseedR::nfl_standings(games, ranks = "DRAFT")
#>  16:31:14 | Initiate Standings & Tiebreaking Data
#>  16:31:14 | Compute Division Ranks
#>  16:31:14 | Compute Conference Ranks
#>  16:31:14 | Compute Draft Order
# Let's view the structure of the output
str(standings, max.level = 1, width = 50, strict.width = "cut")
#> Classes 'data.table' and 'data.frame':   64 obs. of  24 variables:
#>  $ season             : int  2023 2023 2023 2023..
#>  $ team               : chr  "BUF" "MIA" "NYJ" "..
#>  $ conf               : chr  "AFC" "AFC" "AFC" "..
#>  $ division           : chr  "AFC East" "AFC Ea"..
#>  $ games              : int  17 17 17 17 17 17 1..
#>  $ wins               : num  11 11 7 4 13 11 10 ..
#>  $ true_wins          : int  11 11 7 4 13 11 10 ..
#>  $ losses             : int  6 6 10 13 4 6 7 8 7..
#>  $ ties               : int  0 0 0 0 0 0 0 0 0 0..
#>  $ pf                 : int  451 496 268 236 483..
#>  $ pa                 : int  311 391 355 366 280..
#>  $ pd                 : int  140 105 -87 -130 20..
#>  $ win_pct            : num  0.647 0.647 0.412 0..
#>  $ div_pct            : num  0.667 0.667 0.333 0..
#>  $ conf_pct           : num  0.583 0.583 0.333 0..
#>  $ sov                : num  0.471 0.358 0.454 0..
#>  $ sos                : num  0.471 0.45 0.502 0...
#>  $ div_rank           : int  1 2 3 4 1 2 3 4 1 2..
#>  $ div_tie_broken_by  : chr  "Head-To-Head Win "..
#>  $ conf_rank          : int  2 6 13 16 1 5 7 8 4..
#>  $ conf_tie_broken_by : chr  "Head-To-Head Swee"..
#>  $ exit               : int  19 18 0 0 20 18 18 ..
#>  $ draft_rank         : int  28 21 10 3 30 23 20..
#>  $ draft_tie_broken_by: chr  NA NA NA NA ...
#>  - attr(*, ".internal.selfref")=<externalptr>

nflseedR also provides functionality to create a “pretty” html table using the gt package. Use nfl_standings_prettify() with the output of nfl_standings() to create the table. It allows grouping by division, conference or overall and it can sort by division rank, conference rank (seed), and draft rank.

The default groups by division and sorts by division rank.

# It doesn't allow more than one season
s <- standings[season == 2024]
nflseedR::nfl_standings_prettify(s)
2024 NFL Standings
Div
Rank
team
G
W
L
T
pf
pa
pd
pct
Div
PCT
Conf
PCT
sov
sos
Division Tie
broken by
Conf
Rank
Conference Tie
broken by
Draft
Rank
Draft Tie
broken by
AFC East
1 The BUF NFL logo 17 13 4 0 525 368 157 0.765 0.833 0.750 0.448 0.467 2 30
2 The MIA NFL logo 17 8 9 0 345 364 -19 0.471 0.500 0.500 0.294 0.419 10 13
3 The NYJ NFL logo 17 5 12 0 338 404 -66 0.294 0.333 0.417 0.341 0.495 11 7
4 The NE NFL logo 17 4 13 0 289 417 -128 0.235 0.333 0.250 0.471 0.471 13 SOV (2) 4
AFC North
1 The BAL NFL logo 17 12 5 0 518 361 157 0.706 0.667 0.667 0.525 0.529 3 27
2 The PIT NFL logo 17 10 7 0 380 347 33 0.588 0.500 0.583 0.453 0.502 6 Head-To-Head Sweep (2) 21 Conference Tiebreaker
3 The CIN NFL logo 17 9 8 0 472 434 38 0.529 0.500 0.500 0.314 0.478 8 17
4 The CLE NFL logo 17 3 14 0 258 435 -177 0.176 0.333 0.250 0.510 0.536 15 SOV (2) 2
AFC South
1 The HOU NFL logo 17 10 7 0 372 372 0 0.588 0.833 0.667 0.376 0.481 4 25
2 The IND NFL logo 17 8 9 0 377 427 -50 0.471 0.500 0.583 0.309 0.457 9 Head-To-Head Sweep (2) 14
3 The JAX NFL logo 17 4 13 0 320 435 -115 0.235 0.500 0.333 0.265 0.478 12 Conference Win PCT (3) 5
4 The TEN NFL logo 17 3 14 0 311 460 -149 0.176 0.167 0.250 0.431 0.522 16 1
AFC West
1 The KC NFL logo 17 15 2 0 385 326 59 0.882 0.833 0.833 0.463 0.488 1 31
2 The LAC NFL logo 17 11 6 0 402 301 101 0.647 0.667 0.667 0.348 0.467 5 22
3 The DEN NFL logo 17 10 7 0 425 311 114 0.588 0.500 0.500 0.394 0.502 7 20 Conference Tiebreaker
4 The LV NFL logo 17 4 13 0 309 434 -125 0.235 0.000 0.250 0.353 0.540 14 6
NFC East
1 The PHI NFL logo 17 14 3 0 463 303 160 0.824 0.833 0.750 0.424 0.453 2 32
2 The WAS NFL logo 17 12 5 0 485 391 94 0.706 0.667 0.750 0.358 0.436 6 29
3 The DAL NFL logo 17 7 10 0 350 468 -118 0.412 0.500 0.417 0.387 0.522 11 12
4 The NYG NFL logo 17 3 14 0 273 415 -142 0.176 0.000 0.083 0.412 0.554 16 3
NFC North
1 The DET NFL logo 17 15 2 0 564 342 222 0.882 1.000 0.917 0.494 0.516 1 28
2 The MIN NFL logo 17 14 3 0 432 332 100 0.824 0.667 0.750 0.408 0.474 5 24
3 The GB NFL logo 17 11 6 0 460 338 122 0.647 0.167 0.500 0.412 0.533 7 23
4 The CHI NFL logo 17 5 12 0 310 370 -60 0.294 0.167 0.250 0.388 0.554 13 Head-To-Head Sweep (2) 10
NFC South
1 The TB NFL logo 17 10 7 0 502 385 117 0.588 0.667 0.667 0.465 0.502 3 Conference Win PCT (2) 19 Head-To-Head (2)
2 The ATL NFL logo 17 8 9 0 389 423 -34 0.471 0.667 0.583 0.426 0.519 9 Conference Win PCT (2) 15
3 The CAR NFL logo 17 5 12 0 341 534 -193 0.294 0.333 0.333 0.329 0.498 SOV (2) 14 Division Tiebreaker 8
4 The NO NFL logo 17 5 12 0 338 398 -60 0.294 0.333 0.333 0.306 0.505 SOV (2) 15 Division Tiebreaker 9
NFC West
1 The LA NFL logo 17 10 7 0 367 386 -19 0.588 0.667 0.500 0.441 0.505 SOV (2) 4 26
2 The SEA NFL logo 17 10 7 0 375 368 7 0.588 0.667 0.500 0.424 0.498 SOV (2) 8 18
3 The ARI NFL logo 17 8 9 0 400 379 21 0.471 0.500 0.333 0.404 0.536 10 16
4 The SF NFL logo 17 6 11 0 389 436 -47 0.353 0.167 0.333 0.402 0.564 12 11
nflseedR

But we can also do things like ordering the complete league by draft rank.

nflseedR::nfl_standings_prettify(s, grp_by = "nfl", order_by = "draft_rank")
2024 NFL Standings
Draft
Rank
team
G
W
L
T
pf
pa
pd
pct
Div
PCT
Conf
PCT
sov
sos
Div
Rank
Division Tie
broken by
Conf
Rank
Conference Tie
broken by
Draft Tie
broken by
1 The TEN NFL logo 17 3 14 0 311 460 -149 0.176 0.167 0.250 0.431 0.522 4 16
2 The CLE NFL logo 17 3 14 0 258 435 -177 0.176 0.333 0.250 0.510 0.536 4 15 SOV (2)
3 The NYG NFL logo 17 3 14 0 273 415 -142 0.176 0.000 0.083 0.412 0.554 4 16
4 The NE NFL logo 17 4 13 0 289 417 -128 0.235 0.333 0.250 0.471 0.471 4 13 SOV (2)
5 The JAX NFL logo 17 4 13 0 320 435 -115 0.235 0.500 0.333 0.265 0.478 3 12 Conference Win PCT (3)
6 The LV NFL logo 17 4 13 0 309 434 -125 0.235 0.000 0.250 0.353 0.540 4 14
7 The NYJ NFL logo 17 5 12 0 338 404 -66 0.294 0.333 0.417 0.341 0.495 3 11
8 The CAR NFL logo 17 5 12 0 341 534 -193 0.294 0.333 0.333 0.329 0.498 3 SOV (2) 14 Division Tiebreaker
9 The NO NFL logo 17 5 12 0 338 398 -60 0.294 0.333 0.333 0.306 0.505 4 SOV (2) 15 Division Tiebreaker
10 The CHI NFL logo 17 5 12 0 310 370 -60 0.294 0.167 0.250 0.388 0.554 4 13 Head-To-Head Sweep (2)
11 The SF NFL logo 17 6 11 0 389 436 -47 0.353 0.167 0.333 0.402 0.564 4 12
12 The DAL NFL logo 17 7 10 0 350 468 -118 0.412 0.500 0.417 0.387 0.522 3 11
13 The MIA NFL logo 17 8 9 0 345 364 -19 0.471 0.500 0.500 0.294 0.419 2 10
14 The IND NFL logo 17 8 9 0 377 427 -50 0.471 0.500 0.583 0.309 0.457 2 9 Head-To-Head Sweep (2)
15 The ATL NFL logo 17 8 9 0 389 423 -34 0.471 0.667 0.583 0.426 0.519 2 9 Conference Win PCT (2)
16 The ARI NFL logo 17 8 9 0 400 379 21 0.471 0.500 0.333 0.404 0.536 3 10
17 The CIN NFL logo 17 9 8 0 472 434 38 0.529 0.500 0.500 0.314 0.478 3 8
18 The SEA NFL logo 17 10 7 0 375 368 7 0.588 0.667 0.500 0.424 0.498 2 SOV (2) 8
19 The TB NFL logo 17 10 7 0 502 385 117 0.588 0.667 0.667 0.465 0.502 1 3 Conference Win PCT (2) Head-To-Head (2)
20 The DEN NFL logo 17 10 7 0 425 311 114 0.588 0.500 0.500 0.394 0.502 3 7 Conference Tiebreaker
21 The PIT NFL logo 17 10 7 0 380 347 33 0.588 0.500 0.583 0.453 0.502 2 6 Head-To-Head Sweep (2) Conference Tiebreaker
22 The LAC NFL logo 17 11 6 0 402 301 101 0.647 0.667 0.667 0.348 0.467 2 5
23 The GB NFL logo 17 11 6 0 460 338 122 0.647 0.167 0.500 0.412 0.533 3 7
24 The MIN NFL logo 17 14 3 0 432 332 100 0.824 0.667 0.750 0.408 0.474 2 5
25 The HOU NFL logo 17 10 7 0 372 372 0 0.588 0.833 0.667 0.376 0.481 1 4
26 The LA NFL logo 17 10 7 0 367 386 -19 0.588 0.667 0.500 0.441 0.505 1 SOV (2) 4
27 The BAL NFL logo 17 12 5 0 518 361 157 0.706 0.667 0.667 0.525 0.529 1 3
28 The DET NFL logo 17 15 2 0 564 342 222 0.882 1.000 0.917 0.494 0.516 1 1
29 The WAS NFL logo 17 12 5 0 485 391 94 0.706 0.667 0.750 0.358 0.436 2 6
30 The BUF NFL logo 17 13 4 0 525 368 157 0.765 0.833 0.750 0.448 0.467 1 2
31 The KC NFL logo 17 15 2 0 385 326 59 0.882 0.833 0.833 0.463 0.488 1 1
32 The PHI NFL logo 17 14 3 0 463 303 160 0.824 0.833 0.750 0.424 0.453 1 2
nflseedR

Please note that nfl_standings_prettify() returns a gt::gt() table so you can change it according to your own preferences.

Simulations

With nflseedR 2.0, we have rethought and implemented the execution of simulations from scratch. Particular attention was paid to flexibility and performance. As the usage of the new function nfl_simulations() differs from the old function simulate_nfl(), we will keep both variants for the time being and maintain two separate articles explaining how to use them.

It is strongly recommended to switch to nfl_simulations() because it is far superior to the old implementation in practically every respect, especially in terms of performance.