ffscrapr icon indicating copy to clipboard operation
ffscrapr copied to clipboard

Add defense league points to ff_scoringhistory with new nflfastR defense player stats

Open christianlohr9 opened this issue 3 years ago • 8 comments

Yo Tan,

I just tried to do some fantasy WAR analysis in an IDP league and recognized that's not possible for a specific scoring, because ff_scoringhistory scrapes the calculate_player_stats data.

Maybe it'd be cool to add IDP scoring via the new function for defense stats.

Best regards, Christian

christianlohr9 avatar Dec 16 '22 08:12 christianlohr9

I found out that you have to adjust the function nflfastr_weekly and add a function .nflfastr_defense_long in script 1_import_nflfastr.R.

I added a stat_type "defense" which provides a df_weekly for defense stats. I'm a noob coder and the PR to nflfastR was already a pain in the ass for me, but you could do it like that for instance:

nflfastr_weekly <- function(seasons = TRUE,
                            type = c("offense", "kicking", "defense")) {
  
  type <- match.arg(type)
  
  if (type %in% c("offense", "kicking")) {
    df_weekly <- nflreadr::load_player_stats(seasons = seasons, stat_type = type)
  } else if (type %in% c("defense")) {
    df_weekly <- nflfastR::calculate_player_stats_def(nflfastR::load_pbp(seasons = seasons), weekly =TRUE)
  }
  
  return(df_weekly)
}

.nflfastr_defense_long <- function(season){
  ps <- nflfastr_weekly(seasons = season, type = "defense") %>%
    dplyr::select(dplyr::any_of(c(
      "season", "week","player_id",
      "def_tackles", "def_tackles_solo", "def_tackles_with_assist", "def_tackle_assist", "def_tackles_for_loss", "def_tackles_for_loss_yards",
      "def_fumbles_forced", "def_fumbles", "def_fumble_recovery_own", "def_fumble_recovery_yards_own", "def_fumble_recovery_opp", "def_fumble_recovery_yards_opp",
      "def_sacks", "def_sack_yards", "def_qb_hit",
      "def_interceptions", "def_interception_yards", "def_pass_defended",
      "def_tds", "def_safety", "def_penalty", "def_penalty_yards"
      )
    )) %>%
    tidyr::pivot_longer(
      names_to = "metric",
      cols = -c("season","week","player_id")
    )
  
  return(ps)
}

christianlohr9 avatar Dec 16 '22 08:12 christianlohr9

If I'm correct the only thing in addition would be to add this to ff_scoringhistory.sleeper_conn like this:

ff_scoringhistory.sleeper_conn <- function(conn, season = 1999:nflreadr::most_recent_season(), ...) {
  checkmate::assert_numeric(season, lower = 1999, upper = as.integer(format(Sys.Date(), "%Y")))
  
  # Pull in scoring rules for that league
  league_rules <-
    ff_scoring(conn) %>%
    dplyr::left_join(
      ffscrapr::nflfastr_stat_mapping %>% dplyr::filter(.data$platform == "sleeper"),
      by = c("event" = "ff_event")
    )
  
  ros <- .nflfastr_roster(season)
  
  ps <- bind_rows(
    .nflfastr_offense_long(season), 
    .nflfastr_defense_long(season)
    )
  
  if("K" %in% league_rules$pos){
    ps <- dplyr::bind_rows(
      ps,
      .nflfastr_kicking_long(season))
  }
  
  ros %>%
    dplyr::inner_join(ps, by = c("gsis_id"="player_id","season")) %>%
    dplyr::inner_join(league_rules, by = c("metric"="nflfastr_event","pos")) %>%
    dplyr::mutate(points = .data$value * .data$points) %>%
    dplyr::group_by(.data$season, .data$week, .data$gsis_id, .data$sportradar_id) %>%
    dplyr::mutate(points = round(sum(.data$points, na.rm = TRUE), 2)) %>%
    dplyr::ungroup() %>%
    tidyr::pivot_wider(
      id_cols = c("season", "week", "gsis_id", "sportradar_id", "sleeper_id", "player_name", "pos", "team", "points"),
      names_from = "metric",
      values_from = "value",
      values_fill = 0,
      values_fn = max
    )
}

christianlohr9 avatar Dec 16 '22 09:12 christianlohr9

And the last (and probably most annoying thing) is to update the usedata_statmapping.R which is not available for me^^

christianlohr9 avatar Dec 16 '22 09:12 christianlohr9

Hey Christian, thanks for the interest - I don't have bandwidth or plans to tackle this in the immediate future but perhaps can review it as an offseason thing.

The code above looks fine! I do not want to introduce a dependency on nflfastR in this package, so it would require the infrastructure legwork of getting this all sorted within the nflreadr structure in order to be able to do something like this.

tanho63 avatar Dec 16 '22 14:12 tanho63

Hey @tanho63 - any chance you've got some cycles to check this out? Or @christianlohr9 maybe if @tanho63 can help direct us on the infra work we can tag team it?

pmadison avatar Apr 29 '23 19:04 pmadison

Hiya! So I'd judge the steps required to implement as follows:

  • [x] fix this bug in the nflfastR defense summarize function: nflverse/nflfastR#410 - probably by adding/binding the defense data to an empty dataframe so that it always has the columns in question
  • [x] add a script to nflverse-pbp to build the data and upload to nflverse-data repo, something like https://github.com/nflverse/nflverse-pbp/blob/master/R/update_player_stats.R
  • [x] update nflreadr::load_player_stats() to read the new uploaded data https://github.com/nflverse/nflreadr/blob/main/R/load_player_stats.R
  • [ ] then extend ffscrapr::ff_scoringhistory() to work with the defensive scoring, similar to what Christian posted above.

I haven't started on these yet, so happy to take some PRs in roughly this order!

tanho63 avatar Apr 30 '23 20:04 tanho63

Hey @tanho63, now that nflreadr::load_player_stats() has a defense option, would it be easy-ish to extend ffscrapr::ff_scoringhistory() to work with these?

TheMathNinja avatar Aug 28 '23 17:08 TheMathNinja

Not something that I have bandwidth for in the near future, but would welcome a PR if someone wanted to tackle it

tanho63 avatar Aug 28 '23 17:08 tanho63