Source:
vignettes/nfl_standings.Rmd
nfl_standings.Rmd
2021 NFL Standings & Team Ratings
Easy Moderate Difficult Most Difficult
Table created by: Kyle Cuilla @kc_analytics with {reactablefmtr} • Data: Pro-Football-Reference.com
Code
# Function to pull data from PFR
get_pfr_standings <- function(year) {
url <- paste0("https://www.pro-football-reference.com/years/",year,"/")
AFC_table <- url %>%
xml2::read_html() %>%
html_nodes(xpath = '//*[@id="AFC"]') %>%
html_table()
AFC_table <- AFC_table[[1]]
NFC_table <- url %>%
xml2::read_html() %>%
html_nodes(xpath = '//*[@id="NFC"]') %>%
html_table()
NFC_table <- NFC_table[[1]]
NFL_table <- rbind(AFC_table, NFC_table)
NFL_table_clean <- NFL_table %>%
mutate(Division = ifelse(str_detect(Tm, "FC"), Tm, NA)) %>%
fill(Division, .direction = "down") %>%
filter(str_detect(Tm, "FC", negate = TRUE)) %>%
mutate(Playoffs = ifelse(str_detect(Tm, "[*+]"), "Yes", "No")) %>%
mutate(Tm = gsub("[*+]", "", Tm)) %>%
rename(Record = `W-L%`) %>%
unite(Record, W, L, T, sep = "-") %>%
mutate(Team = word(Tm, -1)) %>%
mutate(
Team = case_when(
Team == "Team" ~ "Football Team",
TRUE ~ Team
)
) %>%
mutate_at(c("SRS", "OSRS", "DSRS", "PF", "PA", "MoV", "SoS"),
as.numeric) %>%
mutate(SOS = ntile(SoS, 4)) %>%
select(
Division,
Team,
Record,
Playoffs,
SOS,
PF,
PA,
MoV,
OSRS,
DSRS,
SRS,
)
}
# Join to team logos from {nflfastR}
data <- get_pfr_standings(2021) %>%
left_join(distinct(select(teams_colors_logos, team_nick, team_logo_espn)),
by = c('Team' = 'team_nick')) %>%
select(Division, team_logo_espn, everything())
# Create table
data %>%
mutate(
mov_cols = case_when(MoV >= 0 ~ "darkgreen",
TRUE ~ "red"),
playoff_cols = case_when(Playoffs == "Yes" ~ "darkgreen",
TRUE ~ "red")
) %>%
reactable(
.,
theme = fivethirtyeight(centered = TRUE, header_font_size = 11),
pagination = FALSE,
showSortIcon = FALSE,
highlight = TRUE,
compact = TRUE,
defaultSorted = "SRS",
defaultSortOrder = "desc",
columnGroups = list(
colGroup(name = "Team Rating (SRS)",
columns = c("SRS", "OSRS", "DSRS")),
colGroup(name = "Team Scoring & Margin of Victory",
columns = c("PF", "PA", "MoV"))
),
rowStyle = group_border_sort("Division"),
columns = list(
Division = colDef(
name = "Div.",
maxWidth = 85,
style = group_merge_sort("Division")
),
team_logo_espn = colDef(
name = "",
maxWidth = 75,
sortable = FALSE,
style = background_img(height = "200%", width = "125%")
),
Team = colDef(
minWidth = 100,
align = "left",
cell = merge_column(., "Record", merged_position = "below"),
style = list(borderRight = "1px solid #777")
),
Record = colDef(show = FALSE),
Playoffs = colDef(
maxWidth = 70,
align = "center",
cell = pill_buttons(., color_ref = "playoff_cols", opacity = 0.7),
style = list(borderRight = "1px solid #777")
),
playoff_cols = colDef(show = FALSE),
SOS = colDef(
maxWidth = 47,
align = "center",
cell = icon_sets(., icon_set = "ski rating", icon_position = "over"),
style = list(borderRight = "1px solid #777")
),
PF = colDef(
maxWidth = 180,
cell = data_bars(., text_size = 13, box_shadow = TRUE)),
PA = colDef(
maxWidth = 180,
cell = data_bars(., text_size = 13, box_shadow = TRUE)),
MoV = colDef(
maxWidth = 80,
cell = pill_buttons(., number_fmt = function(value) sprintf("%+0.1f", value), colors = "transparent", opacity = 0, bold_text = TRUE, text_color_ref = "mov_cols"
),
style = list(borderRight = "1px solid #777")
),
mov_cols = colDef(show = FALSE),
OSRS = colDef(
maxWidth = 55,
cell = color_tiles(., bias = 1.3, box_shadow = TRUE)
),
DSRS = colDef(
maxWidth = 55,
cell = color_tiles(., bias = 0.6, box_shadow = TRUE)
),
SRS = colDef(
maxWidth = 55,
cell = color_tiles(., bias = 0.7, box_shadow = TRUE)
)
)
) %>%
add_title("2021 NFL Standings & Team Ratings", margin = margin(0, 0, 10, 0)) %>%
add_icon_legend(icon_set = "ski rating") %>%
add_source("Table created by: Kyle Cuilla @kc_analytics with {reactablefmtr} • Data: Pro-Football-Reference.com", font_size = 12)