NFL’s 2022 Season Analysis

code
analysis
sports
Author

Lucas Paiva

Published

November 6, 2022

Let’s work a little bit with NFL data from the nflfastR package in CRAN.

#Libraries:
library(dplyr)
library(ggplot2)
library(nflfastR)

We need to upload the available NFL Data.

#Uploading NFL Data:
#Play by Play Data:
data_pbp_2022 = load_pbp(season = 2022)
head(data_pbp_2022, 5)
# A tibble: 5 × 372
  play_id game_id  old_g…¹ home_…² away_…³ seaso…⁴  week posteam poste…⁵ defteam
    <dbl> <chr>    <chr>   <chr>   <chr>   <chr>   <int> <chr>   <chr>   <chr>  
1       1 2022_01… 202209… NYJ     BAL     REG         1 <NA>    <NA>    <NA>   
2      43 2022_01… 202209… NYJ     BAL     REG         1 NYJ     home    BAL    
3      68 2022_01… 202209… NYJ     BAL     REG         1 NYJ     home    BAL    
4      89 2022_01… 202209… NYJ     BAL     REG         1 NYJ     home    BAL    
5     115 2022_01… 202209… NYJ     BAL     REG         1 NYJ     home    BAL    
# … with 362 more variables: side_of_field <chr>, yardline_100 <dbl>,
#   game_date <chr>, quarter_seconds_remaining <dbl>,
#   half_seconds_remaining <dbl>, game_seconds_remaining <dbl>,
#   game_half <chr>, quarter_end <dbl>, drive <dbl>, sp <dbl>, qtr <dbl>,
#   down <dbl>, goal_to_go <dbl>, time <chr>, yrdln <chr>, ydstogo <dbl>,
#   ydsnet <dbl>, desc <chr>, play_type <chr>, yards_gained <dbl>,
#   shotgun <dbl>, no_huddle <dbl>, qb_dropback <dbl>, qb_kneel <dbl>, …
# ℹ Use `colnames()` to see all variable names
#Players Stats:
data_players_stats = load_player_stats(season = 2022)
head(data_players_stats, 5)
# A tibble: 5 × 52
  player_id playe…¹ playe…² posit…³ posit…⁴ heads…⁵ recen…⁶ season  week seaso…⁷
  <chr>     <chr>   <chr>   <chr>   <chr>   <chr>   <chr>    <int> <int> <chr>  
1 00-00195… T.Brady Tom Br… QB      QB      https:… TB        2022     1 REG    
2 00-00195… T.Brady Tom Br… QB      QB      https:… TB        2022     2 REG    
3 00-00195… T.Brady Tom Br… QB      QB      https:… TB        2022     3 REG    
4 00-00195… T.Brady Tom Br… QB      QB      https:… TB        2022     4 REG    
5 00-00195… T.Brady Tom Br… QB      QB      https:… TB        2022     5 REG    
# … with 42 more variables: completions <int>, attempts <int>,
#   passing_yards <dbl>, passing_tds <int>, interceptions <dbl>, sacks <dbl>,
#   sack_yards <dbl>, sack_fumbles <int>, sack_fumbles_lost <int>,
#   passing_air_yards <dbl>, passing_yards_after_catch <dbl>,
#   passing_first_downs <dbl>, passing_epa <dbl>,
#   passing_2pt_conversions <int>, pacr <dbl>, dakota <dbl>, carries <int>,
#   rushing_yards <dbl>, rushing_tds <int>, rushing_fumbles <dbl>, …
# ℹ Use `colnames()` to see all variable names

So, we got a lot of data available of the 2022 NFL Season, I loaded to dataframes into R from the package repository:

With this data we can look into some stuff that can help us understand what are the realy good teams and players, also I want to see if some of my favorites players are playing well and if they are corresponding my expectations.

After loading the data, since we got so many variables, especially in the play by play data, we need to see what are the columns that helps us out to reach our goal, so we can use the field descriptions available from the nflfastR to chose the columns we want.

#Fields Descriptions:
field_descriptions |> 
  mutate(print_string = paste(Field, Description, sep = ": ")) |> 
  select(Field, print_string) 
# A tibble: 372 × 2
   Field        print_string                                                    
   <chr>        <chr>                                                           
 1 play_id      play_id: Numeric play id that when used with game_id and drive …
 2 game_id      game_id: Ten digit identifier for NFL game.                     
 3 old_game_id  old_game_id: Legacy NFL game ID.                                
 4 home_team    home_team: String abbreviation for the home team.               
 5 away_team    away_team: String abbreviation for the away team.               
 6 season_type  season_type: 'REG' or 'POST' indicating if the game belongs to …
 7 week         week: Season week.                                              
 8 posteam      posteam: String abbreviation for the team with possession.      
 9 posteam_type posteam_type: String indicating whether the posteam team is hom…
10 defteam      defteam: String abbreviation for the team on defense.           
# … with 362 more rows
# ℹ Use `print(n = ...)` to see more rows

To chose the columns I want, I concatenated the field name and descriptions and clipped this to a simple sheet (Excel, LibreOffice, Google Sheets..) and started to check the columns I wanted, after I checked I the columns we just need to clip back to R and select only the fields we want.

#Selecting fields we want:
data_pbp_2022_sel_fieds = data_pbp_2022 |> 
  select(play_id,
game_id,
home_team,
away_team,
season_type,
week,
posteam,
defteam,
game_date,
drive,
sp,
qtr,
down,
yrdln,
desc,
play_type,
qb_dropback,
qb_scramble,
pass_length,
pass_location,
air_yards,
rushing_yards,
yards_after_catch,
td_team,
td_player_name,
posteam_score,
defteam_score,
penalty,
epa,
total_home_epa,
total_away_epa,
total_home_rush_epa,
total_away_rush_epa,
total_home_pass_epa,
total_away_pass_epa,
first_down_rush,
first_down_pass,
first_down_penalty,
third_down_converted,
third_down_failed,
fourth_down_converted,
fourth_down_failed,
away_score,
home_score,
location,
result,
spread_line,
passer_id
)

So, from 372 available columns we selected 45 to beggin with. Now, we need to see the classes of this variables.

str(data_pbp_2022_sel_fieds)
nflverse_data [21,896 × 48] (S3: nflverse_data/tbl_df/tbl/data.table/data.frame)
 $ play_id              : num [1:21896] 1 43 68 89 115 136 172 202 230 254 ...
 $ game_id              : chr [1:21896] "2022_01_BAL_NYJ" "2022_01_BAL_NYJ" "2022_01_BAL_NYJ" "2022_01_BAL_NYJ" ...
 $ home_team            : chr [1:21896] "NYJ" "NYJ" "NYJ" "NYJ" ...
 $ away_team            : chr [1:21896] "BAL" "BAL" "BAL" "BAL" ...
 $ season_type          : chr [1:21896] "REG" "REG" "REG" "REG" ...
 $ week                 : int [1:21896] 1 1 1 1 1 1 1 1 1 1 ...
 $ posteam              : chr [1:21896] NA "NYJ" "NYJ" "NYJ" ...
 $ defteam              : chr [1:21896] NA "BAL" "BAL" "BAL" ...
 $ game_date            : chr [1:21896] "2022-09-11" "2022-09-11" "2022-09-11" "2022-09-11" ...
 $ drive                : num [1:21896] NA 1 1 1 1 1 1 2 2 2 ...
 $ sp                   : num [1:21896] 0 0 0 0 0 0 0 0 0 0 ...
 $ qtr                  : num [1:21896] 1 1 1 1 1 1 1 1 1 1 ...
 $ down                 : num [1:21896] NA NA 1 1 2 3 4 1 2 3 ...
 $ yrdln                : chr [1:21896] "BAL 35" "BAL 35" "NYJ 22" "NYJ 41" ...
 $ desc                 : chr [1:21896] "GAME" "9-J.Tucker kicks 68 yards from BAL 35 to NYJ -3. 10-B.Berrios to NYJ 22 for 25 yards (51-J.Ross)." "(14:56) 32-Mi.Carter left end to NYJ 41 for 19 yards (32-M.Williams; 36-C.Clark)." "(14:29) (No Huddle, Shotgun) 19-J.Flacco pass incomplete short left to 32-Mi.Carter." ...
 $ play_type            : chr [1:21896] NA "kickoff" "run" "pass" ...
 $ qb_dropback          : num [1:21896] NA 0 0 1 0 1 0 1 1 0 ...
 $ qb_scramble          : num [1:21896] 0 0 0 0 0 0 0 0 0 0 ...
 $ pass_length          : chr [1:21896] NA NA NA "short" ...
 $ pass_location        : chr [1:21896] NA NA NA "left" ...
 $ air_yards            : num [1:21896] NA NA NA 0 NA 0 NA -4 3 NA ...
 $ rushing_yards        : num [1:21896] NA NA 19 NA 5 NA NA NA NA 4 ...
 $ yards_after_catch    : num [1:21896] NA NA NA NA NA NA NA 8 1 NA ...
 $ td_team              : chr [1:21896] NA NA NA NA ...
 $ td_player_name       : chr [1:21896] NA NA NA NA ...
 $ posteam_score        : num [1:21896] NA 0 0 0 0 0 0 0 0 0 ...
 $ defteam_score        : num [1:21896] NA 0 0 0 0 0 0 0 0 0 ...
 $ penalty              : num [1:21896] NA 0 0 0 0 1 0 0 0 0 ...
 $ epa                  : num [1:21896] 0 -0.444 1.469 -0.492 -0.326 ...
 $ total_home_epa       : num [1:21896] 0 -0.444 1.025 0.533 0.207 ...
 $ total_away_epa       : num [1:21896] 0 0.444 -1.025 -0.533 -0.207 ...
 $ total_home_rush_epa  : num [1:21896] 0 0 1.47 1.47 1.14 ...
 $ total_away_rush_epa  : num [1:21896] 0 0 -1.47 -1.47 -1.14 ...
 $ total_home_pass_epa  : num [1:21896] 0 0 0 -0.492 -0.492 ...
 $ total_away_pass_epa  : num [1:21896] 0 0 0 0.492 0.492 ...
 $ first_down_rush      : num [1:21896] NA 0 1 0 0 0 0 0 0 1 ...
 $ first_down_pass      : num [1:21896] NA 0 0 0 0 0 0 0 0 0 ...
 $ first_down_penalty   : num [1:21896] NA 0 0 0 0 0 0 0 0 0 ...
 $ third_down_converted : num [1:21896] NA 0 0 0 0 0 0 0 0 1 ...
 $ third_down_failed    : num [1:21896] NA 0 0 0 0 1 0 0 0 0 ...
 $ fourth_down_converted: num [1:21896] NA 0 0 0 0 0 0 0 0 0 ...
 $ fourth_down_failed   : num [1:21896] NA 0 0 0 0 0 0 0 0 0 ...
 $ away_score           : int [1:21896] 24 24 24 24 24 24 24 24 24 24 ...
 $ home_score           : int [1:21896] 9 9 9 9 9 9 9 9 9 9 ...
 $ location             : chr [1:21896] "Home" "Home" "Home" "Home" ...
 $ result               : int [1:21896] -15 -15 -15 -15 -15 -15 -15 -15 -15 -15 ...
 $ spread_line          : num [1:21896] -6.5 -6.5 -6.5 -6.5 -6.5 -6.5 -6.5 -6.5 -6.5 -6.5 ...
 $ passer_id            : chr [1:21896] NA NA NA "00-0026158" ...
 - attr(*, "nflverse_timestamp")= POSIXct[1:1], format: "2022-11-06 06:07:38"
 - attr(*, "nflverse_type")= chr "play by play data"
 - attr(*, "nflfastR_version")=Classes 'package_version', 'numeric_version'  hidden list of 1
  ..$ : int [1:3] 4 5 0

Since we have play by play data, this dataset has a lot of NA values, because some columns only apply to specific plays, example, the air_yards variable will only have values when the play is a pass from the possession team.

Here, we need to create our dataframes using this available data, we don’t have a organized dataset with the teams statistics by week and etc, therefore, we need to do some cleaning and aggregation steps. But since we have offensive and defensive plays in the same datasets, we need to orient our data handling to the visuals we want to create, so lets work on it.

How are the Teams Tiers by Winning Pctg?

#Creating our wins dataframe by week:
df_wins = data_pbp_2022_sel_fieds |> 
  select(game_id, home_team, away_team, week, result) |> unique() |> 
  mutate(win = if_else(result <= 0, 0, 1)) #Creating a Win flag

#Adjusting to calculate all wins for all teams:
df_wins_adj = rbind(#Home Teams Wins:
      df_wins |> 
        select(game_id, home_team, result, win) |> 
        rename(team_abbr = home_team),
      #Away Teams Wins:
      df_wins |> 
        select(game_id, away_team, result, win) |> 
        rename(team_abbr = away_team) |> 
        mutate(win = abs(win - 1))
      ) 

#Plotting:
asp_ratio <- 1.618 

df_wins_adj |> 
  inner_join(select(teams_colors_logos, team_name, team_abbr, team_logo_espn), by = "team_abbr") |> 
  group_by(team_logo_espn, team_abbr, team_name) |> 
  summarise("Games Played" = n(),
            "% Win" = sum(win)/n()) |> 
  ##ggplot part:
  ggplot(aes(x = reorder(team_name, -`% Win`), y = `% Win`)) + 
  geom_bar(stat = "identity", alpha = 0.8, col = "black") +
  ggimage::geom_image(aes(image = team_logo_espn),
                      size = 0.05, by = "width", asp = asp_ratio) +
  scale_y_continuous(labels = scales::percent) + 
  theme_classic() + 
  theme(axis.text.x = element_blank(),
        aspect.ratio = 1/asp_ratio) +
  labs(x = "Teams", y = "Winning Percentage",
       title = "Winning Percentage by Team Throught 8 Weeks")

Offensive and Defensive EPA

EPA is a very good measure for evaluating how good the teams units are playing.

#Off and Def EPA per Play:
data_pbp_2022_sel_fieds |> 
  select(game_id, play_id, posteam, defteam, week, epa) |> 
  na.omit() |> 
  tidyr::pivot_longer(c("posteam", "defteam"), names_to = "unit", values_to = "team_abbr") |> 
  mutate(unit = factor(unit, levels = c("posteam", "defteam"), labels = c("offense", "defense"))) |> 
  group_by(team_abbr, unit) |> summarise(avg_epa = mean(epa)) |> ungroup() |> 
  tidyr::spread(unit, avg_epa) |> 
  inner_join(select(teams_colors_logos, team_name, team_abbr, team_logo_espn), by = "team_abbr") |> 
  ##ggplot aprt:
  ggplot(aes(x = offense, y = defense)) + 
  geom_point(col = "white") + 
  ggimage::geom_image(aes(image = team_logo_espn),
                      size = 0.05, by = "width", asp = asp_ratio) + 
  theme_classic() + 
  labs(x = "EPA/Play (Offense)",
       y = "EPA/Play (Defense)",
       title = "EPA by Team")

Play Selection Around the League

Here we got evaluate the play selection by each down and after we can look into it by each team.

#Plays Types in the dataset:
df_plays = data_pbp_2022_sel_fieds |> 
  filter(down %in% c(1, 2, 3) & penalty == 0) |> 
  select(game_id, posteam, play_type, down, week) |> 
  group_by(posteam, play_type) |> summarise(n = n()) |> mutate(freq = n/sum(n)) |> 
  select(-n) |> filter(play_type %in% c("run", "pass")) |> 
  arrange(play_type, desc(freq)) |> group_by(play_type) |> 
  mutate(id = if_else(play_type == "pass", row_number(), NULL)) |> ungroup() |> 
  group_by(posteam) |> mutate(id = sum(id, na.rm = T)) #This part is just to order the axis by the play type category
  

##ggplot part:
df_plays |> 
  ggplot(aes(x = reorder(posteam, id), y = freq, fill = play_type, label = round(freq*100, 0))) + 
  geom_bar(stat = "identity", col = "black") + 
  theme_classic() +
  geom_text(position = position_stack(vjust = .5)) + 
  theme(axis.text.x = element_text(angle = 45, colour = "black"),
        legend.position = "top") + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = "Team", 
       y = "Play Selection Percentage",
       title = "Play Selection by Team",
       subtitle = "Distribution of plays by each team ordered by the most pass centred offense to the least one on 3 Downs")

Air Yards versus Rush Yards per Play

data_pbp_2022_sel_fieds |> 
  select(game_id, play_id, posteam, air_yards, rushing_yards, play_type, penalty) |> 
  filter(!is.na(play_type) & play_type %in% c("run", "pass")) |> 
  group_by(posteam) |> summarise(avg_air = mean(air_yards, na.rm = T),
                                 avg_rush = mean(rushing_yards, na.rm = T)) |> 
  rename(team_abbr = posteam) |> 
  inner_join(select(teams_colors_logos, team_name, team_abbr, team_logo_espn), by = "team_abbr") |> 
  ggplot(aes(x = avg_air, y = avg_rush)) + 
  geom_point(col = "white") + 
  ggimage::geom_image(aes(image = team_logo_espn),
                      size = 0.05, by = "width", asp = asp_ratio) + 
  theme_classic() + 
  labs(x = "Air Yards per Play",
       y = "Rush Yards per Play",
       title = "Air Yards versus Rush Yards per Play by Team")

Play Selection and Field Position

For this one some manipulations have to be done.

`%!in%` = Negate(`%in%`)
df_fields_plays_downs = data_pbp_2022_sel_fieds |> 
  select(play_id, game_id, posteam, yrdln, play_type, down) |> 
  filter(!is.na(play_type) & play_type %!in% c("no_play", "kickoff", "extra_point") & !is.na(down)) |> 
  mutate(adj_play = forcats::fct_lump_n(play_type, n = 4, ties.method = "first", other_level = "Others Plays"), #Plays "Irrelevants" won't mather
         field_position_num = if_else(stringr::word(yrdln, 1) == posteam, as.numeric(paste0("-", stringr::word(yrdln, 2))), as.numeric(stringr::word(yrdln, 2))),
         field_position_cat = case_when(field_position_num <= -30 ~ "Own 20",
                                        field_position_num > -30 & field_position_num <= 0 ~ "Defensive Field > 20 Yrds",
                                        field_position_num > 0 & field_position_num <= 30 ~ "Offensive Field - Not Red Zone",
                                        T ~  "Red Zone"), #Creating categories for field position
         field_position_cat = factor(field_position_cat, levels = c("Own 20", "Defensive Field > 20 Yrds", "Offensive Field - Not Red Zone", "Red Zone")))  
#Aggregation Part:
stats_fields_plays = df_fields_plays_downs |> 
  group_by(posteam, field_position_cat, adj_play) |> 
  summarise(n = n()) |> mutate(freq = n/sum(n)) |> select(-n) |> 
  tidyr::spread(adj_play, freq)
stats_fields_plays[is.na(stats_fields_plays)] = 0
stats_fields_plays |> ungroup() |> 
  inner_join(select(teams_colors_logos, team_abbr, team_name), by = c("posteam" = "team_abbr")) |> select(-posteam) |> 
  select(team_name, field_position_cat, pass, run, field_goal, punt, `Others Plays`) |> 
  mutate_if(is.double, scales::percent, accuracy = 0.1) |> 
  knitr::kable(align = "c")
team_name field_position_cat pass run field_goal punt Others Plays
Arizona Cardinals Own 20 60.6% 30.6% 0.0% 8.8% 0.0%
Arizona Cardinals Defensive Field > 20 Yrds 64.0% 27.4% 0.0% 7.9% 0.6%
Arizona Cardinals Offensive Field - Not Red Zone 50.3% 39.5% 7.5% 0.0% 2.7%
Arizona Cardinals Red Zone 51.6% 42.7% 1.6% 1.6% 2.4%
Atlanta Falcons Own 20 40.0% 49.0% 0.0% 11.0% 0.0%
Atlanta Falcons Defensive Field > 20 Yrds 43.3% 45.3% 0.0% 7.3% 4.0%
Atlanta Falcons Offensive Field - Not Red Zone 33.1% 56.2% 9.9% 0.0% 0.8%
Atlanta Falcons Red Zone 34.4% 57.3% 5.2% 3.1% 0.0%
Baltimore Ravens Own 20 47.4% 40.8% 0.0% 9.9% 2.0%
Baltimore Ravens Defensive Field > 20 Yrds 57.1% 34.9% 0.0% 5.6% 2.4%
Baltimore Ravens Offensive Field - Not Red Zone 44.0% 47.8% 8.2% 0.0% 0.0%
Baltimore Ravens Red Zone 41.0% 46.2% 6.0% 2.6% 4.3%
Buffalo Bills Own 20 58.8% 33.8% 0.0% 5.9% 1.5%
Buffalo Bills Defensive Field > 20 Yrds 64.8% 29.5% 0.0% 2.3% 3.4%
Buffalo Bills Offensive Field - Not Red Zone 56.4% 32.2% 7.4% 0.0% 4.0%
Buffalo Bills Red Zone 55.6% 38.9% 1.9% 2.8% 0.9%
Carolina Panthers Own 20 50.0% 37.7% 0.0% 11.6% 0.7%
Carolina Panthers Defensive Field > 20 Yrds 55.1% 33.5% 0.0% 10.8% 0.6%
Carolina Panthers Offensive Field - Not Red Zone 43.8% 40.6% 15.6% 0.0% 0.0%
Carolina Panthers Red Zone 55.4% 36.6% 1.0% 5.9% 1.0%
Chicago Bears Own 20 43.0% 50.8% 0.0% 6.2% 0.0%
Chicago Bears Defensive Field > 20 Yrds 38.0% 50.9% 0.0% 11.0% 0.0%
Chicago Bears Offensive Field - Not Red Zone 30.6% 56.0% 9.0% 0.0% 4.5%
Chicago Bears Red Zone 33.7% 57.1% 4.1% 5.1% 0.0%
Cincinnati Bengals Own 20 59.8% 29.9% 0.0% 8.5% 1.8%
Cincinnati Bengals Defensive Field > 20 Yrds 59.4% 29.7% 0.0% 7.8% 3.1%
Cincinnati Bengals Offensive Field - Not Red Zone 58.6% 33.6% 7.1% 0.0% 0.7%
Cincinnati Bengals Red Zone 62.0% 31.8% 3.1% 3.1% 0.0%
Cleveland Browns Own 20 56.4% 37.6% 0.0% 6.0% 0.0%
Cleveland Browns Defensive Field > 20 Yrds 51.1% 41.5% 0.0% 6.7% 0.7%
Cleveland Browns Offensive Field - Not Red Zone 36.4% 54.3% 7.9% 0.0% 1.3%
Cleveland Browns Red Zone 41.8% 47.3% 4.1% 4.8% 2.1%
Dallas Cowboys Own 20 54.3% 33.3% 0.0% 8.5% 3.9%
Dallas Cowboys Defensive Field > 20 Yrds 42.9% 46.9% 0.0% 9.7% 0.6%
Dallas Cowboys Offensive Field - Not Red Zone 38.6% 48.5% 10.9% 0.0% 2.0%
Dallas Cowboys Red Zone 52.9% 33.9% 5.0% 5.8% 2.5%
Denver Broncos Own 20 51.4% 35.2% 0.0% 13.4% 0.0%
Denver Broncos Defensive Field > 20 Yrds 52.9% 37.8% 0.0% 8.1% 1.2%
Denver Broncos Offensive Field - Not Red Zone 52.8% 31.5% 12.0% 0.0% 3.7%
Denver Broncos Red Zone 50.0% 37.7% 5.3% 6.1% 0.9%
Detroit Lions Own 20 54.2% 38.9% 0.0% 6.1% 0.8%
Detroit Lions Defensive Field > 20 Yrds 55.8% 35.0% 0.0% 8.3% 0.8%
Detroit Lions Offensive Field - Not Red Zone 55.1% 37.4% 6.5% 0.0% 0.9%
Detroit Lions Red Zone 54.6% 38.7% 2.5% 2.5% 1.7%
Green Bay Packers Own 20 59.4% 30.3% 0.0% 9.1% 1.2%
Green Bay Packers Defensive Field > 20 Yrds 52.8% 38.2% 0.0% 8.3% 0.7%
Green Bay Packers Offensive Field - Not Red Zone 50.5% 40.0% 8.6% 0.0% 1.0%
Green Bay Packers Red Zone 55.0% 38.0% 0.8% 5.4% 0.8%
Houston Texans Own 20 51.9% 34.8% 0.0% 13.3% 0.0%
Houston Texans Defensive Field > 20 Yrds 54.8% 35.7% 0.0% 7.0% 2.5%
Houston Texans Offensive Field - Not Red Zone 51.0% 35.7% 13.3% 0.0% 0.0%
Houston Texans Red Zone 55.6% 35.2% 2.8% 6.5% 0.0%
Indianapolis Colts Own 20 64.7% 25.3% 0.0% 9.5% 0.5%
Indianapolis Colts Defensive Field > 20 Yrds 59.8% 32.3% 0.0% 7.1% 0.8%
Indianapolis Colts Offensive Field - Not Red Zone 52.8% 36.6% 9.8% 0.0% 0.8%
Indianapolis Colts Red Zone 55.9% 36.6% 3.4% 4.1% 0.0%
Jacksonville Jaguars Own 20 61.1% 32.5% 0.0% 6.3% 0.0%
Jacksonville Jaguars Defensive Field > 20 Yrds 51.4% 37.2% 0.0% 8.1% 3.4%
Jacksonville Jaguars Offensive Field - Not Red Zone 43.0% 49.7% 7.3% 0.0% 0.0%
Jacksonville Jaguars Red Zone 56.8% 37.6% 0.8% 4.8% 0.0%
Kansas City Chiefs Own 20 55.5% 39.8% 0.0% 4.7% 0.0%
Kansas City Chiefs Defensive Field > 20 Yrds 58.1% 30.5% 0.0% 8.6% 2.9%
Kansas City Chiefs Offensive Field - Not Red Zone 59.5% 34.5% 6.1% 0.0% 0.0%
Kansas City Chiefs Red Zone 60.4% 28.1% 4.2% 4.2% 3.1%
Los Angeles Rams Own 20 68.1% 22.1% 0.0% 8.8% 0.9%
Los Angeles Rams Defensive Field > 20 Yrds 50.0% 39.3% 0.0% 9.3% 1.3%
Los Angeles Rams Offensive Field - Not Red Zone 62.4% 30.3% 7.3% 0.0% 0.0%
Los Angeles Rams Red Zone 66.0% 25.8% 2.1% 4.1% 2.1%
Los Angeles Chargers Own 20 65.2% 23.0% 0.0% 10.4% 1.5%
Los Angeles Chargers Defensive Field > 20 Yrds 59.9% 32.8% 0.0% 6.6% 0.7%
Los Angeles Chargers Offensive Field - Not Red Zone 56.8% 31.8% 8.1% 0.0% 3.4%
Los Angeles Chargers Red Zone 60.4% 33.0% 0.9% 4.7% 0.9%
Las Vegas Raiders Own 20 56.4% 36.8% 0.0% 6.8% 0.0%
Las Vegas Raiders Defensive Field > 20 Yrds 58.2% 30.3% 0.0% 9.0% 2.5%
Las Vegas Raiders Offensive Field - Not Red Zone 59.3% 29.7% 9.3% 0.0% 1.7%
Las Vegas Raiders Red Zone 59.8% 33.3% 5.7% 1.1% 0.0%
Miami Dolphins Own 20 59.5% 31.0% 0.0% 9.5% 0.0%
Miami Dolphins Defensive Field > 20 Yrds 60.6% 31.8% 0.0% 6.8% 0.8%
Miami Dolphins Offensive Field - Not Red Zone 54.2% 33.1% 8.5% 0.0% 4.2%
Miami Dolphins Red Zone 57.3% 34.7% 3.2% 4.8% 0.0%
Minnesota Vikings Own 20 53.3% 34.7% 0.0% 12.0% 0.0%
Minnesota Vikings Defensive Field > 20 Yrds 59.2% 32.0% 0.0% 7.8% 1.0%
Minnesota Vikings Offensive Field - Not Red Zone 58.3% 34.8% 7.0% 0.0% 0.0%
Minnesota Vikings Red Zone 57.0% 28.0% 5.6% 3.7% 5.6%
New England Patriots Own 20 48.7% 39.1% 0.0% 9.0% 3.2%
New England Patriots Defensive Field > 20 Yrds 53.4% 39.0% 0.0% 5.9% 1.7%
New England Patriots Offensive Field - Not Red Zone 37.9% 49.2% 10.5% 0.0% 2.4%
New England Patriots Red Zone 50.7% 37.3% 3.0% 6.7% 2.2%
New Orleans Saints Own 20 48.1% 42.3% 0.0% 9.5% 0.0%
New Orleans Saints Defensive Field > 20 Yrds 54.5% 37.9% 0.0% 6.2% 1.4%
New Orleans Saints Offensive Field - Not Red Zone 52.2% 36.5% 10.4% 0.0% 0.9%
New Orleans Saints Red Zone 54.9% 33.6% 4.1% 3.3% 4.1%
New York Giants Own 20 55.0% 35.6% 0.0% 9.4% 0.0%
New York Giants Defensive Field > 20 Yrds 36.5% 52.7% 0.0% 9.0% 1.8%
New York Giants Offensive Field - Not Red Zone 37.2% 50.4% 10.2% 0.0% 2.2%
New York Giants Red Zone 48.5% 41.4% 5.1% 3.0% 2.0%
New York Jets Own 20 59.7% 27.3% 0.0% 11.7% 1.3%
New York Jets Defensive Field > 20 Yrds 56.0% 30.7% 0.0% 12.0% 1.2%
New York Jets Offensive Field - Not Red Zone 50.7% 40.1% 9.2% 0.0% 0.0%
New York Jets Red Zone 58.7% 31.7% 3.8% 1.9% 3.8%
Philadelphia Eagles Own 20 49.4% 39.0% 0.0% 9.9% 1.7%
Philadelphia Eagles Defensive Field > 20 Yrds 48.5% 45.5% 0.0% 6.0% 0.0%
Philadelphia Eagles Offensive Field - Not Red Zone 36.8% 54.2% 5.2% 0.0% 3.9%
Philadelphia Eagles Red Zone 47.1% 43.7% 1.7% 5.0% 2.5%
Pittsburgh Steelers Own 20 50.9% 38.5% 0.0% 10.6% 0.0%
Pittsburgh Steelers Defensive Field > 20 Yrds 58.5% 29.5% 0.0% 11.4% 0.6%
Pittsburgh Steelers Offensive Field - Not Red Zone 49.5% 38.7% 9.9% 0.0% 1.8%
Pittsburgh Steelers Red Zone 63.8% 25.9% 6.0% 2.6% 1.7%
Seattle Seahawks Own 20 54.7% 39.0% 0.0% 6.3% 0.0%
Seattle Seahawks Defensive Field > 20 Yrds 52.3% 35.6% 0.0% 10.6% 1.5%
Seattle Seahawks Offensive Field - Not Red Zone 51.4% 35.8% 11.9% 0.0% 0.9%
Seattle Seahawks Red Zone 50.4% 38.1% 4.4% 2.7% 4.4%
San Francisco 49ers Own 20 55.8% 36.4% 0.0% 7.3% 0.6%
San Francisco 49ers Defensive Field > 20 Yrds 55.2% 38.8% 0.0% 4.5% 1.5%
San Francisco 49ers Offensive Field - Not Red Zone 42.7% 49.6% 7.7% 0.0% 0.0%
San Francisco 49ers Red Zone 50.0% 37.5% 3.8% 8.7% 0.0%
Tampa Bay Buccaneers Own 20 61.4% 28.3% 0.0% 10.2% 0.0%
Tampa Bay Buccaneers Defensive Field > 20 Yrds 60.5% 28.3% 0.0% 9.9% 1.3%
Tampa Bay Buccaneers Offensive Field - Not Red Zone 58.7% 26.5% 12.3% 0.0% 2.6%
Tampa Bay Buccaneers Red Zone 65.3% 23.8% 1.0% 5.9% 4.0%
Tennessee Titans Own 20 40.0% 48.5% 0.0% 11.5% 0.0%
Tennessee Titans Defensive Field > 20 Yrds 43.2% 45.3% 0.0% 11.5% 0.0%
Tennessee Titans Offensive Field - Not Red Zone 40.7% 44.0% 12.1% 0.0% 3.3%
Tennessee Titans Red Zone 32.5% 55.4% 0.0% 4.8% 7.2%
Washington Commanders Own 20 53.7% 31.6% 0.0% 13.2% 1.6%
Washington Commanders Defensive Field > 20 Yrds 58.7% 32.7% 0.0% 8.2% 0.5%
Washington Commanders Offensive Field - Not Red Zone 58.0% 33.9% 8.0% 0.0% 0.0%
Washington Commanders Red Zone 45.5% 48.9% 1.1% 4.5% 0.0%