Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new function for series conversions and series results #393

Merged
merged 8 commits into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: nflfastR
Title: Functions to Efficiently Access NFL Play by Play Data
Version: 4.4.0.9012
Version: 4.4.0.9013
Authors@R:
c(person(given = "Sebastian",
family = "Carl",
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export(calculate_expected_points)
export(calculate_player_stats)
export(calculate_player_stats_def)
export(calculate_player_stats_kicking)
export(calculate_series_conversion_rates)
export(calculate_standings)
export(calculate_win_probability)
export(clean_pbp)
Expand Down
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
* import/export `nflverse_sitrep`
* Add gsis_id patch to `clean_pbp()`. (v4.4.0.9009)
* `calculate_player_stats_def()` failed in situations where play-by-play data is missing certain stats. (#382) (v4.4.0.9010)
* Spot-fixing `calculate_player_stats` for NA names. (v4.4.0.9011)
* Spot-fixing `calculate_player_stats()` for NA names. (v4.4.0.9011)
* Added new function `calculate_player_stats_kicking()` that aggregates player stats for field goals and extra points at game level or overall. (v4.4.0.9012)
* Added new function `calculate_series_conversion_rates()` that computes series conversion and series result rates at a game level or season level. (v4.4.0.9013)

# nflfastR 4.4.0

Expand Down
2 changes: 1 addition & 1 deletion R/aggregate_game_stats_kicking.R
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ calculate_player_stats_kicking <- function(pbp, weekly = FALSE) {
dplyr::full_join(game_winners, as.character(grp_vars)) %>%
dplyr::left_join(df_player_names, "player_id") %>%
dplyr::group_by(!!!grp_vars) %>%
dplyr::mutate(games = length(unique(unlist(c(games_fg, games_pat, games_gwfg))))) %>%
dplyr::mutate(games = length(unique(unlist(c(.data$games_fg, .data$games_pat, .data$games_gwfg))))) %>%
dplyr::ungroup() %>%
dplyr::select(
dplyr::any_of(c("season", "week", "season_type")), "player_id",
Expand Down
175 changes: 175 additions & 0 deletions R/calculate_series_conversion_rates.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#' Compute Series Conversion Information from Play by Play
#'
#' @description A "Series" begins on a 1st and 10 and each team attempts to either earn
#' a new 1st down (on offense) or prevent the offense from converting a new
#' 1st down (on defense). Series conversion rate represents how many series
#' have been either converted to a new 1st down or ended in a touchdown.
#' This function computes series conversion rates on offense and defense from
#' nflverse play-by-play data along with other series results.
#' The function automatically removes series that ended in a QB kneel down.
#'
#' @param pbp Play-by-play data as returned by [`load_pbp()`], [`build_nflfastR_pbp()`], or
#' [`fast_scraper()`].
#' @param weekly If `TRUE`, returns week-by-week stats, otherwise,
#' season-by-season stats in argument `pbp`.
#'
#' @return A data frame of series information including the following columns:
#' \describe{
#' \item{season}{The NFL season}
#' \item{team}{NFL team abbreviation}
#' \item{week}{Week if `weekly` is `TRUE`}
#' \item{off_n}{The number of series the offense played (excludes QB kneel
#' downs, kickoffs, extra point/two point conversion attempts, non-plays, and
#' plays that do not list a "posteam")}
#' \item{off_scr}{The rate at which a series ended in either new 1st down or
#' touchdown while the offense was on the field}
#' \item{off_scr_1st}{The rate at which an offense earned a 1st down
#' or scored a touchdown on 1st down}
#' \item{off_scr_2nd}{The rate at which an offense earned a 1st down
#' or scored a touchdown on 2nd down}
#' \item{off_scr_3rd}{The rate at which an offense earned a 1st down
#' or scored a touchdown on 3rd down}
#' \item{off_scr_4th}{The rate at which an offense earned a 1st down
#' or scored a touchdown on 4th down}
#' \item{off_1st}{The rate of series that ended in a new 1st down while the
#' offense was on the field (does not include offensive touchdown)}
#' \item{off_td}{The rate of series that ended in an offensive touchdown while the
#' offense was on the field}
#' \item{off_fg}{The rate of series that ended in a field goal attempt while the
#' offense was on the field}
#' \item{off_punt}{The rate of series that ended in a punt while the
#' offense was on the field}
#' \item{off_to}{The rate of series that ended in a turnover (including on downs), in an
#' opponent score, or at the end of half (or game) while the
#' offense was on the field}
#' \item{def_n}{The number of series the defense played (excludes QB kneel
#' downs, kickoffs, extra point/two point conversion attempts, non-plays, and
#' plays that do not list a "posteam")}
#' \item{def_scr}{The rate at which a series ended in either new 1st down or
#' touchdown while the defense was on the field}
#' \item{def_scr_1st}{The rate at which a defense allowed a
#' 1st down or touchdown on 1st down}
#' \item{def_scr_2nd}{The rate at which a defense allowed a
#' 1st down or touchdown on 2nd down}
#' \item{def_scr_3rd}{The rate at which a defense allowed a
#' 1st down or touchdown on 3rd down}
#' \item{def_scr_4th}{The rate at which a defense allowed a
#' 1st down or touchdown on 4th down}
#' \item{def_1st}{The rate of series that ended in a new 1st down while the
#' defense was on the field (does not include offensive touchdown)}
#' \item{def_td}{The rate of series that ended in an offensive touchdown while the
#' defense was on the field}
#' \item{def_fg}{The rate of series that ended in a field goal attempt while the
#' defense was on the field}
#' \item{def_punt}{The rate of series that ended in a punt while the
#' defense was on the field}
#' \item{def_to}{The rate of series that ended in a turnover (including on downs), in an
#' opponent score, or at the end of half (or game) while the
#' defense was on the field}
#' }
#' @export
#'
#' @examples
#' \donttest{
#' pbp <- nflfastR::load_pbp(2021)
#'
#' weekly <- calculate_series_conversion_rates(pbp, weekly = TRUE)
#' dplyr::glimpse(weekly)
#'
#' overall <- calculate_series_conversion_rates(pbp, weekly = FALSE)
#' dplyr::glimpse(overall)
#' }
calculate_series_conversion_rates <- function(pbp,
weekly = FALSE){
# For tests
# pbp <- nflreadr::load_pbp()

# Offense -----------------------------------------------------------------

off_series <- pbp %>%
dplyr::filter(
!is.na(.data$down),
.data$series_result != "QB kneel"
# .data$rush == 1 | .data$pass == 1
) %>%
dplyr::group_by(.data$season, .data$week, team = .data$posteam, .data$series) %>%
dplyr::summarise(
conversion = dplyr::first(.data$series_success),
result = dplyr::first(.data$series_result),
last_down = dplyr::last(.data$down),
.groups = "drop"
)

offense <- off_series %>%
dplyr::group_by(.data$season, .data$team, .data$week) %>%
dplyr::summarise(
off_n = dplyr::n(),
off_scr = mean(.data$conversion),
off_scr_1st = mean(.data$last_down == 1 * .data$conversion),
off_scr_2nd = mean(.data$last_down == 2 * .data$conversion),
off_scr_3rd = mean(.data$last_down == 3 * .data$conversion),
off_scr_4th = mean(.data$last_down == 4 * .data$conversion),
off_1st = mean(.data$result == "First down"),
off_td = mean(.data$result == "Touchdown"),
off_fg = mean(.data$result %in% c("Field goal", "Missed field goal")),
off_punt = mean(.data$result == "Punt"),
off_to = mean(
.data$result %in% c("Turnover on downs", "Turnover", "Opp touchdown", "Safety", "End of half")
),
.groups = "drop"
)

# Defense -----------------------------------------------------------------

def_series <- pbp %>%
dplyr::filter(
!is.na(.data$down),
.data$series_result != "QB kneel"
# .data$rush == 1 | .data$pass == 1
) %>%
dplyr::group_by(.data$season, .data$week, team = .data$defteam, .data$series) %>%
dplyr::summarise(
conversion = dplyr::first(.data$series_success),
result = dplyr::first(.data$series_result),
last_down = dplyr::last(.data$down),
.groups = "drop"
)

defense <- def_series %>%
dplyr::group_by(.data$season, .data$team, .data$week) %>%
dplyr::summarise(
def_n = dplyr::n(),
def_scr = mean(.data$conversion),
def_scr_1st = mean(.data$last_down == 1 * .data$conversion),
def_scr_2nd = mean(.data$last_down == 2 * .data$conversion),
def_scr_3rd = mean(.data$last_down == 3 * .data$conversion),
def_scr_4th = mean(.data$last_down == 4 * .data$conversion),
def_1st = mean(.data$result == "First down"),
def_td = mean(.data$result == "Touchdown"),
def_fg = mean(.data$result %in% c("Field goal", "Missed field goal")),
def_punt = mean(.data$result == "Punt"),
def_to = mean(
.data$result %in% c("Turnover on downs", "Turnover", "Opp touchdown", "Safety", "End of half")
),
.groups = "drop"
)


# Offense + Defense -------------------------------------------------------

combined <- dplyr::left_join(offense, defense, by = c("season", "team", "week"))

if (isFALSE(weekly)){
combined <- combined %>%
dplyr::select(-"week") %>%
dplyr::group_by(.data$season, .data$team) %>%
dplyr::summarise(
dplyr::across(.cols = dplyr::ends_with("_n"), .fns = sum),
dplyr::across(.cols = !dplyr::ends_with("_n"), .fns = mean),
.groups = "drop"
) %>%
dplyr::relocate("def_n", .after = "off_to")
}

combined
}
91 changes: 91 additions & 0 deletions man/calculate_series_conversion_rates.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkgdown/_pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ reference:
- calculate_player_stats_def
- calculate_player_stats_kicking
- calculate_standings
- calculate_series_conversion_rates
- report
- title: Documentation
contents:
Expand Down