diff --git a/.github/.gitignore b/.github/.gitignore index 2d19fc7..f920f88 100644 --- a/.github/.gitignore +++ b/.github/.gitignore @@ -1 +1,2 @@ + *.html diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 16f915b..98b0908 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -34,3 +34,4 @@ jobs: git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com" Rscript -e 'roxygen2::roxygenize()' Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' + diff --git a/.github/workflows/pkgdown_test.yaml b/.github/workflows/pkgdown_test.yaml index a08d4cd..bc3ff53 100644 --- a/.github/workflows/pkgdown_test.yaml +++ b/.github/workflows/pkgdown_test.yaml @@ -32,4 +32,4 @@ jobs: git config --local user.name "GitHub Actions" Rscript -e 'roxygen2::roxygenise()' Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE, branch = "gh-pages-test")' - + diff --git a/.gitignore b/.gitignore index 7c06555..510f32f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .Ruserdata .DS_Store docs + diff --git a/DESCRIPTION b/DESCRIPTION index ecd3489..be80fc6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: puntr Type: Package Title: Analysis of Punting License: MIT -Version: 1.3 +Version: 1.4 Authors@R: c( person("Dennis", "Brookner", role = c("aut", "cre"), email = "debrookner@gmail.com"), person("Raphael", "LadenGuindon", role = "aut")) @@ -13,12 +13,8 @@ URL: https://puntalytics.github.io/puntr, https://github.com/Puntalytics/puntr Encoding: UTF-8 LazyData: true Imports: - cfbfastR, dplyr, forcats, - ggimage, - ggplot2, - ggrepel, glue, magrittr, nflfastR, diff --git a/R/college.R b/R/college.R index b4cd76d..4bc40c0 100644 --- a/R/college.R +++ b/R/college.R @@ -1,84 +1,84 @@ -#' Import college punting data -#' @description Import college punting data for seasons in the scope of \code{cfbfastR} (back to 2014). -#' This function is a wrapper around \code{cfbfastR::load_cfb_pbp}. -#' @param years A year or range of years to be scraped -#' @return A tibble \code{punts} of punts in the \code{cfbfastR} format -#' @examples -#' \dontrun{ -#' import_college_punts(2018:2021) +#' #' Import college punting data +#' #' @description Import college punting data for seasons in the scope of \code{cfbfastR} (back to 2014). +#' #' This function is a wrapper around \code{cfbfastR::load_cfb_pbp}. +#' #' @param years A year or range of years to be scraped +#' #' @return A tibble \code{punts} of punts in the \code{cfbfastR} format +#' #' @examples +#' #' \dontrun{ +#' #' import_college_punts(2018:2021) +#' #' } +#' #' @export +#' import_college_punts <- function(years) { +#' punts <- purrr::map_df(years, function(x){ +#' cfbfastR::load_cfb_pbp(x) %>% +#' dplyr::filter(punt == 1) %>% +#' dplyr::mutate(season = x) +#' }) +#' return(punts) #' } -#' @export -import_college_punts <- function(years) { - punts <- purrr::map_df(years, function(x){ - cfbfastR::load_cfb_pbp(x) %>% - dplyr::filter(punt == 1) %>% - dplyr::mutate(season = x) - }) - return(punts) -} - -#' Convert college data to \code{puntr} format #' -#' @description Rename columns and process data such that the output can be plugged directly into \code{puntr::calculate_all}, -#' and the output of that can be plugged directly into \code{puntr::create_mini} (or \code{puntr::create_miniY}). -#' @param punts A data frame containing punts in the cfbfastR format -#' @param power_five Logical, defaults to TRUE to include only punters from Power 5 teams -#' @return A tibble \code{punts} in a format usable for \code{puntr::calculate_all} -#' @examples -#' \dontrun{ -#' college_to_pro(punts) +#' #' Convert college data to \code{puntr} format +#' #' +#' #' @description Rename columns and process data such that the output can be plugged directly into \code{puntr::calculate_all}, +#' #' and the output of that can be plugged directly into \code{puntr::create_mini} (or \code{puntr::create_miniY}). +#' #' @param punts A data frame containing punts in the cfbfastR format +#' #' @param power_five Logical, defaults to TRUE to include only punters from Power 5 teams +#' #' @return A tibble \code{punts} in a format usable for \code{puntr::calculate_all} +#' #' @examples +#' #' \dontrun{ +#' #' college_to_pro(punts) +#' #' } +#' #' @export +#' college_to_pro <- function(punts, power_five = TRUE) { +#' +#' punts <- punts %>% +#' #dplyr::rename(season = year) %>% +#' dplyr::mutate(GrossYards = play_text %>% +#' stringr::str_extract("punt for [:digit:]+") %>% +#' stringr::str_extract("[:digit:]+") %>% +#' as.numeric()) %>% +#' dplyr::filter(!is.na(GrossYards)) %>% +#' dplyr::mutate(return_yards = as.numeric(yds_punt_return), +#' return_yards = ifelse(is.na(return_yards),0,return_yards), +#' return_yards = ifelse(stringr::str_detect(play_text,"loss"),-1*return_yards,return_yards)) %>% +#' #dplyr::filter(!is.na(return_yards)) %>% +#' dplyr::mutate(punter_player_name = play_text %>% +#' stringr::str_extract(".+(?= punt for)")) %>% +#' dplyr::mutate(YardsFromOwnEndZone = as.integer(100 - yards_to_goal)) %>% +#' dplyr::filter(YardsFromOwnEndZone <= 70) %>% +#' dplyr::mutate(touchback = play_text %>% stringr::str_detect("ouchback")) %>% +#' dplyr::mutate(return_yards = dplyr::if_else(touchback, 0, return_yards)) %>% +#' dplyr::mutate(NetYards = GrossYards - return_yards) %>% +#' dplyr::mutate(GrossYards = dplyr::if_else(touchback, as.numeric(GrossYards-20), as.numeric(GrossYards))) %>% +#' dplyr::mutate(punt_out_of_bounds = play_text %>% stringr::str_detect("out.of.bounds")) %>% +#' dplyr::mutate(punt_fair_catch = play_text %>% stringr::str_detect("air catch")) %>% +#' dplyr::mutate(punt_downed = play_text %>% stringr::str_detect("downed")) %>% +#' dplyr::mutate(PD = dplyr::if_else(YardsFromOwnEndZone >=41, 1, 0)) %>% +#' #rename columns to avoid breaking calculate_all() +#' dplyr::rename(ep_before_cfb = ep_before, +#' ep_after_cfb = ep_after) +#' +#' +#' +#' # Pull from data-repo to avoid requiring cfbd API key +#' #team_info <- cfbfastR::cfbd_team_info() +#' team_info <- readRDS(url("https://github.com/saiemgilani/cfbfastR-data/blob/master/team_info/rds/cfb_team_info_2020.rds?raw=true")) +#' +#' if(power_five) { +#' team_info <- team_info %>% +#' dplyr::filter(conference %in% c("Pac-12","SEC","Big Ten","Big 12","ACC")) +#' } +#' +#' team_info <- team_info %>% +#' dplyr::mutate(logo = purrr::map(logos,magrittr::extract2,1), +#' logo = as.character(logo)) %>% +#' dplyr::select(school, logo, color, alt_color) %>% +#' dplyr::rename(team_abbr = school, +#' team_logo_espn = logo, +#' team_color = color, +#' team_color2 = alt_color) +#' +#' punts <- punts %>% dplyr::inner_join(team_info, by = c("pos_team" = "team_abbr")) +#' +#' return(punts) #' } -#' @export -college_to_pro <- function(punts, power_five = TRUE) { - - punts <- punts %>% - #dplyr::rename(season = year) %>% - dplyr::mutate(GrossYards = play_text %>% - stringr::str_extract("punt for [:digit:]+") %>% - stringr::str_extract("[:digit:]+") %>% - as.numeric()) %>% - dplyr::filter(!is.na(GrossYards)) %>% - dplyr::mutate(return_yards = as.numeric(yds_punt_return), - return_yards = ifelse(is.na(return_yards),0,return_yards), - return_yards = ifelse(stringr::str_detect(play_text,"loss"),-1*return_yards,return_yards)) %>% - #dplyr::filter(!is.na(return_yards)) %>% - dplyr::mutate(punter_player_name = play_text %>% - stringr::str_extract(".+(?= punt for)")) %>% - dplyr::mutate(YardsFromOwnEndZone = as.integer(100 - yards_to_goal)) %>% - dplyr::filter(YardsFromOwnEndZone <= 70) %>% - dplyr::mutate(touchback = play_text %>% stringr::str_detect("ouchback")) %>% - dplyr::mutate(return_yards = dplyr::if_else(touchback, 0, return_yards)) %>% - dplyr::mutate(NetYards = GrossYards - return_yards) %>% - dplyr::mutate(GrossYards = dplyr::if_else(touchback, as.numeric(GrossYards-20), as.numeric(GrossYards))) %>% - dplyr::mutate(punt_out_of_bounds = play_text %>% stringr::str_detect("out.of.bounds")) %>% - dplyr::mutate(punt_fair_catch = play_text %>% stringr::str_detect("air catch")) %>% - dplyr::mutate(punt_downed = play_text %>% stringr::str_detect("downed")) %>% - dplyr::mutate(PD = dplyr::if_else(YardsFromOwnEndZone >=41, 1, 0)) %>% - #rename columns to avoid breaking calculate_all() - dplyr::rename(ep_before_cfb = ep_before, - ep_after_cfb = ep_after) - - - - # Pull from data-repo to avoid requiring cfbd API key - #team_info <- cfbfastR::cfbd_team_info() - team_info <- readRDS(url("https://github.com/saiemgilani/cfbfastR-data/blob/master/team_info/rds/cfb_team_info_2020.rds?raw=true")) - - if(power_five) { - team_info <- team_info %>% - dplyr::filter(conference %in% c("Pac-12","SEC","Big Ten","Big 12","ACC")) - } - - team_info <- team_info %>% - dplyr::mutate(logo = purrr::map(logos,magrittr::extract2,1), - logo = as.character(logo)) %>% - dplyr::select(school, logo, color, alt_color) %>% - dplyr::rename(team_abbr = school, - team_logo_espn = logo, - team_color = color, - team_color2 = alt_color) - - punts <- punts %>% dplyr::inner_join(team_info, by = c("pos_team" = "team_abbr")) - - return(punts) -} diff --git a/R/import.R b/R/import.R index ae17b8f..85968a3 100644 --- a/R/import.R +++ b/R/import.R @@ -34,9 +34,9 @@ import_punts <- function(years, local = FALSE, path = NULL) { } #' Import play-by-play data -#' @description Grab all play-by-play data, not just punts. This function pulls data directly from the \code{nflfastR-data} repo, and -#' is purely a wrapper for ease of use. -#' @param years A year or range of years between 1999 and 2020, inclusive +#' @description DEPRECATED: Grab all play-by-play data, not just punts, from the \code{nflfastR-data} repo. This will be much slower than +#' using the \code{nflfastR::load_pbp()} or \code{nflfastR::update_pbp()} functions. +#' @param years A year or range of years between 1999 and 2021, inclusive #' @return A tibble \code{pbp} containing play-by-play data for the specified years #' @examples #' \dontrun{ diff --git a/R/mini2.R b/R/mini2.R index be38142..ea9dcb0 100644 --- a/R/mini2.R +++ b/R/mini2.R @@ -99,15 +99,24 @@ custom_summary <- function(data, ...) { SHARP_RERUN_OF = mean(SHARP_RERUN_OF, na.rm = TRUE), SHARP_RERUN_PD = mean(SHARP_RERUN_PD, na.rm = TRUE), ..., - team = getmode_local(posteam), - team_logo_espn = getmode_local(team_logo_espn), - team_color = getmode_local(team_color), - team_color2 = getmode_local(team_color2), + team = puntr::getmode(posteam), + team_logo_espn = puntr::getmode(team_logo_espn), + team_color = puntr::getmode(team_color), + team_color2 = puntr::getmode(team_color2), ) return(.summary) } - -getmode_local <- function(v) { - uniqv <- unique(v) - uniqv[which.max(tabulate(match(v, uniqv)))] +#' Find the mode of a column +#' @description Hilariously, R does not have a built-in method for finding the mode of a column. \code{puntr} has included an internal +#' helper function for this purpose for a while, and now it can be yours too! +#' @param column The dataframe column (or any other list) from which you would like to extract the most frequently occuring item +#' @return The mode of \code{column} +#' @examples +#' \dontrun{ +#' players_most_common_team <- getmode(punts_by_punter$posteam) +#' } +#' @export +getmode <- function(column) { + uniqv <- unique(column) + uniqv[which.max(tabulate(match(column, uniqv)))] } diff --git a/_pkgdown.yml b/_pkgdown.yml index 9d18c86..5eec0d1 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -6,3 +6,26 @@ navbar: data: text: Data from past seasons href: https://github.com/Puntalytics/puntr-data/tree/master/data +reference: +- title: Get data + desc: Import punting play-by-play data and calculate [Puntalytics metrics](https://puntalytics.github.io/metrics.html) +- contents: + - import_punts + - trust_the_process + - punt_trim + - calculate_all +- subtitle: College data +- contents: + - import_college_punts + - college_to_pro +- title: Summarize data + desc: Turn a play-by-play dataframe into a dataframe summarizing player stats +- contents: + - starts_with("by_punter") +- title: Miscellaneous + contents: + - getmode +- title: Deprecated +- contents: + - starts_with("create") + - import_seasons diff --git a/vignettes/puntr.Rmd b/vignettes/puntr.Rmd index 9a87a1f..20cd4d8 100644 --- a/vignettes/puntr.Rmd +++ b/vignettes/puntr.Rmd @@ -23,8 +23,9 @@ library(tidyverse) # always a good idea to do this too For speed, we've already scraped (using [`nflfastR`](https://www.nflfastr.com/)) and saved punting data for the 1999-2020 seasons. The easiest thing to do is download the `puntr-data` repo [here](https://github.com/Puntalytics/puntr-data/tree/master/data), and then point `puntr::import_punts()` to your local copy of the data. You can also download the data directly each time; this takes around 15 minutes. Import, clean, and calculate as follows: ```{r imports, message=FALSE} -#punts_raw <- import_punts(1999:2020, local=TRUE, path=your_local_path) # recommended -punts_raw <- import_punts(2018:2020) # This takes ~15 minutes +# punts_raw <- import_punts(1999:2020, local=TRUE, path=your_local_path) # recommended +# punts_raw <- import_punts(1999:2020) # This takes ~15 minutes +punts_raw <- import_punts(2018:2020) punts_cleaned <- trust_the_process(punts_raw) # clean punts <- calculate_all(punts_cleaned) # calculate custom Puntalytics metrics ``` @@ -83,6 +84,7 @@ Let's take a look at some of the columns in this data frame: punters %>% arrange(desc(pEPA)) %>% select(punter_player_name, Gross, Net, pEPA) %>% + mutate(across(where(is.numeric), round, 3)) %>% rmarkdown::paged_table() ``` @@ -95,6 +97,7 @@ which gives every unique punter season a row. punter_seasons %>% arrange(desc(pEPA)) %>% select(punter_player_name, season, Gross, Net, pEPA) %>% + mutate(across(where(is.numeric), round, 3)) %>% rmarkdown::paged_table() ``` And finally, to compare punter **games**, use @@ -109,7 +112,7 @@ These dataframes - `punts`, `punters`, `punter_seasons` and `punter_games` - sho ## Using `puntr` with college data -***NOTE: `puntr` was successfully migrated from `cfbscrapR` to `cfbfastR` in version 1.2.2*** +***NOTE: `puntr` was successfully migrated from `cfbscrapR` to `cfbfastR` in version 1.2.2*** ***NOTE: The `by_` family of summary functions have not yet been tested for `cfbfastR` data, but might work.*** `puntr` can also handle punting data for college football, piggybacking off of the scraping abilities of the [`cfbfastR`](https://saiemgilani.github.io/cfbfastR/) package. You need at least 3 seasons worth of data to run `calculate_all()`. Import and clean as follows: